最稳解法是 focus 事件里调 scrollintoview;ios 需在 focus 触发时立即执行,android 用 { block: 'nearest', inline: 'nearest' };必须监听 input/textarea 自身 focus,禁用 behavior: 'smooth';viewport meta 必须含 height=device-height 且 user-scalable=yes;底部按钮改用 position: sticky;blur 后需防抖还原滚动位置。

focus 事件里调 scrollIntoView 是最稳解法
软键盘遮挡输入框,不是样式没写好,而是滚动没触发或时机错了。iOS 在 focus 触发时、键盘还没完全弹出前就已开始重排,此时调用 scrollIntoView 最准;Android 虽稍滞后,但用 { block: 'nearest', inline: 'nearest' } 可避免把光标顶出视口顶部。
- 必须监听
input或textarea自身的focus事件,不是click或全局focusin - 别加
behavior: 'smooth'——微信 X5 内核不支持,会静默失败 - 示例:
input.addEventListener('focus', () => input.scrollIntoView({ block: 'nearest', inline: 'nearest' }));
viewport meta 写错,JS 就白跑
90% 的“滚动无效”或“按钮漂移”,根源是 <meta name="viewport"> 锁死了浏览器对键盘弹出的响应能力。iOS 会因此忽略 visualViewport.height 变化,JS 根本感知不到键盘已起。
- 必须包含
height=device-height:否则 iOS 的window.innerHeight在键盘弹出后不变,所有高度判断失效 - 绝对禁用
user-scalable=no:它会让 iOS 静默禁用视口重绘,visualViewport.height永远等于屏幕物理高度 - 推荐写法:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes, height=device-height">
别用 position: fixed 布局底部按钮
软键盘弹出时 position: fixed 按钮被顶到键盘上方,不是 z-index 或层级问题,而是它锚定的是“视觉视口”,而键盘弹出会压缩该视口,但 fixed 元素定位不更新。
- 改用
position: sticky:父容器设position: sticky; bottom: 0;,本身不脱离文档流 - 外层容器必须有明确高度,推荐
height: 100dvh;(iOS 16+/Chrome 100+ 支持),不是100vh - 给父容器加
padding-bottom: 60px;(按按钮实际高度微调),为键盘腾出物理空间 - 删掉所有
transform: translateZ(0)和will-change: transform,它们会破坏 iOS 键盘滚动联动
blur 后不恢复,页面就悬在半空
focus 时滚上去了,blur 时不处理,用户再点别的输入框,光标可能直接消失——尤其 iOS 收起键盘后页面不自动回滚,留白+错位是常态。
- 监听
blur事件,配合 200ms 防抖再执行一次位置还原 - 记录滚动位置不能依赖
document.body.scrollTop(iOS 常为 0),要用window.scrollY或document.documentElement.scrollTop - 如果用了
body { overflow: hidden; }防滚动干扰,blur 后必须立刻恢复overflow: auto或原值
visualViewport、focus 时机、resize 是否触发的差异——真机测试必须做,DevTools 模拟器不触发真实的视口挤压。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











