做移动端页面,尤其是聊天、评论这种底部带输入框的场景,基本都会踩到一个坑:
在 iOS 部分机型上,点开输入框想打字,软键盘一弹出来,把输入框整个盖住了。你低头噼里啪啦打了一串字,结果连光标在哪都看不见,体验瞬间归零。
一开始我也很懵,明明布局是 position: fixed; bottom: 0,老老实实贴在屏幕底部,怎么就被键盘吃了呢?
今天就把这个坑的来龙去脉和解决办法梳理一下,希望能帮到同样踩坑的你。
问题出在哪
iOS 软键盘弹出时有个很「任性」的特点:它不像 Android 那样会把页面高度撑开或者压缩布局,而是直接盖在页面上面。
更要命的是,键盘弹出的那一刻,window.innerHeight 根本不会立刻变化,你拿它去算位置,拿到的还是键盘弹出前的旧值。于是输入框还傻乎乎地以为自己在屏幕最底部,实际上早就被键盘盖住了。
思路
既然 innerHeight 不靠谱,那就换一个靠谱的参照物 —— window.visualViewport。
它可以实时反馈当前真正可见区域的高度。那么键盘遮挡的高度,自然就是两者之差:
1 | 遮挡高度 = window.innerHeight - window.visualViewport.height |
拿到这个差值后,把输入框的 bottom 动态上移这么多,就能把输入框「抬」到键盘上方。等键盘收起了,再把位移去掉,恢复原样。
关键代码
1 | // 输入框聚焦时 |
样式上,让输入框的 bottom 引用这个变量,有值就偏移,无值就贴底:
1 | .input-box { |
几个注意点
- 为什么要延迟 300ms:软键盘弹起来是有动画的,立即计算拿到的可能是还没稳定下来的高度,等一等更准。
- 为什么加
isIOS判断:Android 键盘一般会主动压缩布局,不需要这套处理,加上判断避免误伤正常机型。 - 为什么用 CSS 变量:比起直接改
bottom的数值,用变量更干净,失焦时删掉变量就自动复位,不用记原来是多少。 - 为什么要 scrollIntoView:有的场景光抬输入框还不够,配合把光标滚入可视区,双保险,体验才稳。
写在最后
说实话,这个「键盘遮挡」问题在 iOS 上花样百出,网上答案也五花八门。我个人觉得 visualViewport 这套是目前最可靠、侵入最小的做法,改造成本低,效果也直观。
如果你也遇到过类似的坑,欢迎在评论区聊聊你的解法,一起少走弯路 😄

