在移动web页面中,很容易遇到,位于页面底部的输入框,在获取焦点时,被弹起的软键盘遮挡的情况。近期的一个项目就面对了这样一个问题。
我们需要做的一个页面,视觉是这样的:
可选择的布局方案有很多种,我最终选择了flex
布局,使输入框固定于页面底部。因为使用fixed
定位会带来键盘弹起后输入框无法复位的兼容性问题,而使用absolute
布局又需要手动计算聊天区域内容高度(calc(100vh-输入框区域高度)
),相比之下flex
布局写法简洁一些。
写完后,如果不做任何处理,我们会发现,在部分IOS手机上,使用第三方输入法,软键盘弹起时,会把输入框遮住。
于是,我们进行了下列处理:
第一阶段
软键盘弹起后(即输入框focus后)设置定时器,反复执行下面代码:
1 | inputElement.scrollIntoView(true); //元素的顶端与可视区域的顶端对齐 |
或把以上代码换为:1
document.body.scrollTop = document.body.scrollHeight;
对以上代码进行测试,结果发现:
IOS系统版本在11.0(>=11.0) - 11.3(<11.3)之间的手机,输入框都完全被软键盘遮挡(包括系统键盘和第三方键盘)。IOS系统版本不在此范围内的手机,以及安卓手机,使用自带键盘或第三方键盘,均未发现问题。
(以上结果的测试环境为微信内置浏览器,使用safari浏览器测,输入框会高出软键盘一小段距离。接下来的测试环境也均为微信内置浏览器)
原因解释:
IOS11.0-11.3 对scrolIntoView
及scrollTop
的解释有bug,直接执行会导致输入框滚到底部被遮挡。
第二阶段
发现当键盘弹起后不做任何处理时,IOS11.0-11.3的手机的自带输入法的软键盘不会遮挡输入框,但使用第三方输入法的输入框会被遮挡,原因是第三方输入法比IOS自带输入法多了一个toolbar,高度为88px,IOS系统未将其识别为软键盘的高度增加。为了优先兼容自带输入法,我们在代码中做了系统判断,如果手机系统版本在IOS11.0-11.3之间,则js不做任何处理,否则则执行以上强制滚动的代码。
我将以上代码写成了插件形式,代码地址为:https://github.com/nieyt/input-show
后期优化方向: 兼容特殊版本区间的第三方输入法;提高键盘弹出及收起时在观感上的流畅度
其他踩坑&思考
发现键盘弹起时使用
scrolIntoView
或scrollTop
方法,输入框依然被遮挡的手机,键盘弹起时window.innerHeight
未发生改变。我们可以在输入框focus时使用定时器延时获取键盘弹起时window.innerHeight
的高度,与页面初始window.innerHeight
的高度进行对比,如果未发生改变,则不执行scrolIntoView
或scrollTop
等方法。但这样做的缺点是,我们无法知道键盘弹起的延时时间,只能设置较长的延时时间,导致判断延时过长。第三方输入法比系统输入法多出88px的toolbar,我们可以用
position: absolute
布局页面,当键盘弹起时,设置inputElement.css('bottom', '88px')
,可兼容使用第三方输入法手机的输入框不被遮挡,但会导致使用系统自带输入法的手机,输入框距离键盘位置高出88px。由于上一条所说的,部分手机上软键盘弹起时页面window.innerHeight
不发生改变,所以我们无法通过window.innerHeight
来判断输入框是否在可视区域内(使用document.documentElement.clientHeight
也同window.innerHeight
,不发生改变),因此无法判断手机使用的是哪种输入法。既然没有找到完美的解决方案,我们也可以从产品设计上做出改变,比如和产品商量在输入框下面增加一条横栏(比如工具栏设计等),只要该横栏高于88px就行,就再也不用担心第三方输入法遮挡输入框的问题啦。像这样(毫无违和感):