100vh在ios safari中计算不准是因为它基于布局视口而非可视区域,且不响应地址栏/键盘变化;应使用@supports包裹整条100dvh规则并配合js动态设置--vh变量兜底。

为什么100vh在iOS Safari里算不准
因为100vh拿的是布局视口(Layout Viewport)高度,不是用户当前看到的区域。iPhone Safari加载时地址栏和底部工具栏都占空间,但100vh却按“全屏物理高度”计算——比如 iPhone 14 的 844px,而真实可视高度只有约 700px。结果就是内容被工具栏遮住,或者留白。更麻烦的是:这个值在页面加载那一刻就锁死了,后续地址栏收起/弹出、键盘唤起,100vh完全不响应变化。
@supports必须包裹整条规则,不能只包值
直接写 min-height: 100vh; min-height: 100dvh; 是错的。老 Safari(iOS 15.x 及更早)不认识 100dvh,会忽略整行,但保留上一行 100vh,问题照旧;新 Safari(iOS 16.4+)可能因解析顺序或构建工具干扰,依然走 100vh 分支。
-
@supports必须包裹整个 CSS 规则块,不能只包值 - 兜底声明(
min-height: 100vh)必须写在@supports外面,确保老环境至少有基础高度 - 优先用
min-height而非height,避免内容超长时被截断
正确写法:
.page { min-height: 100vh; }
@supports (min-height: 100dvh) {
.page { min-height: 100dvh; }
}
position: absolute 元素设了 100dvh 还是没撑开
绝对定位元素的百分比高度(包括 100dvh)依赖包含块高度。如果父容器没设高度,100dvh 实际计算为 0。常见于弹窗、遮罩层、底部固定按钮——它们的父容器(比如 body 或某个 wrapper)没设 min-height。
- 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;,更稳定 - 若父容器本身靠
--vh驱动,JS 初始化必须先于子元素渲染,否则读到的是初始 1px - 确保父容器(如
html或body)有明确高度,否则env(safe-area-inset-bottom)计算无意义
iOS 15 及更早必须用 JS 动态设 --vh
此时 100dvh 完全不识别,页面会塌成一条线。必须用 JS 动态读取 window.innerHeight 并写入 CSS 变量:--vh,且要防抖更新。
- 必须在
DOMContentLoaded后立即执行一次,否则首屏渲染用的是地址栏未收起时的错误初始值 - 监听
resize(横竖屏)、scroll(iOS 地址栏收起时仅触发scroll)、以及微信特有的weixinjsbridgeReady - 防抖必须用
requestAnimationFrame,而非setTimeout—— 微信安卓下resize触发极快且无规律,节流不当会导致高度卡死 - CSS 中统一用
min-height: calc(var(--vh, 1px) * 100),不依赖父容器是否设了height: 100%
关键点在于:100dvh 不是银弹,它只在 iOS 16.4+、Chrome 109+、Firefox 117+ 稳定支持;而仍有大量用户停留在 iOS 15.x 或旧版 WebView,这部分必须靠 JS 回退,且初始化时机和事件监听点一个都不能漏。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











