问题描述
在移动端 H5 开发中,我们经常会碰到输入框固定在页面底部的布局情况,比如聊天应用,文章评论等。当我们点击输入框输入文字时,系统会弹出虚拟键盘以便输入内容,这个时候会有几种情况出现:
- 输入框随着页面移动到键盘的上面,显示正常;
- 输入框被键盘挡住,收起键盘,再次点击输入框后和情况1一样;
- 输入框被键盘挡住,后续的收起/打开都依然是挡住的;
- 输入框没有被键盘挡住,但收起键盘后页面不会回落;
解决方案
只要是输入框在页面底部的就会有这些问题出现,下面我们分情况来寻找解决方案。
Android
Android 下解决起来比较简单,我们可以借助 Element.scrollIntoView() 这个方法来解决。顾名思义,这个方法的作用就是让当前的元素滚动到浏览器窗口的可视区域内,正是我们想要的效果。用之前我们要先看一下兼容性:
别怕,标1的只是不支持
smooth 选项,这个我们也用不到。所以除了一些非常老的机器都是支持的,兼容性没问题。
那么怎么用呢?这里我们要把 scrollIntoView() 的第一个参数设为 false,意思是元素的底端将和其所在滚动区的可视区域的底端对齐,具体可以查看 MDN 的文档。
// Syntax
element.scrollIntoView();
element.scrollIntoView(alignToTop); // Boolean parameter
element.scrollIntoView(scrollIntoViewOptions); // Object parameter
因为是键盘弹出后出现的问题,我们可以监听输入框的 focus 事件:
const input = document.querySelector('#input');
input.addEventListener('focus', () => {
input.scrollIntoView(false);
});
但这样还是不行,因为键盘弹出是有动画的,如果立即执行 scrollIntoView() 有时候输入框还是会被挡住。这里我们可以加一个 setTimeout() :
input.addEventListener('focus', () => {
setTimeout(() => {
input.scrollIntoView(false);
}, 300);
});
一般 300ms 就够了,如果不行可以适当再延长一下。
iOS
按道理,Element.scrollIntoView() 在 iOS 下也是可以用的,但很可惜实际测试下来大多都是无效的,所以我们需要找另外的的方法。
针对我们这种情况——让输入框不被挡住,其实也就是键盘弹出后需要让页面再次滚动到底部,所以我们可以借助 document.body.scrollTop 来实现,或者直接让它的值为页面高度就行了:
input.addEventListener('focus', () => {
setTimeout(() => {
document.body.scrollTop = document.body.scrollHeight;
}, 300);
});
小结
好了,现在我们只需要区分一下系统就可以解决问题了:
const ua = navigator.userAgent;
const iOS = /iPad|iPhone|iPod/.test(ua);
const input = document.querySelector('#input');
input.addEventListener('focus', () => {
setTimeout(() => {
if (iOS) {
if (!/OS 11_[0-3]\D/.test(ua)) {
document.body.scrollTop = document.body.scrollHeight;
}
} else {
input.scrollIntoView(false);
}
}, 300);
});
咦~怎么多了个判断把 iOS 11.0~11.3 给排除了?对,因为测试的时候发现,在这些系统下操作 scrollTop 或者 scrollIntoView 是有 bug 的,会把页面滚动到页面顶部。。。所以这里把它们排除了。
Android 的其它问题
Android 某些情况下还会出现这个问题,就是键盘显示了,但页面一动不动,好像键盘悬浮在页面上面:
键盘问题Android
如果碰到这个问题,单单用 js 已经是解决不了问题了,得需要 App 修改,具体方法可以参考 StackOverflow 的答案,原理的话可以查看《Android爬坑之旅:软键盘挡住输入框问题的终极解决方案》这篇文章。
iOS 的其它问题
不止 Android,iOS 也会碰到各种奇葩问题:
出来我就不回去了
这个是在乐动力、闲鱼、大麦、微信等 App 发现的问题,键盘收起后,页面不会回到底部,留出大片空白:
坚决不露头
在对接淘票票时出现了这个问题,就是用了上面的方法后,输入框依然不出现,不管用什么都不行:
躲猫猫
键盘弹出后,输入框却“消失”了:
小结
这些问题不是一开始就有的,都是之前好好的,然后突然出问题了,而且还是接二连三的集中在一段时间内爆发了。排查后发现这些 App 的 Xcode 版本进行了更新,升到了 v10。微信也回应这是 iOS 的 bug:
“出来我就不回去了”这种情况我们可以通过在
blur 事件中再次 scrollIntoView() 调整回来:
input.addEventListener('blur', () => {
document.body.scrollIntoView();
});
别的几个问题需要 iOS 开发尝试以下方法来解决:
- 使用 WKWebView;
- 更新 XCode;
- 如果用了 WindVane 请升级到最新版本;
- 键盘收起时
setNeedsLayout。
总结
你看单单一个输入框被键盘遮挡的问题就需要分这么多情况来处理,甚至还需要 App 开发一起修复。
总结一下:
- iOS:使用 WKWebView,同时用最新版的 XCode 来编译;
- Android:可能需要打 AndroidBug5497Workaround 补丁;
- H5:分情况处理 scrollIntoView。
const input = document.querySelector('textarea');
const target = document.querySelector('form');
// input 是输入框
// target 是需要对齐的容器,可选
riseInput(input, target);