配移动端全屏布局配了五年,每次100vh一写上去在手机上就跑出屏幕了——今天三个CSS单位把这个坑彻底填上了

配移动端全屏布局配了五年,每次 100vh 一写上去,在手机上就跑出屏幕了——底部被地址栏截掉一截,滑动的时候整个页面还跳一下。今天 CSS 终于给了三个原生单位,这个坑彻底填上了。

问题出在哪

移动端浏览器有「动态视口」的概念。同一部手机,地址栏展开时可视高度约 716px,折叠后变成 772px,键盘弹起时更小只有 500px 左右。传统 CSS 里写 height: 100vh,浏览器会按最大视口高度算,结果 100vh 实际在 iPhone 上就溢出了 50~80px。

早年大家靠 100% - address_bar_height 硬算,后来用 position: fixed 加各种负 margin hack,再后来用 JS 监听 resize 事件动态设置高度——这些方案都有人用,但每一种都有副作用。

三个新单位对症下药

Chrome/Safari/Firefox 从 2022 年开始陆续支持了三种新的视口高度单位:

dvh(dynamic viewport height)是最常用的那个——它会跟随浏览器 UI 的显隐实时变化,地址栏展开时自动缩小,折叠时自动放大,键盘弹起时也跟着收缩。用 height: 100dvh,固定全屏布局在所有状态下都不会溢出了。

svh(small viewport height)对应的是「浏览器 UI 占据最多空间」的最小视口。如果你要做一个永远贴屏幕底部的导航栏,用 bottom: 0; height: 100svh 可以保证它不会被地址栏或键盘盖掉。

lvh(large viewport height)反过来,是浏览器 UI 完全收起后的最大视口。固定顶栏导航在键盘弹出时不想跟着内容区一起压缩,就用 height: 100lvh 给它一个不动的基准。

实战怎么选

固定全屏容器(移动端 H5 页面最常见)→ height: 100dvh,配合 overflow: auto 让内部内容正常滚动。

固定底部标签栏(TabBar 类)→ position: fixed; bottom: 0; left: 0; right: 0; height: auto; max-height: 100svh,内容多时内部滚动,但不会超出可视范围。

页面顶栏(不希望被键盘挤出视野)→ height: 100lvh,键盘弹起时内容区动,导航栏原地不动。

落地代码

固定全屏容器,最推荐的写法:

.full-screen {
  height: 100dvh;
  overflow-y: auto;
}

固定底部栏:

.tabbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: auto;
  max-height: 100svh;
}

顶栏不被键盘影响:

.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: auto;
  max-height: 100lvh;
}

更精细的控制,可以监听键盘事件配合 CSS 变量:

const onKeyboardShow = () => document.documentElement.style.setProperty("--kb-height", "50vh");
const onKeyboardHide = () => document.documentElement.style.setProperty("--kb-height", "0");
document.addEventListener("virtualkeyboardshow", onKeyboardShow);
document.addEventListener("virtualkeyboardhide", onKeyboardHide);

兼容性和坑

所有主流浏览器 2023 年底已全部支持,caniuse 查询结果是 95%+ 覆盖率。旧浏览器降级可以这样写:

.my-container {
  height: 100dvh; /* 现代浏览器 */
  height: 100vh; /* 降级备选,实际会溢出但不会坏 */
  min-height: -webkit-fill-available; /* WebKit 老版补充 */
}

一个容易踩的坑:不要把 100dvh100vh 混用在同一个布局体系里——同一个页面里同时用两种单位,在键盘弹出时会因为基准不一致产生布局撕裂。选一个主单位贯穿全页。

结论

移动端全屏布局不需要 JS,不需要 hack,不需要 Fill-Available 这种私有属性,三个 CSS 单位各司其职就把过去五年的坑平了。现在写移动端 H5,直接 height: 100dvh 就是标准答案。

评论区

0 条评论

登录后可评论。

阿跨·跨端开发 596 阅读