100vh失效是因为浏览器将它锚定在物理屏幕高度而非可视区域,软键盘弹出后visualviewport.height变小但100vh不更新;应改用min-h-[100dvh]配合flex布局、focusin/blur监听及absolute定位模拟fixed等综合方案。

为什么100vh在软键盘弹出时会失效
不是你写错了,是浏览器把100vh锚定在物理屏幕高度上,而软键盘弹出后实际可视区域(visualViewport.height)变小了,但100vh值根本不更新。iOS Safari 尤其明显:键盘弹起后100vh还是初始值,导致容器撑高、内容被截、底部按钮悬空或消失。
用min-h-[100dvh]替代min-h-screen
100dvh(dynamic viewport height)才是随键盘伸缩的真实单位,iOS 16.4+ 和 Android Chrome 109+ 已稳定支持。但直接替换不够,必须配对使用:
-
min-h-[100dvh]要设在根容器(如#app),不能只套在某个子块里 - 必须配合
flex flex-col布局,否则min-h-[100dvh]只是最小高度,不触发弹性分配 - 主内容区加
flex-1或flex-grow,底部栏加flex-shrink-0,否则键盘一弹它就被压缩变形 - 删掉
<meta>里height=device-height这类干扰项,它会让所有vh类单位失效
focusin/blur动态切class比resize更可靠
window.addEventListener('resize')在 iOS 微信 WebView 中触发率低于 20%,且常只在键盘收起时才触发一次。真正该监听的是focusin和blur:
- 监听所有
input、textarea、[contenteditable],别漏掉textarea -
focusin回调里必须加setTimeout(() => { ... }, 0),否则getBoundingClientRect()读到的是键盘弹出前的旧坐标 - 用
visualViewport?.height计算当前真实可视高度,别用window.innerHeight——iOS 键盘收起后它可能延迟几百毫秒才恢复 -
blur后立刻恢复body.style.height = 'auto',否则滚动时定位异常
绝对定位模拟fixed必须满足三个硬条件
只把position: fixed改成position: absolute会穿帮。真正起效需同时满足:
- 根容器设
position: relative且min-height: 100vh(不是height: 100%,否则键盘弹出时塌陷) - 底部元素加
width: 100%,否则横屏或缩放后偏移 -
focusin后切换为position: absolute,并用window.innerHeight - inputRect.bottom算bottom值,不是写死0 -
blur后必须调el.style.removeProperty('top')和el.style.removeProperty('bottom'),再恢复position: fixed
最易被忽略的点:多个输入框共用同一底部栏时,document.activeElement可能异步变化,必须在focusin回调开头立刻做=== inputEl校验,否则状态错位;env(keyboard-inset-bottom)虽好,但安卓完全不支持,且必须写bottom: env(keyboard-inset-bottom, 0px) fallback,否则键盘收起后按钮悬空。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











