弹层出现后阻止页面滚动的两种方案

5,097 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

介绍

相信很多小伙伴在做弹出层时,经常漏掉一个需求就是让屏幕固定住,不然页面滚动条还能再滚动,这或多或少会影响些用户体验。本期我们就用两种不同的手段阻止滚动条来修补掉这个影响用户体验的需求吧。

正文

原始场景搭建

code.juejin.cn/pen/7125614…

我们这里用 petite-vue 搭建了出现这个问题的原始场景,我们点击 open 按钮后就会激活 isShow 使弹框和蒙层出现,点击蒙层则会关闭弹框,此时可以发现出现弹层后,我们鼠标滚轮依然可以滚动。

无限制.gif

fixed定位阻止

code.juejin.cn/pen/7125620…

#app{
  &.has-dialog{
    position: fixed;
  }
}

当弹层出现后,我们会在主窗体上赋予浮动定位,此时整个界面就定住不动了,连滚动条都消失了,那肯定就从根本上禁止掉了页面滚动这个行为。但是会出现一个问题就是,如果你不设置 top 属性,那么弹层不仅出现时甚至关闭后,滚动条又回归到 0 ,这样又回带了新的用户体验问题。

<div id="app" :style="{top:-scrollTop+'px'}"></div>

不过也很好解决。我们样式上绑定一下 top ,然后就用js在每次显示弹层都会获取到当前的滚动条位置赋值到 scrollTop 上就可以了。

createApp({
  scrollTop: 0,
  openDialog() {
    this.scrollTop = window.scrollY;
    this.isShow = true;
  },
  closeDialog() {
    this.isShow = false;
    this.$nextTick(() => {
      window.scrollTo({
        top: this.scrollTop,
      })
    })
  }
}).mount()

当然,你还要注意一点,不能光想着弹层的出现,你还要在他关闭后,且渲染完毕了,再用 window.scrollTo 方法,把位置再还原回去。

这个方法因为在pc端滚动条会突然消失,所以可能多少有些突兀,但如果在移动端或者用的是自定义滚动条,就不存在这个问题了。

css限制.gif

禁用滚动条事件

code.juejin.cn/pen/7125629…

既然不想页面滚动,那么我们直接禁用滚轮的默认事件不就好了,当然你在弹层出现的时候可能会写这样的代码:

window.addEventListener("wheel", handleStopWheel)

function handleStopWheel(e) {
   e.preventDefault();
}

对的,这样看似没什么错误,但是运行起来会发现不仅滚动没有禁用掉,而且弹层出现后每次滚动都会在控制台不停的报错。

示意图1.png

可这是为什么呢?原来是浏览器为了提升自身性能所做的规定,在 wheelmousewheel 等事件的监听的时候,浏览器默认它是不会别阻止打断的,这更像是一个协议,比如说,你跟浏览器谈拢,告诉它,你大胆去做自己的事情吧,我不会阻止你的,之后的浏览器再也不会有所顾及拼命去完成任务,而你怎么喊它,它也不会听你的了。

那么,我们怎么让权限回到自己的手上呢?我们就要传入一个配置属性,它就是 passive ,如果是 true,则表示由 指定的函数 listener 永远不会调用 preventDefault() 。所以,我们要手动改成 false ,这样就可以阻止它的滚动且不会报错啦。

window.addEventListener("wheel", handleStopWheel, {
    passive: false
})

当然,还有一点不要忘记,就是在关闭弹层的时候,还要移除一下滚轮事件。

window.removeEventListener("wheel", handleStopWheel)

这个方案禁止滚动条,在pc端完全可以满足我们的体验需求,但有一点要注意的是,如果是在移动端,它可没有滚轮事件,你需要换成触摸事件来实现。

js限制.gif