100vh在iOS Safari和微信WebView中不准确,因其按物理屏幕高度计算而非可视区域,导致内容截断或留白;应优先使用100dvh并用@supports正确包裹,iOS 15及微信需JS动态设置--vh兜底。

100vh 在手机浏览器里不准确,不是你写错了,是它压根没按“你能看见的区域”算高度——iOS Safari 和微信 WebView 把地址栏、底部工具栏的高度全塞进初始视口里,锁死计算值,后续滚动、键盘弹出都不更新。
为什么 100vh 在 iOS Safari 里比屏幕还高
刚加载时,Safari 拿设备物理屏高(比如 iPhone 14 是 844px)算 1vh = 8.44px,于是 100vh = 844px;但此时地址栏和底部工具栏占了约 140px,真实可视高度只有 ~700px。结果就是内容被截、底部留白、overflow: hidden 失效。
- 滚动后地址栏收起,可视区域变大,但
100vh值不会重算 - Android Chrome 同样存在,尤其在 PWA 或全屏模式下更明显
-
height: 100vh比min-height: 100vh更危险:前者硬截断,用户滑不到底部
@supports(min-height: 100dvh) 必须包整条规则
只写 .page { min-height: 100vh; min-height: 100dvh; } 是错的——老 Safari(iOS 15.x 及更早)不认识 100dvh,直接忽略第二行,仍走 100vh;新 Safari 也可能因解析顺序或构建工具干扰,继续用旧值。
- 正确写法:
.page { min-height: 100vh; }+@supports (min-height: 100dvh) { .page { min-height: 100dvh; } } - 兜底声明必须在外层,确保老环境至少有基础高度
- 别用 PostCSS 插件自动转换单位,它们常把
100dvh错误降级成100vh
position: absolute 元素设了 100dvh 为啥没反应
绝对定位元素的百分比高度(包括 100dvh)依赖包含块(containing block)高度,不是“强制占满屏幕”。常见于弹窗、遮罩层、底部按钮等场景。
立即学习“前端免费学习笔记(深入)”;
- 父容器(如
body或直接祖先)没设min-height: 100dvh或显式高度 → 子元素100dvh实际计算为 0 - 别对绝对定位元素用
height: 100dvh,改用top: 0; bottom: 0;,更稳定 - 若父容器本身靠
--vh驱动,JS 初始化必须先于子元素渲染,否则读到的是初始 1px
兼容 iOS 15 及微信 WebView 必须用 JS 动态设 --vh
iOS 15.x 和微信内置 WebView 完全不支持 dvh,纯 CSS 方案在此类环境会塌成一条线。唯一可靠路径是监听 resize 动态设置 CSS 变量。
- 必须在
DOMContentLoaded后立即执行一次,否则首屏渲染用的是地址栏未收起时的错误初始值 - 监听
resize就够了(地址栏收放、键盘弹起都会触发),不用监听scroll(高频且无必要) - CSS 中写成:
height: calc(var(--vh, 1vh) * 100);,其中1vh是兜底值 - 安卓部分定制系统(如华为 EMUI)
visualViewport.height返回异常,需加校验:if (v.height > 0 && v.height
真正容易被忽略的点:所有依赖视口高度的逻辑(比如 env(safe-area-inset-bottom)、JS 读取 window.innerHeight、100dvh 的生效前提)都要求 html 和 body 没有 transform、filter、opacity 等会创建新层叠上下文的属性——一旦有,包含块就断了。


















