100vh在移动端fixed元素上塌陷是因为浏览器将视口高度锁定为地址栏未收起时的值,iOS Safari 16.4+等支持100dvh但需@supports包裹,老环境须JS注入--vh变量并防抖监听resize。

为什么100vh在移动端fixed元素上会塌陷
不是代码写错了,是浏览器把“视口高度”算成了地址栏还挂着时的值。iOS Safari加载时就锁死1vh = 设备物理屏高 ÷ 100,哪怕用户一滚动地址栏收起、可视区域变大,100vh也不更新。结果就是height: 100vh的fixed遮罩比你能看见的区域还高,内容被截、底部留白、甚至触发意外滚动条。Android Chrome和部分WebView也有类似表现,偏差常达80–120px。
用100dvh替代100vh但必须加@supports包裹
100dvh才是真·当前可视区域高度,iOS Safari 16.4+、Chrome 109+、Firefox 117+ 已稳定支持。但它不能裸用,否则老环境直接忽略整条规则导致高度塌陷。
- 错误写法:
.modal { height: 100vh; height: 100dvh; }—— 老Safari跳过第二行,仍用100vh - 正确写法:
.modal { height: 100vh; }+@supports (height: 100dvh) { .modal { height: 100dvh; } } - 坚持用
min-height而非height:避免内容超长时被强制裁剪
fixed元素套dvh单位没反应?检查包含块和定位方式
绝对/固定定位元素的100dvh依赖包含块(containing block)的高度。如果父容器没设高度,100dvh实际计算为0。
- 别对fixed元素直接写
height: 100dvh,改用top: 0; bottom: 0;更可靠 - 若父容器本身也靠
--vh驱动,确保它先完成初始化,否则子元素读到的是初始1px - 给
html和body设min-height: 100dvh(同样需@supports包裹),避免后代fixed元素因继承链断裂而失效
兜底方案:JS动态注入--vh变量(iOS 15及更早必须)
iOS 15、微信X5内核等环境完全不识别dvh,CSS方案彻底失效,只能JS回退。
立即学习“前端免费学习笔记(深入)”;
- 在
DOMContentLoaded后立即执行一次:document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`) - 监听
resize事件(iOS地址栏显隐会触发),用requestAnimationFrame防抖,不用setTimeout - CSS中统一写:
height: calc(var(--vh, 1vh) * 100),其中1vh是兜底比例参考 - 注意SSR首屏可能闪动,hydration阶段需重设
真正容易被忽略的是:即使用了100dvh,也要检查父容器是否参与高度继承链;而一旦引入JS方案,requestAnimationFrame防抖和首次同步执行这两个点,漏掉任何一个都会导致首屏白屏或滚动跳变。


















