因为height: 100vh被浏览器按“初始视口”锁定计算,iOS Safari等在软键盘弹出时不更新window.innerHeight、不触发resize,导致容器仍按全屏高渲染而实际可视区被压缩;应改用min-height/max-height: 100dvh + overflow-y: auto,并对关键子项设flex-shrink: 0。

为什么 height: 100vh 在键盘弹出时会错位
不是 Flex 写错了,是 height: 100vh 被浏览器按“初始视口”锁定计算。iOS Safari 和部分安卓 WebView 在软键盘弹出时,window.innerHeight 不更新、resize 事件不触发,但可视区域(visualViewport.height)实际被压缩——结果就是容器还撑着全屏高,内容却被截断或底部输入框直接消失。
用 100dvh 替代 100vh 是最简解法
100dvh(dynamic viewport height)代表“无键盘状态下的完整视口高度”,它不受键盘弹出影响,且能随安全区域(如 iPhone 底部手势条)自动调整。Chrome 105+、Safari 16.4+ 已原生支持。
- 把所有
height: 100vh换成min-height: 100dvh; max-height: 100dvh; overflow-y: auto - 关键子项(如底部输入框容器)加
flex-shrink: 0,防止被 flex 压缩 - 若需兼容老版本(如 Safari focusin/
blur动态切换 class,并层叠min-height: 100vh; height: 100dvh
viewport meta 标签必须写对,否则 dvh 也救不了
如果 <meta name="viewport"> 缺失或写错,100dvh 会退化为不可靠行为。真机上常见现象:页面整体缩小、右侧滑出空白、document.documentElement.clientWidth 远小于屏幕宽度。
- 必须写在
最前面:<meta name="viewport" content="width=device-width, initial-scale=1.0"> - 禁用
maximum-scale=1或固定width=375—— 它们会强制缩放,干扰视口单位解析 - 全面屏设备需配合
padding-bottom: env(safe-area-inset-bottom),否则底部按钮仍会被手势条遮挡
visualViewport API 是唯一可靠的高度监听方式
window.resize 在 iOS 键盘收起时不触发,document.documentElement.clientHeight 更新延迟数百毫秒。只有 visualViewport 能实时拿到用户真正看到的高度。
- 初始化时存基准值:
const baseHeight = visualViewport.height - 监听变化:
visualViewport.addEventListener('resize', () => { const kbHeight = baseHeight - visualViewport.height; }) - 用
kbHeight动态调整按钮位置(例如transform: translateY(${kbHeight}px)),比改bottom更少重排 - Android 微信内置浏览器不支持该 API,必须降级到
focusin/blur+setTimeout防抖读取getBoundingClientRect()
真实错位往往卡在三个细节:focusin 触发太早,读不到新坐标;Safari 对 window.scrollTo() 有节流;父容器用了 transform 或 overflow: hidden,导致 getBoundingClientRect() 返回异常值。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











