最近做一个移动端的Web应用,因为是单页模式,所以少不了各种各样的弹出框,侧边栏,结果发现这些元素在滚动到边界时,滚动事件会传递给父元素,导致父元素开始滚动(在PC端也是一样存在这样的问题,而在移动端更为突出,有时即使不在边界也会导致父元素滚动,自身却不动)。
在网上搜寻一番后,找到很多关于这个问题的讨论,解决方法大致可以分为两类:
- (JS)监听元素
el
滚动事件event
,当el.scrollTop
或者el.scrollLeft
到达边界值的时候,用event.preventDefault()
方法取消浏览器默认操作和用event.preventDefault()
方法停止事件往上传播; - (CSS)在表层元素(弹出框,侧边栏等等)显示时,修改底层元素(一般是网页主体
body
)的样式,如position:fixed
、overflow:hidden
等等,使其不可滚动。
具体可以参考移动端滚动穿透问题,其中提到的“终极完美解决方案”便属上面的第二类。
疑虑
虽说是“终极完美解决方案”,但其实我是不相信的,因为修改了元素定位方式,元素需要重绘,内容显示位置也会改变,即使记录内容位置后续恢复,画面也少不免抖动,所以怎么可能是“完美”呢?
实践
尽管有所疑虑,还是要实践一下。于是简单地写一个测试样例了:
1 |
|
样例页面中,点击.main
时,将.nav
设为可见,且将body
的定位设为fixed
,向上偏移到body
本来的滚动高度,这样.nav
可以滚动而.main
不能滚动,就不会出现滚动穿透的问题;再点击.nav
,将.nav
设为不可见,且将body
的定位设为static
,取消body
的向上偏移并滚回本来的滚动高度。
有个问题
样例页面在Safari浏览器中运行正常,在Chrome浏览器中却有问题,因为Safari浏览器认为body
元素是滚动主体,而Chrome浏览器认为html
元素才是滚动主体。document
中获取body
元素的键值是body
,而获取html
元素的键值是documentElement
,所以要在Chrome浏览器中运行正常的脚步代码应该是:
1 | var main = document.querySelector('.main'); |
运行效果
在.nav
出现或消失时,认真看可以开到.main
有些小抖动,但并不明显,在今天移动端硬件(CPU,内存)和软件(浏览器)优化足够的情况下,这个滚动穿透的解决方法确实是可以放心使用的。
最后
开卷有疑,实践证明。