移动 web的坑,总是在你不经意间就能遇到 😒
问题
由于ios系统(最新的iphone机型(iphone 13),经测试其他机型也有同样的问题),弹起软键盘的时候,position: fixed会失效。(ios认为用户更希望的是元素随着滚动而移动,效果类似absoult定位)
与安卓不同的是,ios弹起软键盘时,原页面的高度不变,并且会把输入框滚动到可视区域(具体滚动到什么位置,什么样是滚动算法。我没有深究) 这会带来一个问题:页面最外层出现了滚动条(经测试,在bode上设置overflow:hidden并没有用)。直接导致吸顶功能无效
所以,目前有两个问题:
- 弹起软键盘时,页面最外层会有滚动条
- 弹起软键盘是,页面很可能会发生滚动
解决方案:
- 禁止页面滚动
- 弹起软键盘时,如果发生了滚动,则把滚动高度重置为0
下面以vue代码为例:
// 阻止事件冒泡
preventDefault(e: any) {
e.preventDefault()
}
mounted() {
// 1. 页面加载后,监听touchmove事件,并阻止它冒泡。以实现禁止页面滚动
document.body.addEventListener('touchmove', this.preventDefault, { passive: false })
// 2. 当软键盘弹起时,resize会变化。如果有滚动,则置为0
window.visualViewport.addEventListener('resize', () => {
if (document.documentElement.scrollTop !== 0) {
document.documentElement.scrollTop = 0
}
})
}
beforeDestroy() {
document.body.removeEventListener('touchmove', this.preventDefault)
}
按照上面的实现后,页面的滚动问题就处理好了。但新的问题又来了,吸顶功能失效
由于把页面的滚动禁止了,无法滚动的话,吸顶功能也就失去了它的意义。所以,我们需要让部分区域可以滚动(即:非吸顶 或 吸底 部分可以滚动)
增加如下代码:
touchend() {
document.body.addEventListener('touchmove', this.preventDefault, { passive: false })
}
touchstart() {
document.body.removeEventListener('touchmove', this.preventDefault)
}
并在对应dom上添加事件监听:
<div class="content-body scroll-box" @touchstart="touchstart" @touchend="touchend"></div>
到这一步后,发现页面滚动区域可以正常滚动了。但又有新问题,偶发的滚动时会触发到最外层的滚动条。
经过不断的探索和测试,发现当滚动区域的滚动条常驻时,不会触发外部滚动条。所以大胆猜测一下,可能是滚动区域的内容高度不够产生滚动条,当我们按住滚动区域进行滚动时,实际上触发了最外层的滚动。
让滚动区域永远存在滚动条。
.scroll-box {
height 110%; // 具体设置多少,可以根据自己的需要调整。
}
至此,可以完美完成吸顶功能。
总结:
这是ios系统的问题,前端只能自己想办法绕过它。解决方案分3步:
- 监听body的touchmove事件,并阻止冒泡
- 当touchstart滚动区域时,临时去掉touchmove监听事件,touchend时,添加回来
- 保证滚动区域永远有滚动条