100vh在横屏下变小或错位是因为其基于初始方向的静态计算,不响应window.innerheight突变;应优先使用@supports检测的min-height:100dvh,兼容不足时用js动态同步--vh变量并节流监听resize与scroll。

为什么100vh在横屏下突然变小或错位
这不是单位写错了,是浏览器把“竖屏时的视口高度”直接复用到了横屏场景。iPhone 14 横屏时 window.innerHeight 从 844px 骤降到约 390px,但 100vh 仍按旧值渲染——结果元素被截、底部留白、fixed 定位飘走。本质是 vh 基于“初始方向”的静态计算,不响应真实尺寸突变。
- 地址栏收起、软键盘弹出、横竖屏切换都会改
window.innerHeight,但vh不更新 - iOS Safari 对
vw的更新也有延迟,尤其快速旋转时,CSS 还没重排完就已应用旧值 - 微信 WebView 和 X5 内核根本不信任
orientation媒体查询,只认window.innerHeight真实值
优先用 100dvh / 100dvw 替代 100vh / 100vw
100dvh(dynamic viewport height)是目前最直接有效的解法,它由浏览器原生支持,自动响应 window.innerHeight 变化,无需 JS 干预。但要注意兼容性断层:
- Chrome 100+、Safari 16.4+、Firefox 103+ 支持;Android Chrome 100 以下不支持
100dvw - 不能和
calc()混用100dvh:比如height: calc(100dvh - 60px)在部分 iOS 版本会解析失败,降级写法是min-height: 100dvh; padding-bottom: 60px - 兜底必须用绝对单位:CSS 中写
height: clamp(100px, 100dvh, 1000px),fallback 不能是100vh,而应是100%或固定844px(仅限 iOS 16+ 测试机型)
JS 动态同步 --vh 变量必须带节流 + 初始化 + fallback
当 100dvh 不可用(如老安卓 WebView),只能手动维护 --vh。但只在 DOMContentLoaded 里设一次就等于埋雷——横屏切回竖屏后,--vh 还是 390px 对应的值。
- 初始化必须立刻执行:
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`) - 监听
resize,但要节流(iOS 地址栏收起时不触发resize,得补监听scroll) - CSS 中赋值必须是
px单位,且 fallback 用绝对值:height: calc(var(--vh, 1px) * 100),不能写var(--vh, 1vh)——1vh是相对单位,无法参与calc数值运算
fixed 定位在横屏下失效的根本原因不是 orientation,而是包含块劫持
你以为是媒体查询没生效?其实更大概率是某个父级加了 transform、filter 或 opacity: 0.99,导致 fixed 元素的“锚点”从视口变成了那个父容器——横屏后坐标系翻转,bottom: 20px 就算对了也映射到错误方向。
- 逐层检查 Computed 面板,看
position: fixed元素的Containing Block是不是html;如果不是,往上找第一个带transform(哪怕只是translateZ(0))、will-change或opacity 的祖先 - 第三方组件(如 Swiper、Ant Design Mobile 弹窗)内部常默认加
transform: translateX(0),你没写,但它写了 - 解决方案不是删 transform,而是给 fixed 元素加
contain: layout paint(部分新内核有效),或直接换position: sticky——它不受包含块影响,且横竖屏切换时能自动响应滚动容器尺寸变化
真正难处理的从来不是“怎么写”,而是“什么时候该信 CSS,什么时候必须自己读 window.innerHeight”。横竖屏适配的本质是放弃让浏览器猜你的意图,转而用真实像素值驱动布局。任何依赖 orientation 媒体查询或静态 vh 的方案,在折叠屏、分屏、软键盘弹出等真实场景中,都可能漏掉一帧——而这帧,就是用户看到错位的全部时间。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











