移动端阻止弹窗下层页面被滑动方法介绍

在做H5开发时,很多场景下都需要弹窗。当出现弹窗时,大部分场景下是期望弹窗下层页面不能被滑动的。
首页 新闻资讯 行业资讯 移动端阻止弹窗下层页面被滑动方法介绍

在做H5开发时,很多场景下都需要弹窗。

当出现弹窗时,大部分场景下是期望弹窗下层页面不能被滑动的。

当然也不期望弹窗被滑动

近期肝页面又碰到了这个问题

下面介绍几种最常用的方式,以及一些边界情况与应对策略。

overflow:hidden

流传最广的方式就是 给元素设置 overflow:hidden

给body设置,就能达到阻止页面滑动的目的

复制

document.body.style.overflow = 'hidden'document.body.style.overflow = 'visible'
  • 1.

  • 2.

大部分情况下这个是能有效果的

但在部分机器上,这个是不生效的:

弹窗遮罩

还有一种情况如下,页面部分元素有局部滑动

复制

<body style="overflow:hidden;">
  <div style="overflow:scroll;height:100%;"><!-- more element -->
  </div></body>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

当前情况给body设置 overflow:hidden依旧是无效果的

此时给弹窗加上遮罩如下,正常情况下,下层元素就不会收到touchmove事件

复制

<body style="overflow:hidden;">
  <div style="overflow:scroll;height:100%;"><!-- more element -->
  </div>

  <!-- dialog -->
  <div class="dialog"><!-- 遮罩 --><div class="mask" style="position:fixed;inset:0;"></div><div class="content"></div>
  </div></body>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

其中inset属性是left,top,right,bottom的简写

但在部分机型下,下层元素仍然会收到touchmove事件,因此会跟着滑动

于是需要祭出下面的方法

prevent touchmove

阻止触摸滑动事件touchmove的默认行为

复制

const touchHandle = function(e) {
  e.preventDefault()}// 弹窗的事件{
  onShow(){document.body.addEventListener('touchmove', preventDefault, {  passive: false,});
  },
  onHide(){document.body.removeEventListener('touchmove', preventDefault);
  }}
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

在弹窗打开时直接阻止目标元素的滑动事件的默认行为

弹窗内容是不可滑动的话,那么这种方法是最省事高效的

如果弹窗中有可滑动的内容,且滑动的内容比较复杂

那么通过touchmove去细力度的控制阻止滑动事件时就很麻烦

position:fixed

还有一种常用的就是position:fixed

在弹窗打开时,将目标元素进行固定,在关闭时恢复

由于定位会改变元素在页面上的位置,所以需要在fixed前记录元素的位置

取消fixed之后将元素又滚动到原来的位置

复制

// 弹窗的事件{
  onShow(){document.body.style.top = `${  document.body.getClientRects()[0].top}px`;document.body.style.position = 'fixed';document.body.style.left = '0';document.body.style.right = '0';
  },
  onHide(){document.body.style.position = 'visible';window.scrollTo(  0,  Math.abs(+document.body.style.top.replace('px', '')));
  }}
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

使用class代替style

这个也是碰巧发现的,在iOS低端机将上述方式都尝试后

仍发现一个问题,现象如下(TODO:补图)

下层页面不会被滑动了,但遮罩和弹窗整体还能被下拉

弹窗是一个下拉列表弹窗,其出现的位置需要动态的计算,如下结构

复制

<body style="overflow:hidden;">
  <!-- dialog -->
  <div class="dialog" style="top:88px;"><!-- 遮罩 --><div class="mask" style="position:fixed;inset:0;"></div><!-- 内容 --><div class="content"></div>
  </div></body>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.


最终发现是由于style与class设置的样式在这个机型上展示虽然一致

但实际交互起来的表现却不一致

修复后的html结构如下,在元素里插入了一个style标签,使用class选择器与!important重载这个距离的样式

复制

<body style="overflow:hidden;">
  <!-- dialog -->
  <div class="dialog" style="top:88px;"><!-- 遮罩 --><div class="mask" style="position:fixed;inset:0;"></div><!-- 内容 --><div class="content"></div><style>  .dialog{top:88px !important;  }</style>
  </div></body>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

代码如下

复制

{
  onShow(){setTimeout(() => {  const dialogEl = document.querySelector<HTMLElement>('.dialog')  if (!dialogEl) {return      }  const style = document.createElement('style')  style.textContent = `      .dialog{top:${dialogEl.style.top} !important;  }  `
      dialogEl.append(style)}, 200)
  }}
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

非常令人迷惑的一个操作,但就是解决了问题

小结

针对移动端弹窗下层页面可被滑动的异常场景

本文介绍了4种常见解决方法,与1种"谜之操作"

demo演示91e9a1d60db6f3597bd467b2a47193dbcfec16.webpdemo截图

23    2022-01-26 12:28:48    移动端 弹窗 滑动