100vh在键盘弹出后失效,因其按初始视口高度静态计算,ios safari等不更新vh值也不触发resize,导致布局错乱;应改用100dvh+降级,并结合focusin/blur和env(safe-area-inset-bottom)协同处理。

为什么100vh在键盘弹出后失效
因为 100vh 是按页面加载时的视口高度计算的静态值,iOS Safari 和部分安卓 WebView 在软键盘弹出时**不更新 vh 单位**,也不保证触发 resize 事件。结果就是:容器仍按 667px 高渲染,但实际可视区域只剩 400px,底部内容被遮、列表截断、flex: 1 区域塌陷。
常见错误现象:
• 页面“被顶起”,输入框卡在键盘边缘
• position: fixed 底部按钮突然上浮甚至消失
• Flex 布局子项高度错乱,滚动位置跳变
- 别用
height: 100vh做全屏容器高度 —— 它在键盘弹出后就不可信 - iOS 下
window.innerHeight变化延迟严重,不能直接赋值给style.height - Android Chrome 相对稳定,但微信内置浏览器等 WebView 仍可能漏掉 resize
用 100dvh + 兼容降级替代 100vh
100dvh(dynamic viewport height)是当前最干净的 CSS 方案,它始终代表「无键盘时的完整视口高度」,Safari 16.4+、Chrome 105+ 已原生支持。
实操写法:
body, .container {
min-height: 100dvh;
max-height: 100dvh;
overflow-y: auto;
}
兼容老版本必须加降级:
- 写成
min-height: 100vh; min-height: 100dvh;—— 后者覆盖前者,不支持 dvh 的浏览器回退到 vh - 避免只写
height: 100dvh,否则内容超长时无法滚动 - Flex 子项要设
flex-shrink: 0或flex: 0 0 auto,防止被压缩变形
监听 focusin/blur 比 resize 更可靠
resize 在 iOS 键盘收起时不触发或触发过早,focusin 才是键盘弹出最确定的信号。JS 配合 class 切换是最可控的兜底方式。
关键点:
- 对所有
<input>、<textarea></textarea>绑定focusin,记录当前window.innerHeight作为“基准高” - 在
focusin回调里加setTimeout(() => { ... }, 0),确保读到键盘弹出后的最新坐标 -
blur后延时 300ms 再恢复布局,避开 iOS 键盘收起过程中的 height 抖动 - 不要只依赖一个事件 ——
focusin+blur+visualViewport.resize三者配合才覆盖全机型
env(safe-area-inset-bottom) 不是键盘高度,但能感知键盘
env(safe-area-inset-bottom) 在键盘弹出时会动态增大(iOS 16+、Chrome 106+ 支持),但它表示的是「系统 UI 侵入视口的最小距离」,不是键盘真实像素高度。
适合场景:
- 底部操作栏、输入框上方的滚动列表 —— 用
padding-bottom: env(safe-area-inset-bottom)预留空间 - 必须加降级:
padding-bottom: max(env(safe-area-inset-bottom), 20px),否则老 Safari 会忽略整条规则 - 别用它算
top或bottom值 —— 它在无键盘时为 0,不适合做绝对定位偏移
真正难处理的,是那些既要用 fixed 又得贴底、还跨多个 WebView 版本的输入框;这时候 visualViewport.height 的差值计算 + transform: translateY() 往往比改 bottom 更稳。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











