解决IOS中微信浏览器软键盘弹出导致的若干Bug

问题 1 键盘弹起后会遮挡键盘上方的内容

在微信浏览器中,如果需要模拟一个类似微信聊天的窗口,那么一般情况下需要将输入框使用 fixed 定位放置在页面最下方。就像这样:

但是,在 IOS 中的虚拟键盘和 Android 里是不同的。在 IOS 中,虚拟键盘弹出以后,键盘上面的输入提示会比键盘弹出慢半拍,所以就会导致输入法的提示框将正常页面挡住的情况。

这时候,就需要在键盘弹出后,等待一段时间(几百毫秒),然后再将页面的滚动条进行调整,就可以让页面弹到键盘之上。

假设页面布局如下(使用了 Vue 框架),其中 Dialogues 组件是可以滚动的聊天内容,PageFooter 是使用 fixed 定位在页面底部的输入框:

1
2
3
4
5
6
7
8
9
10
<div id="index">
<div id="mainPanel">
<div id="dialogueContent">
<Dialogues></Dialogues>
</div>
</div>
<footer>
<PageFooter></PageFooter>
</footer>
</div>

然后,就可以在监听到软键盘打开事件(比如 dialogueContent 中 input 元素的 onclick 或者 onchange 事件)后,执行下面的语句,让 dialogContent 的滚动条向下滚动,这样里面的内容就不会被覆盖了。

1
2
3
4
5
6
7
let dialogueContent = document.querySelector('#dialogueContent')
setTimeout(function() {
dialogueContent.scrollTop = dialogueContent.scrollHeight
setTimeout(function() {
dialogueContent.scrollTop = dialogueContent.scrollHeight
}, 250)
}, 250)

至于为什么要触发两次呢,是因为 IOS 手机种类比较多,从五六年前 iPhone5s 到最新的 iPhoneXR 都有人在用,每种手机的响应速度也各不相同,有些手机可能在第一个 250ms 内还没有完成键盘弹出的工作,所以可以再加一个定时器来兼容旧的手机。

但问题也依旧存在,就是 fixed 定位的输入框在软键盘弹出以后就不会像 Android 一样固定在页面底部,而是可以上下滑动,类似于 absolute 定位。这时,可以将 body 设置为 absolute 定位,然后再将 MainPage 使用 absolute 定位于 body 底部。

问题 2 在虚拟键盘收起以后 body 定位的问题

紧接着,第二个问题就出现了。在点击虚拟键盘右上角的“完成按钮以后”,页面下方并没有回弹,而是定在了原来软键盘上方的位置,必须在页面上滑动两下才可以触发回弹。(特别是在大屏的 IOS 手机上)

大概是这样:

而在将 body 设置为 absolute 以后,情况更加离奇。页面 UI 是回弹了,但是触控事件响应的位置是没有回弹的,依旧是软键盘打开区域的上方。解决方法同样是必须在页面上方华东两下才能回弹。

大概是这样:

最开始的想法是去监听 resize 事件,如果软键盘收回,就强行调整 body 的高度,但是发现软键盘的弹出和收回并不会触发该事件,只得作罢。

最终,找到了一篇解决 类似问题的文章,才找到了解决方案。

大致思路就是在键盘收回以后,主动触发浏览器对页面的重绘操作。如何进行呢?只需要在监听到 onblur 事件以后,让页面滚动到原来的位置即可。

比如,组件模板中的 HTML 为

1
<input type="text" placeholder="发送内容" v-model="content" @blur="resizeWindow">

然后可以在 JS 的 methods 中添加一个函数:

1
2
3
resizeWindow() {
document.body.scrollTop = document.body.scrollTop
}

这样,输入框在失去焦点以后会触发页面的重绘,刚刚的问题就随之而解了。