光标下移被键盘遮挡

做移动端页面,尤其是聊天、评论这种底部带输入框的场景,基本都会踩到一个坑:

在 iOS 部分机型上,点开输入框想打字,软键盘一弹出来,把输入框整个盖住了。你低头噼里啪啦打了一串字,结果连光标在哪都看不见,体验瞬间归零。

一开始我也很懵,明明布局是 position: fixed; bottom: 0,老老实实贴在屏幕底部,怎么就被键盘吃了呢?

今天就把这个坑的来龙去脉和解决办法梳理一下,希望能帮到同样踩坑的你。

问题出在哪

iOS 软键盘弹出时有个很「任性」的特点:它不像 Android 那样会把页面高度撑开或者压缩布局,而是直接盖在页面上面

更要命的是,键盘弹出的那一刻,window.innerHeight 根本不会立刻变化,你拿它去算位置,拿到的还是键盘弹出前的旧值。于是输入框还傻乎乎地以为自己在屏幕最底部,实际上早就被键盘盖住了。

思路

既然 innerHeight 不靠谱,那就换一个靠谱的参照物 —— window.visualViewport

它可以实时反馈当前真正可见区域的高度。那么键盘遮挡的高度,自然就是两者之差:

1
遮挡高度 = window.innerHeight - window.visualViewport.height

拿到这个差值后,把输入框的 bottom 动态上移这么多,就能把输入框「抬」到键盘上方。等键盘收起了,再把位移去掉,恢复原样。

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 输入框聚焦时
function showInputBox() {
inputEl.focus();
setTimeout(() => {
// 计算软键盘遮挡的高度
const bottom = window.innerHeight - window.visualViewport.height;
// 键盘弹出(bottom > 20)且是 iOS 时才处理
if (bottom > 20 && isIOS) {
isAbnormal = true;
// 通过 CSS 变量动态上移输入框
chatStyle['--bottom'] = bottom + 'px';
}
// 确保光标所在的输入框滚进可视区域
document.activeElement.scrollIntoViewIfNeeded();
document.activeElement.scrollIntoView();
}, 300);
}

// 输入框失焦(键盘收起)时
function handleBlur() {
if (isAbnormal) {
isAbnormal = false;
delete chatStyle['--bottom'];
}
}

样式上,让输入框的 bottom 引用这个变量,有值就偏移,无值就贴底:

1
2
3
4
.input-box {
position: fixed;
bottom: var(--bottom, 0);
}

几个注意点

  • 为什么要延迟 300ms:软键盘弹起来是有动画的,立即计算拿到的可能是还没稳定下来的高度,等一等更准。
  • 为什么加 isIOS 判断:Android 键盘一般会主动压缩布局,不需要这套处理,加上判断避免误伤正常机型。
  • 为什么用 CSS 变量:比起直接改 bottom 的数值,用变量更干净,失焦时删掉变量就自动复位,不用记原来是多少。
  • 为什么要 scrollIntoView:有的场景光抬输入框还不够,配合把光标滚入可视区,双保险,体验才稳。

写在最后

说实话,这个「键盘遮挡」问题在 iOS 上花样百出,网上答案也五花八门。我个人觉得 visualViewport 这套是目前最可靠、侵入最小的做法,改造成本低,效果也直观。

如果你也遇到过类似的坑,欢迎在评论区聊聊你的解法,一起少走弯路 😄

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%