关于移动端100vh的坑和终极解决方案,看这一篇就够了!

你肯定有遇到过这种情况:费尽心血写了个页面,容器设置了height: 100vh,在电脑上看完美无缺,结果到手机上......底部居然被浏览器地址栏或者工具栏给挡掉了!

例子 电脑浏览器

手机浏览器

可以看到顶部被浏览器工具栏给遮挡住了。

当你滚动页面时,浏览器地址栏和底部工具栏会自动隐藏/显示。但100vh取的是包含隐藏地址栏的完整视口高度,而不是当前可见部分的高度。

结果就是:设置了100vh的元素,实际高度会超出屏幕可见区域,底部内容直接被截断。

实例

来看个真实例子。假设你要做个登录页:

html 复制代码
<div class="login-page">
  <div class="login-form">
    <h2>欢迎登录</h2>
    <input type="text" placeholder="用户名">
    <input type="password" placeholder="密码">
    <button>登录</button>
  </div>
</div>
css 复制代码
.login-page {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

电脑上看没问题,但到手机上:

  1. 页面打开时,地址栏可见,登录按钮可能被推到可视区域外
  2. 滚动一下,地址栏隐藏了,页面又突然"跳"一下
  3. 用户体验极其糟糕

用户输完密码找不到登录按钮!

解决方案1:dvh(推荐)

css 复制代码
.login-page {
  height: 100dvh; /* 把vh换成dvh就行 */
}

dvh是CSS新规范,专门解决这个问题的。它会自动根据浏览器界面变化动态调整高度。

现代浏览器基本都支持了。如果担心老设备,可以加个回退:

css 复制代码
.login-page {
  height: 100vh;
  height: 100dvh; /* 优先使用dvh */
}

解决方案2:JavaScript动态计算(备用方案)

如果还需要支持很老的浏览器,可以用JS:

javascript 复制代码
function setRealVH() {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--real-vh', `${vh}px`);
}

// 初始化
setRealVH();

// 窗口变化时重新计算
window.addEventListener('resize', setRealVH);
window.addEventListener('orientationchange', setRealVH);

CSS中这样用:

css 复制代码
.login-page {
  height: calc(var(--real-vh, 1vh) * 100);
}

解决方案3:纯CSS Hack(不推荐但可用)

css 复制代码
.login-page {
  height: 100vh;
  height: -webkit-fill-available; /* 针对WebKit浏览器 */
}

@media (orientation: portrait) {
  .login-page {
    height: 100vh;
    height: -webkit-fill-available;
  }
}

这个方案兼容性一般,但知道一下也没坏处。

建议

  1. 新项目直接用dvh,简单有效
  2. 老项目逐步替换,先从关键页面开始
  3. 重要内容永远不要依赖视口底部,确保即使有遮挡也能滚动看到

总结

移动端100vh的坑确实烦人,但现在有了dvh这个终极解决方案,问题基本解决了。

下次再做全屏布局,记得用100dvh而不是100vh,保你平安无忧。

如果这篇文章帮你省了几个小时的调试时间,帮我点个爱心吧~

我是大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《写给小公司前端的 UI 规范:别让页面丑得自己都看不下去》

《只会写 Mapper 就敢说会 MyBatis?面试官:原理都没懂》

《别学23种了!Java项目中最常用的6个设计模式,附案例》

《Vue3+TS设计模式:5个真实场景让你代码更优雅》

《别再手写判空了!SpringBoot 自带的 20 个高效工具类》

相关推荐
QCY19 小时前
「完全理解」1 分钟实现自己的 Coding Agent
前端·agent·claude
一拳不是超人20 小时前
Electron主窗口弹框被WebContentView遮挡?独立WebContentView弹框方案详解!
前端·javascript·electron
anyup20 小时前
🔥2026最推荐的跨平台方案:H5/小程序/App/鸿蒙,一套代码搞定
前端·uni-app·harmonyos
雮尘20 小时前
如何在非 Claude IDE (TARE、 Cursor、Antigravity 等)下使用 Agent Skills
前端·agent·ai编程
icebreaker20 小时前
Weapp-vite:原生模式之外,多一种 Vue SFC 选择
前端·vue.js·微信小程序
icebreaker20 小时前
重走 Vue 长征路 Weapp-vite:编译链路与 Wevu 运行时原理拆解
前端·vue.js·微信小程序
wuhen_n20 小时前
代码生成:从AST到render函数
前端·javascript·vue.js
喝咖啡的女孩21 小时前
浏览器前端指南
前端
wuhen_n21 小时前
AST转换:静态提升与补丁标志
前端·javascript·vue.js
喝咖啡的女孩21 小时前
浏览器前端指南-2
前端