全屏弹窗软键盘错位需三方面协同:容器用flex+100dvh撑高、定位动态切换absolute/fixed、平台信号适配env()与框架配置。

全屏弹窗在软键盘弹起时错位,本质是它被当作普通页面内容参与了视口重排——而系统对弹窗的定位锚点(visual viewport)在键盘介入后失效。直接靠 position: fixed 或单纯监听 resize 都不可靠,需从容器约束、定位策略和平台信号三方面协同处理。
用 flex 布局替代 fixed 定位
把弹窗根容器设为 display: flex; flex-direction: column; min-height: 100dvh,再让弹窗内容区 flex: 1,底部操作区加 flex-shrink: 0。这样弹窗高度由父容器撑开,不依赖视口锚定,键盘弹出时只压缩可用空间,不会导致弹窗“上浮”或“卡半空”。注意必须用 100dvh(非 100vh),并确保 <meta name="viewport"> 含 viewport-fit=cover,否则单位无效。
监听 focusin/blur 动态控制弹窗状态
不能依赖 resize,iOS Safari 的触发率极低,安卓也不稳定。应监听所有可聚焦元素的 focusin(不是 focus)事件:
- 当
document.activeElement是弹窗内的input或textarea时,给弹窗加keyboard-active类,临时移除fixed,改用position: absolute+top: 0+width: 100%,并设height: 100% - 用
setTimeout(() => {}, 0)延迟读取getBoundingClientRect(),避免取到键盘未展开前的旧尺寸 -
blur触发后立即清除内联样式,恢复原始定位
兜底:用 env(keyboard-inset-bottom) 和安全区适配
iOS Safari 16.4+ 支持 bottom: env(keyboard-inset-bottom, 0px),但安卓不识别,所以必须降级:
- 写法必须是
bottom: env(keyboard-inset-bottom, 0px),不能省略 fallback - 外层用
@supports (bottom: env(keyboard-inset-bottom))包裹,确保老浏览器忽略该规则而不报错 - 同时设置
padding-bottom: env(safe-area-inset-bottom)防止弹窗底部被刘海或 Home Indicator 遮挡
uni-app 等跨端框架要额外配置
H5 环境下,uni.onKeyboardHeightChange 是唯一稳定信号源;App 端则必须在 pages.json 中为对应页面开启:
-
"softinputMode": "adjustResize"(而非默认的adjustPan),强制 WebView 压缩高度触发重排 - 配合
"softinputNavBar": "none"隐藏 iOS 键盘顶部横条,减少干扰 - 若弹窗含输入框,建议将提交按钮与输入框放在同一
position: relative容器内,用flex对齐,彻底脱离全局定位依赖


















