ios 17 safari仍用布局视口计算100vh,不响应地址栏收起或键盘弹出;应使用@supports(min-height:100dvh)包裹整块规则并兜底min-height:100vh,配合top/bottom替代height及js动态--vh防抖更新。

因为 iOS 17 的 Safari 仍沿用「布局视口」(layout viewport)计算 100vh,不响应地址栏收起、软键盘弹出等动态变化——100vh 在页面加载那一刻就锁死了,后续任何可视区域变化都不会触发重算。
为什么 iOS 17 还不支持 100vh 动态更新
iOS 17 的 Safari(包括 17.0–17.6)依然基于 WebKit 的旧视口模型:它把地址栏、底部工具栏的高度计入初始视口高度,哪怕用户滚动后它们已隐藏,100vh 的基准值(如 iPhone 15 Pro 的 852px)也完全不变。这不是 bug,是规范实现——vh 单位本就绑定 layout viewport,而非用户当前看到的 visual viewport。
典型表现包括:
-
height: 100vh的登录页,键盘一弹,提交按钮直接被遮住 - 固定在底部的
position: fixed按钮,在地址栏收起后上浮约 80px - 轮播图容器顶部被裁切,滚动后才“突然”显示完整内容
@supports (min-height: 100dvh) 必须整块包裹
iOS 17.4+ 才开始稳定支持 100dvh,但 iOS 17.0–17.3 仍会忽略该单位。如果只写:min-height: 100vh; min-height: 100dvh;,老版本 Safari 会跳过第二行,继续走 100vh 分支,问题照旧;新版本也可能因解析顺序或构建工具干扰,未生效。
必须这样写:
.page { min-height: 100vh; }
@supports (min-height: 100dvh) {
.page { min-height: 100dvh; }
}
关键点:
-
@supports必须包裹整个选择器块,不能只包声明值 - 兜底的
min-height: 100vh一定要写在外面,确保 iOS 17.0 也能渲染出基础高度 - 别用
height,坚持用min-height——避免内容超长时被截断,用户滑不到底部
绝对定位元素设 100dvh 为啥还是没效果
给 position: absolute 元素写 min-height: 100dvh,但父容器(比如 body 或某个 div.wrapper)没设高度上下文,它的 100dvh 实际会算成 0——因为百分比高度依赖包含块(containing block)的明确高度。
常见于弹窗、遮罩层、底部固定按钮。解决方案更直接:
- 确保父容器有
min-height: 100dvh或显式height - 改用
top: 0; bottom: 0;替代height: 100dvh,只要父容器是position: relative或fixed就稳 - 若父容器本身靠 JS 设置的
--vh变量驱动,初始化必须早于子元素渲染,否则读到的是初始1px
JS fallback 仍不可少,尤其要防 resize 连续触发
即使目标是 iOS 17,仍有大量用户停留在 17.0–17.3,或使用微信 WebView(基于旧版 WKWebView),100dvh 完全不识别,页面会塌成一条线。
JS 初始化要点:
- 必须在
DOMContentLoaded后立即执行一次,否则首屏用的是地址栏未收起时的错误高度 - 监听
resize(不是scroll),iOS 地址栏收放、键盘弹起/收起都会触发它 - 加
requestAnimationFrame防抖,Safari 的resize在键盘收起时可能 1 秒内连发 4–5 次 - 校验
visualViewport?.height值是否合理,华为 EMUI 等定制系统可能返回异常值
最易被忽略的是:CSS 中要用 calc(100 * var(--vh)),不是 100vh;且 html 和 body 默认 margin 会影响 window.innerHeight,需提前重置。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











