问题背景
相信你开发弹窗(模态框)的时候,会大概率遇到这种问题:当你滑动弹窗时,会发现弹窗背面的页面也跟着滑动。什么?从没遇到过!可恶。。。你小子点个赞收藏一下可以走了。
前段时间开发一个中奖的弹窗页面,需求是用户抽完奖后显示中奖弹窗。开发过程中发现,弹出中奖窗口进行滑动时,无意中滑动到背后的页面,从而产生混乱,给用户带来不好的浏览体验。经过本人多次的尝试,对该问题总结出这个适用性较广、思路易懂的方法。接下来给大家详细说明
什么是滚动穿透问题
滚动穿透问题(Scroll Through Issue)是在弹出窗口(如模态框、弹出菜单等)出现时,背后的页面仍然可以滚动的现象。这可能会导致用户在与弹出窗口进行交互时,无意中滚动到背后的页面。
为啥会出现滚动穿透
问题的根本原因是弹出窗口的出现没有阻止背后页面的滚动行为。在用户与弹出窗口交互的过程中,如果鼠标滚轮或触摸操作发生在背后的页面上,浏览器会默认处理这些滚动事件,导致页面滚动,尽管弹出窗口仍然处于可见状态。
问题demo演示
可能小伙伴还是不懂滚动穿透是什么情况,看看demo就明白了
解决方法(敲黑板,重点来哩!!!
适用范围
- 底部页面:超出一屏有滚动条(不是用了overflow而出现的滚动条)
- 弹窗:有没有滚动条都可
废话不说,上代码!
解决思路(配合👆上面代码食用更香)
主要思路就是在弹窗显示前记录当前的滚动位置并固定底部页面,同时禁用了底部页面的滚动功能,从而解决了弹窗穿透问题。等到弹窗关闭后,可以使用之前备份的滚动位置将底部页面恢复到原始状态。
document.body.style.position = 'fixed': 这一行将页面的定位方式设置为fixed,这会使页面的位置固定不变,不随滚动条的滚动而变化。const scrollTop = window.scrollY: 这一行获取当前页面的垂直滚动位置(滚动条滚动的距离)。window.scrollY返回当前滚动条相对于文档顶部的垂直滚动距离。document.body.dataset.scrollTop = scrollTop.toString(): 这一行将获取到的滚动位置值转换为字符串,并将其存储在document.body元素的dataset属性中。这个操作是为了备份页面的滚动位置,以便后续恢复使用。document.body.style.top = -${scrollTop}px: 这一行将页面向上移动,使其整体上移,距离上边界的位移为负的滚动位置。这样做的效果就是将页面固定在原本的滚动位置,防止页面内容随着滚动条的消失而下移。document.body.style.bottom = '0': 这一行将页面底部定位到窗口底部,确保页面在垂直方向上占满整个窗口高度。setTimeout(() => { document.body.style.overflow = 'hidden'; }, 300): 这里使用了setTimeout函数,延迟300毫秒后将页面的溢出(overflow)属性设置为hidden。这样做可以防止用户通过滚动鼠标滚轮或触摸来滚动页面内容,因为设置为hidden后,页面的滚动条不再可见,也就无法滚动。
为啥要这样解决
-
问题一:为啥要用fixed+overflow搭配来解决,只用其中一个作用不是一样吗?
答:经本人测试,只用其中一个对真机(特别是ios系统,没错就是你🍎)不会生效,一定需要两个配合使用
-
问题二:为啥弹窗显示前要记录页面滚动的位置?
答:这是因为每次显示弹窗时加了position: fixed;出现了新问题: 它会导致显示弹窗后,body回滚到顶部。假如你在浏览某🎬网站时向下翻页了几屏后,突然出现一个弹层广告,关闭广告后你发现整个页面回到最初的顶部,你会不会气得爆炸😠?
-
问题三:还有其他方法可以解决这种问题么?
答:有!像css属性
overscroll-behavior:contain或者记录弹窗滚动距离(如果弹窗本身就有滚动条),如果触底或者到达顶部的时候,给父元素的滚动事件添加preventDefault;然后在弹窗消失的时候,移除preventDefault行为。此外还有很多方法,但是⚠️注意这些方法都要么不能兼容大部分情况,要么需要考虑边界情况比较多,故本人不赞成采用这些方法。
什么情况下不会有滑动穿透问题(这很可能是你没遇到的原因)
- 底部页面高度<=一屏,也就是底部页面根本不会滑动,那自然也不会出现滑动穿透问题
- 底部页面高度定死了,且overflow设为auto或scoll。这时候也不会出现滑动穿透的问题