直接fallback到100vh不行,因ios 15.x及safari 16.3前版本中100vh锁定初始视口高度,不响应地址栏显隐;应监听resize事件配合requestanimationframe动态设置--dvh,并叠加min-height:100vh; min-height:100dvh;,同时补focusin/focusout处理键盘弹出,且必须设置viewport-fit=cover。

为什么直接 fallback 到 100vh 不行
因为 100vh 在 iOS 15.x、Safari 16.3 及更早版本中,依然会锁定在“初始视口高度”(即地址栏收起时的最大高度),导致页面刚加载时内容被裁切、底部按钮被遮挡。这不是 bug,是规范行为——vh 定义本就不响应地址栏显隐。你写 height: 100vh,浏览器就真按它“认为的视口”算一次,之后再也不更新。
用 window.innerHeight + resize 监听是最稳的降级方案
现代移动端浏览器在地址栏收起/展开、键盘弹出/收起、横竖屏切换时,都会触发 resize 事件,并同步更新 window.innerHeight 值。这是唯一被所有主流 WebView 和 Safari 版本一致支持的动态高度来源。
在 Linux 上通过 Chrome 挂载/VNC 自动发布小红书长笔记。Agent 撰写标题和正文后执行 xhs_publish.sh --publish --submit,触发小红书发布。
- 必须监听
resize,不能只靠scroll或orientationchange:后者在部分安卓机型上不触发,且键盘弹出时resize是唯一可靠信号 - 加
requestAnimationFrame节流,避免高频重设样式造成卡顿:resize在 iOS 上可能每帧都触发 - 别只在
mounted或DOMContentLoaded里设一次高度——首次渲染时window.innerHeight还没稳定(尤其带 splash screen 的 PWA) - 示例核心逻辑:
function updateDvhFallback() { document.documentElement.style.setProperty('--dvh', `${window.innerHeight}px`); } let rafId; window.addEventListener('resize', () => { cancelAnimationFrame(rafId); rafId = requestAnimationFrame(updateDvhFallback); });
@supports (height: 100dvh) 会误伤旧版 Safari
Safari 16.3 会识别 @supports (height: 100dvh) 为 true,但实际不支持该单位,导致 CSS 规则被应用却无效,最终高度塌陷。这不是你代码写错,是 WebKit 的实现缺陷。
- 不要用
@supports包裹100dvh声明来降级 - 正确写法是层叠覆盖:
min-height: 100vh; min-height: 100dvh;—— 新浏览器用后一条,老浏览器跳过不认识的单位,自然回退到前一条 - 但注意:这只能解决“有 dvh 支持”的场景;对真正不支持 dvh 且
100vh行为异常的环境(如 iOS 15.6),仍需 JS 补偿
JS 降级必须处理键盘弹出这个隐藏陷阱
键盘弹出时,window.innerHeight 会骤降(比如从 812px → 500px),但很多开发者只监听了 resize,忘了 focusin / focusout 事件本身也可能触发布局变化,尤其在某些 Android WebView 中 resize 延迟或丢失。
- 给所有
<input>和<textarea></textarea>绑定focusin,手动触发一次updateDvhFallback() - 在
focusout后加setTimeout(..., 300)延迟更新——等键盘完全收起再读取高度,否则可能拿到过渡中的错误值 - 如果页面有 fixed 底部操作栏,记得同时更新其
top或bottom值,否则它会悬在半空
meta viewport 里加 viewport-fit=cover。没有它,iOS 会强制保留安全区边距,即使高度算准了,内容也会被挤偏。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










