在移动端 focus 输出框时会调起软键盘,这时视口(viewport)的高度变小,一屏幕展示的内容变少,我们可以滚动更长的距离。
JS 如何捕获视口高度的变化,答案是 window.innerHeight,具体如下:

enter image description here

正常情况是 focus 输入框,键盘弹出,输入框在视口垂直居中,页面无遮挡(通过滚动页面可以看到完整的页面内容)。
但现实是存在各种问题,但可以归结为两类:页面遮挡问题和滚动条位置问题。
这里列举几个场景,最后给出解决方案(可根据自己的实际情况再进行扩展)。

页面遮挡问题

某些安卓手机 window.innerHeight 不会因为弹出键盘而变化,无法捕获视口高度变化,页面内容被遮挡,可以通过增加内容的高度强制展示全部内容,代码如下:

const contentHeight = document.body.clientHeight;
document.body.style.height = `${contentHeight + 250}px`;
// window.scrollTo(0, ......) //手动滚动到相应位置

Tips:iOS8、iOS9 的 window.innerHeight 也不变化,但和安卓的表现不一样,具体如下:

  • iOS8 页面表现完全正常,无需做任何兼容性处理
  • iOS9 页面无遮挡,但滚动条的位置错误,可以在弹出键盘后(setTimeout 300)手动调用 window.scrollTo

滚动条位置问题

安卓系统的 Vivo 手机可以捕获视口高度变化,但是滚动条位置有问题,需要手动调用 window.scrollTo
Tips:iOS10 及以上正常但是调用 window.scrollTo 就会出问题。

解决方案代码

enter image description here

在 focus 输入框时调用 compatMobileKeyboard('focus'),blur 时 compatMobileKeyboard('blur')

代码比较简单,主要思路是在弹出/隐藏键盘前后比较 window.innerHeight 的变化,如果变化明显,即大于 200 则认为能捕获到视口变化。

为什么不比较是否相等,是因为 iOS 浏览器的页面最大化(就是滚动时浏览器的地址栏会变小,底部的 banner 条会隐藏,在安卓的 UC 浏览器也有该功能)时 window.innerHeight 会有大概 69 的变化,这里指定 200 是为了和最大化功能区别开。

根据能否捕获视口变化分成无遮挡和有遮挡两类,然后再处理滚动条位置的问题。

模拟滚动 iScroll

这里没有使用模拟滚动,模拟滚动会以视口的高度为基准,滚动超出视口的内容部分,上面列出的一些场景是无法捕获视口变化的,所以模拟滚动肯定会存在问题。

可以使用 scrollIntoView 来解决问题,但还有一些细节要处理,例如:弹出键盘并 scrollIntoView 到输出框时,这时滚动会有问题,可以在这个时机禁用滚动。

个人建议这种场景不要使用模拟滚动。