移动端 Safari 中 100vh 为何总“短一截”?揭秘视口单位的真正用法
你有没有遇到过这种情况:在电脑上调试得好好的全屏页面,一放到 iPhone 上,底部突然多出一块白边?或者轮播图明明写了 height: 100vh ,却怎么都填不满屏幕?
这并不是你的 CSS 写错了。问题出在—— iOS Safari 对 vh 的理解,和你以为的不一样 。
尤其是在竖屏滚动时,地址栏自动隐藏后,原本被遮挡的空间释放出来,但你的元素高度却“定格”在了加载那一刻。于是,页面看起来像是“短了一截”。
这不是 bug,是行为差异。而解决它的关键,不是放弃 vh ,而是搞清楚它到底怎么算的,以及如何让它“动起来”。
为什么 100vh 在手机上不等于屏幕高度?
我们先来打破一个误解: 100vh 并不总是等于“你能看到的整个屏幕” 。
桌面 vs 移动:视口的定义不同
在桌面浏览器中,视口(viewport)基本是固定的——窗口大小决定了 1vh 的值。但在移动端,尤其是 iOS Safari,情况复杂得多。
当你打开一个网页时:
- 顶部有地址栏
- 底部可能有工具栏(如导航条)
这些 UI 元素会占用一部分屏幕空间。此时,Safari 认为“可视区域”就是去掉这些部分后的高度。于是:
/* 此时 100vh ≈ 屏幕总高 – 地址栏 – 工具栏 */
可一旦你开始下滑页面,Safari 为了让你看到更多内容, 悄悄把地址栏和底部栏收起来了 。实际可用高度变大了,但 100vh 的值呢? 没变!
📌 举个真实例子:iPhone 13 的屏幕高度是 844px。 初始状态,地址栏+底部栏共占约 90px → 100vh = 754px 滚动后,栏隐藏 → 可用高度变成 844px,但 100vh 还是 754px 结果:页面底下空了整整 90px!
这就是所谓“视觉溢出”的根源: CSS 的 vh 是静态快照,而用户的操作让视口动态变化了 。
那 window.innerHeight 呢?它也不靠谱?
有意思的是,JavaScript 提供的 window.innerHeight 是实时更新的。你可以监听 resize 事件来获取当前真正的可视高度。
这意味着:
