height=device-height 是 ios 键盘行为的必要开关,缺失会导致 window.innerheight 和 css vh 锁死,js 无法感知高度变化;user-scalable=no 会静默禁用键盘重绘机制,必须替换为 yes 或省略。

viewport 的 height=device-height 为什么必须加
不加 height=device-height,iOS Safari 在软键盘弹出时 window.innerHeight 完全不变,JS 拿不到任何高度变化信号,所有基于视口计算的修复逻辑(比如滚动、位移)都会失效。这不是“效果不好”,而是压根没触发条件。
Android Chrome 对该参数支持较弱,但加了不会出错;不加反而在部分低端 Android 上可能加剧布局抖动。真正危险的是——它常被当成可选项忽略,而实际是 iOS 键盘行为的开关之一。
-
height=device-height告诉浏览器:布局视口高度应跟随设备物理高度,而非仅由键盘是否弹出决定 - 没有它,
visualViewport.height虽仍会变,但window.innerHeight和 CSSvh单位都锁死,导致样式和 JS 行为脱节 - 实测中,漏掉这一项是「明明写了 scrollIntoView 却没反应」的最常见原因
user-scalable=no 是静默破坏者,不是防缩放工具
user-scalable=no 在含输入框的页面里等同于埋雷。它不会报错,也不会提示,但在 iOS 上直接禁用键盘弹出时的 viewport 重绘机制——visualViewport.height 永远等于屏幕高度,resize 事件不触发,fixed 元素彻底失联。
很多团队把它当“防止用户误操作”的手段,结果是用户点输入框后光标消失、底部按钮被顶飞、页面卡死。这不是兼容性问题,是行为覆盖。
- 必须写成
user-scalable=yes或干脆省略(默认就是 yes) -
maximum-scale=1.0+minimum-scale=1.0可替代user-scalable=no实现“视觉上不可缩放”,且不影响键盘逻辑 - 老项目迁移时,重点 grep
user-scalable=no并替换,比修 JS 更优先
focus 里调 scrollIntoView({ block: 'nearest' }) 最稳
别等 resize 或 focusin,iOS Safari 在键盘弹出前就发 focus,此时调 scrollIntoView 才能抢在视口被压缩前完成定位;Android 部分机型则会在 focus 后极短时间内完成滚动,nearest 模式只做最小必要位移,避免把光标滚出可视区。
- 必须用
{ block: 'nearest', inline: 'nearest' }——smooth在老版微信 X5 内核里直接报错,start会强行滚到顶部,遮挡更严重 - 不要在
click里提前调,部分 Android 浏览器会先执行 click 回调再触发 focus,导致滚动目标还没获得焦点就执行了 - 示例:
input.addEventListener('focus', () => input.scrollIntoView({ block: 'nearest', inline: 'nearest' }));
visualViewport API 是精准感知键盘的唯一可靠方式
window.innerHeight 在不同机型、系统版本下行为割裂:visualViewport.height 才真实反映用户当前可见区域高度。它不依赖 resize 时机,也不受 viewport 锁定影响,只要键盘动,它就变。
但它需要主动监听,且必须配合聚焦状态记录——否则你只知道“视口变小了”,却不知道该把哪个输入框往上推。
- 监听
visualViewport.addEventListener('resize', ...),对比initialHeight和当前值,差值即键盘高度 - 用
focusin记录activeInput,focusout清空,确保每次只处理当前输入框 - 对
position: fixed底部元素,用transform: translateY(-${keyboardHeight}px)比改bottom更安全,不触发重排
height=device-height 和最常被误用的 user-scalable=no,恰恰是问题能否收敛的分水岭。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











