如何处理滚动穿透

435 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

什么是滚动穿透?就是我们搞一个遮罩层,然后遮罩层上下滑动,底下也跟着滚动。

组织时间冒泡到body上可以吗?答案是不行,因为我们的滚动是浏览器原生实现的效果。而且scroll事件一般也是不冒泡的。我们滚动或者手机在屏幕上滑动的时候,有两种可能:一种是整个viewport触发滚动,这种的eventtarget就是document,另外就是元素里面触发了滚动,这个滚动的主题就是元素啦。所以滚动穿透是一个正常的浏览器行为。

我们可以禁止掉touchmove事件,但是这个会禁止掉整个的dom,如果dom里面有滚动咋办呢?

我们可以有选择性地禁止滚动事件,判断一下滚动发生的dom,符合我们的要求再禁止滚动,但是!如果滚动到头尾那么久又不行了。所以我们需要判断是否滑动到头部或者尾部。

所以我们的代码如下:

let loc = 0;
scrollElement.addEventListener('touchstart', (e) => {
    loc = e.target.clientY;
})
scrollElement.addEventListener('touchmove', e=> {
    const clientY = e.targetTouches[0].clientY - initialY;
      if (
        el.scrollTop + el.clientHeight >= el.scrollHeight &&
        clientY < 0
      ) {
        return e.preventDefault();
      }
      if (el.scrollTop <= 0 && clientY > 0) {
        return e.preventDefault();
      }
})

这样我们的整体思路,就是当需要禁止touchmove事件的时候,就把这个函数拿出来调用一下,禁止全局的滚动事件,除了指定的滚动区域。然后如果蒙层消失,立马恢复取消监听touchmove事件,通过这种方式,就能够实现禁止滚动且允许局部的滚动。

需要注意的是,我们一共监听了比较多的事件,取消监听的时候最好把所有的监听事件都给取消监听了,这样能避免内存泄露。

整个流程就是监听,不允许滚动,然后取消监听,允许滚动。