100vh在ios safari中“撑不满”屏幕是因为浏览器将地址栏高度计入视口,滚动后地址栏收起但100vh值不变,导致底部留白;应改用实时响应的100dvh,并配合@supports降级和js动态设置--vh变量。

为什么100vh在iOS Safari里“撑不满”屏幕
不是你CSS写错了,是浏览器把地址栏高度算进视口了。页面加载时100vh按初始视口(含地址栏)计算,滚动后地址栏收起,实际可视区域变高,但100vh值冻结不动,导致底部留白或内容被截断。微信、QQ内置浏览器同理,Android Chrome基本不受影响。
直接用100dvh替代100vh的实操要点
100dvh代表“当前可见视口高度”,能实时响应地址栏显隐和键盘弹出,是目前最简方案:
-
min-height: 100dvh比height: 100dvh更安全——避免父容器高度不足时强制截断 - 别给
或设height: 100dvh,容易触发iOS滚动异常;改用<div class="wrapper">包裹内容并设<code>min-height: 100dvh - iOS Safari 16.4+、Chrome 109+已稳定支持,但仍有约12%活跃设备运行iOS 15.x,必须降级
- CSS中必须用
@supports not (min-height: 100dvh)包裹降级规则,否则老版本会忽略整个块 - JS监听
resize和orientationchange事件更新--vh变量,scroll监听无效且开销大 - 首次渲染前就要执行
setVh(),否则首屏可能闪动或留白;建议放在内联脚本中 - 防抖必须用
requestAnimationFrame,iOS resize连续触发,节流不当会导致高度滞后
@supports降级到100vh+JS微调的必要组合
仅靠@supports (min-height: 100dvh)不够,fallback必须真正生效:
对应关键代码:
function setVh() {<br> document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);<br>}<br>setVh();<br>window.addEventListener('resize', () => requestAnimationFrame(setVh));配合CSS:min-height: calc(var(--vh, 1px) * 100)横屏切换时vw/vh反向计算的修复逻辑
横屏瞬间100vw变成物理高度、100vh缩成物理宽度,不是你单位用错,是浏览器在orientationchange动画帧中重绘CSS,但document.documentElement.clientHeight还没更新:
- 动态视口单位
dvh/dvw天然规避此问题,它们基于visual viewport而非layout viewport - 若必须用
vh,需在orientationchange后加setTimeout(() => setVh(), 100)强制延迟更新,但不如直接切dvh可靠 - 别依赖
window.innerWidth/window.innerHeight做横竖屏判断——它们返回的是layout viewport尺寸,与CSS计算不一致
真实场景里,100dvh不是万能解药,它只解决“高度随浏览器UI动态变化”这一个问题。如果你还要处理刘海/小黑条,env(safe-area-inset-bottom)必须和--vh变量叠加计算,否则底部按钮会被遮住。











