<p>100vh在移动端“失效”是因为键盘弹出或地址栏收起时,vh仍按初始视口计算,导致遮挡或留白;需用--vh变量动态绑定visualViewport.height并监听resize事件,配合CSS回退策略(100dvh → calc(100 * var(--vh)) → 100vh)。</p>

为什么100vh在移动端会“失效”
因为键盘弹出、地址栏收起时,vh单位仍按原始视口高度计算,导致元素被遮挡或留白。比如一个min-height: 100vh的登录页,在iOS Safari输入框聚焦后,页面实际可视高度变小,但100vh没变,结果底部按钮被键盘盖住。
用--vh变量动态绑定visualViewport高度
核心思路是用JS读取window.visualViewport.height(当前可用视口高度),再写入CSS变量--vh,让CSS能响应式使用这个真实值。
- 必须在
visualViewport存在时才启用(Chrome 61+/Safari 15.4+支持) - 监听
visualviewport.resize事件,而非window.resize——后者不触发键盘弹起变化 - 初始值要兜底:
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`),避免JS未执行时样式断裂
示例代码:
if (window.visualViewport) {
const setVh = () => {
document.documentElement.style.setProperty('--vh', `${window.visualViewport.height * 0.01}px`);
};
setVh();
window.visualViewport.addEventListener('resize', setVh);
}
CSS中直接使用:height: calc(100 * var(--vh));(注意不是100vh)
立即学习“前端免费学习笔记(深入)”;
--vh和dvh该选哪个
dvh是原生单位,语义清晰,但兼容性差(Firefox至今不支持,Android WebView支持不稳定)。--vh变量方案手动可控,且能 fallback 到vh:
- 写法上必须双层回退:
height: 100dvh; height: calc(100 * var(--vh)); height: 100vh; - 不能只写
height: calc(100 * var(--vh, 1vh))——CSS变量fallback不支持单位运算,1vh会被当无效值忽略 - 安卓部分定制系统(如华为EMUI)
visualViewport.height返回异常,需加校验:if (v.height > 0 && v.height
别把--vh当全局常量用
它只适合控制“容器级高度”,比如全屏页、模态框、固定定位导航栏。以下场景容易翻车:
- 嵌套滚动区域里用
--vh算子元素高度 → 子元素实际高度受父容器overflow和滚动影响,和视口无关 - 动画中用
--vh做transform: translateY()→ 视口变化时动画会跳帧,视觉撕裂 - 服务端渲染(SSR)页面首次加载时,JS未执行,
--vh为初始值(比如100vh),可能造成布局偏移
真正关键的是:这个变量必须和visualViewport生命周期强绑定,断开监听就等于放弃响应能力——很多项目只在DOMContentLoaded设一次值,之后再也不更新,等于没做适配。


















