必须加height=device-height且禁用user-scalable=no,否则iOS Safari无法触发visualViewport.resize事件,JS无法感知键盘弹出;Android虽支持弱但无害,而user-scalable=no会导致viewport重绘失效,使软键盘适配逻辑完全崩溃。

viewport height=device-height 是软键盘感知的前提
不加 height=device-height,iOS Safari 的 window.innerHeight 在键盘弹出时纹丝不动,JS 根本收不到“视口被压缩”的信号。你写的 scrollIntoView 逻辑可能根本没触发,因为条件判断永远为假——不是代码错,是浏览器压根没告诉你键盘来了。
Android Chrome 对这个属性支持弱,但至少不会让情况更糟;而 iOS 上它直接决定 visualViewport.height 是否随键盘动态变化。实测中,漏掉这一项,90% 的“输入框被遮挡”问题都无解。
-
height=device-height必须和width=device-width、initial-scale=1.0同时存在,缺一不可 - 不要写成
height=812这类固定值,不同机型、横竖屏、折叠屏都会失效 - 老版本 WebView(如微信 X5 内核)可能忽略该参数,但不写就彻底失去机会
user-scalable=no 是 iOS 键盘行为的静默杀手
加上 user-scalable=no,iOS 会直接禁用键盘弹出时的 viewport 重绘机制,visualViewport.pageTop 和 visualViewport.height 永远等于屏幕物理高度,光标位置、滚动锚点全乱套。这不是兼容性问题,是行为层面的静默破坏——DevTools 看不出报错,但 JS 感知不到任何变化。
尤其在 hybrid App 中,native 层若未主动同步键盘状态,这个标签会让整个软键盘适配逻辑归零。
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 删掉
user-scalable=no,哪怕你只想要“禁止缩放”,也该用touch-action: manipulation替代 - 如果必须限制缩放,改用
maximum-scale=1.0+minimum-scale=1.0,它们不影响键盘事件流 - 检查 CMS 或 SSR 框架是否自动生成了重复的
viewport标签,后写的会覆盖前写的,容易悄悄带上user-scalable=no
focus 事件里调 scrollIntoView({ block: 'nearest', inline: 'nearest' }) 最稳
iOS Safari 在键盘弹出前就触发 focus,此时调 scrollIntoView 反而是最准的时机;Android 部分机型则在键盘已升起后才发 focus,但 { block: 'nearest', inline: 'nearest' } 能保证最小必要滚动,不把光标顶出视口顶部。
别等 resize 或 focusin:前者在 iOS 上延迟高且不稳定,后者在部分 Android 上根本不触发;更别在 click 里提前调,某些安卓 WebView 会在 focus 前执行,滚动直接失效。
- 监听
input.addEventListener('focus', () => { input.scrollIntoView({ block: 'nearest', inline: 'nearest' }); }); - 老版微信 X5 内核不支持
behavior: 'smooth',显式删掉更稳妥 - 对
textarea同样适用,但需注意其默认有滚动条,scrollIntoView优先级高于 CSSoverflow
blur 时不恢复滚动位置,页面就悬在半空
focus 时滚上去了,blur 时不处理,用户再点其他输入框,光标可能直接消失——因为页面还卡在上次滚动后的位置,而新焦点元素已在视口外。
不能简单 window.scrollTo(0, 0),会打断用户浏览上下文;也不能依赖 document.body.scrollTop,iOS 上它常为 0。
- 记录位置要用
const lastScrollTop = window.visualViewport?.pageTop || window.pageYOffset; - blur 时加 200ms 防抖,再执行
window.scrollTo(0, lastScrollTop) - fixed 底部输入框 + 遮罩层场景下,blur 时还需临时还原
position: fixed,否则遮罩层可能盖住内容
viewport 标签是否从解析第一刻就正确锁定了视口行为——它决定了浏览器要不要通知你键盘来了,以及通知得多准。


















