应监听 visualviewport.resize 而非 window.resize,因 ios safari 不触发 resize 事件;visualviewport.height 实时准确,配合其 resize 事件或 focus/blur + settimeout 降级方案可可靠适配键盘弹出。

为什么只监听 window.resize 无法修复 iOS 键盘弹出错位
iOS Safari 在软键盘弹出/收起时根本不会触发 window.resize 事件,这是最常踩的坑。你写的 resize 监听器在真机上完全静默,导致高度始终卡在初始值(比如 100vh),.main 区域被压缩、输入框被遮挡、滚动锚点残留——所有现象都源于这个误判。
Android Chrome 虽然会触发 resize,但时机不可靠:键盘展开有延迟,window.innerHeight 可能先跳变再回弹,单次读取极易拿到错误值。
- 不要用
document.documentElement.clientHeight—— 它在 iOS 上不随键盘变化更新 - 避免在
focus回调里立即读高度 —— 键盘动画未完成,此时visualViewport?.height还没稳定 - 必须加
setTimeout(() => {}, 50)防抖,或监听visualViewport?.resize(iOS 16.4+ 支持)
真正该监听的是 visualViewport.resize,不是 window.resize
visualViewport 是浏览器暴露的“当前可见视口”对象,它的 height 属性在 iOS 键盘弹出时实时更新,比 window.innerHeight 准确得多。而 visualViewport.resize 事件才是 iOS 上唯一可靠的高度变化信号源。
兼容性处理要分层:
- 现代 Safari / Chrome(
visualViewport存在)→ 直接监听visualViewport.addEventListener('resize', handler) - 老版 Safari(
visualViewport不存在)→ 退回到focus/blur+setTimeout组合,配合高度阈值判断(如比初始值小 >150px 才认为键盘已弹出) - 绝对不要降级为纯
window.resize,它在 iOS 上等于没写
示例关键逻辑:
if ('visualViewport' in window) {
const updateHeight = () => {
document.documentElement.style.setProperty('--vh', `${window.visualViewport.height}px`);
};
window.visualViewport.addEventListener('resize', updateHeight);
updateHeight(); // 初始化
} else {
const initHeight = window.innerHeight;
const onFocus = () => setTimeout(() => {
if (window.innerHeight <h3>监听之后,CSS 怎么用动态高度替代 100vh</h3><p>光监听没用,CSS 必须配合使用自定义属性(<code>--vh</code>)和 <code>min-height</code>/<code>max-height</code> 组合,不能继续依赖 <code>height: 100vh</code>。</p>
-
min-height: 100dvh是首选,但仅限 Chrome 105+/Safari 16.4+;不支持时必须 JS 注入--vh并在 CSS 中引用 - 容器高度必须写成
min-height: calc(var(--vh, 100vh)); max-height: calc(var(--vh, 100vh));,双保险 -
overflow-y: auto必须加在 flex 容器上(如.container),而不是子项,否则滚动会失效或嵌套错乱 - 对
.main区域,在高度更新后手动调用element.scrollTo(0, 0),防止 iOS 滚动位置残留
CSS 示例:
.container {
display: flex;
flex-direction: column;
min-height: calc(var(--vh, 100vh));
max-height: calc(var(--vh, 100vh));
overflow-y: auto;
}
.main {
flex: 1;
overflow-y: auto;
}
flex-shrink: 0 不是摆设,但必须配 min-height 或显式 height
flex-shrink: 0 确实能防止头部、底部、输入框容器被压缩,但它本身不提供尺寸保障。如果父容器高度塌陷,这些区域仍可能视觉消失或高度归零。
-
.header和.footer必须同时设flex-shrink: 0和min-height: 44px(或height: 44px),否则 iOS 下依然会塌 - 输入框所在容器(如
.input-wrapper)建议用flex: 0 0 auto,语义比单独flex-shrink: 0更明确 - 避免只对
.main设flex: 1—— 当可用高度极小时(如横屏+键盘),它会强行撑开并挤压其他区域;改用flex: 1 1 auto更可控
最易忽略的一点:即使高度动态修正了,子项仍可能因 padding、border 或 box-sizing 计算偏差被意外截断。务必检查所有 flex 子项是否设置了 box-sizing: border-box。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











