最简方案是使用 height: 100dvh,但 ios 15.4 以下及部分 webview 需降级为 js 动态计算 visualviewport.height 或 window.innerheight;100% 和 100vh 失效因锚定 layout viewport 而非动态 visual viewport。

直接用 height: 100dvh 是最简方案,但 iOS 15.4 以下和部分 WebView 必须降级到 JS 动态计算;别再硬套 height: 100% 或 min-height: 100vh,它们在地址栏收起/键盘弹出时会失准。
为什么 height: 100% 和 100vh 在移动端菜单里常失效
根本原因不是写法错,而是浏览器把 vh 锚定在 layout viewport(初始视口),而非用户实际看到的 visual viewport。iOS Safari 尤其明显:页面滚动、地址栏隐藏、软键盘弹出时,100vh 值不变,但可视高度已变——菜单可能被截掉顶部,或底部留大片空白。
-
height: 100%失效是因为父容器(比如body)没显式设高,链路一断就归零 -
min-height: 100vh看似安全,但内容少时撑满,内容多时又不管用,且同样不响应动态视口变化 - 微信内置浏览器、QQ 浏览器等 WebView 对
vh的实现更混乱,有些甚至把状态栏高度也算进去了
用 100dvh 替代 100vh 的前提和限制
100dvh 是唯一原生支持“随地址栏显隐自动更新”的 CSS 单位,但它不是万能开关——得看运行环境是否真支持。
- iOS Safari 从 16.4 开始支持,旧版本(如 iOS 15.x)完全忽略
100dvh,直接当无效值丢弃 - Chrome Android 100+ 支持,但 Cordova / Capacitor 等封装的 WebView 可能滞后,需实测
- 必须配合
@supports (height: 100dvh)做条件覆盖,否则不支持时菜单会塌成一行 - 示例写法:
@supports (height: 100dvh) { .menu { height: 100dvh; } } @supports not (height: 100dvh) { .menu { height: 100vh; /* fallback,仅作兜底,非最优 */ } }
旧版 iOS 必须用 JS 回退获取真实可视高度
对 iOS 15.4 及更早版本,visualViewport.height 是唯一可靠来源——它返回的是当前用户真正能看到的像素高度,不受地址栏遮挡影响。
- 监听
resize、scroll、orientationchange三个事件,但要用requestAnimationFrame节流,避免频繁重绘 - 把结果写入 CSS 自定义属性,比如
--vh: ${height}px,菜单样式直接用height: calc(var(--vh) * 1px) - 注意:Safari 15.4 之前不支持
visualViewport,得 fallback 到window.innerHeight,但它在键盘弹出时也不准,需额外监听focusin/focusout - 关键代码片段:
function updateVH() { const vh = window.visualViewport?.height || window.innerHeight; document.documentElement.style.setProperty('--vh', `${vh}px`); } window.addEventListener('resize', () => requestAnimationFrame(updateVH)); window.addEventListener('scroll', () => requestAnimationFrame(updateVH));
固定底部菜单要特别防地址栏遮挡
如果菜单是 position: fixed; bottom: 0,光靠高度适配还不够——iOS 地址栏收起时会把整个视口往上推,导致菜单被顶出屏幕底部。
- 必须加
padding-bottom: env(safe-area-inset-bottom)到 body,同时菜单自身也要用env(safe-area-inset-bottom)调整 bottom 值 - 不要只依赖
env():iOS 14.5 以下不支持,得用constant(safe-area-inset-bottom)兼容 - 若菜单含输入框,键盘弹出时 fixed 定位会失效(尤其安卓),建议 focus 时临时切为
position: absolute,并手动计算 bottom 值 - 测试重点机型:iPhone X/XR/12(刘海屏)、iPhone SE(无刘海)、iPad mini(多任务侧滑场景)
真正麻烦的不是写几行 CSS,而是不同 iOS 版本、不同 WebView 实现、不同键盘唤起时机,会让同一个 100vh 表现出三种结果。别指望一个值打遍天下,得按支持度分层处理——dvh 优先,visualViewport 次之,innerHeight + 事件兜底最后。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











