粥里有勺糖

vuePress-theme-reco 粥里有勺糖    2018 - 2023
粥里有勺糖 粥里有勺糖

Choose mode

  • dark
  • auto
  • light
关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
author-avatar

粥里有勺糖

285

文章

40

标签

关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
  • devlearn

    • 开发教程
    • 实践:利用ArrayBuffer实现预览指定目录下的所有文件的内容
    • 在linux-deepin上使用deepin-wine5完美运行腾讯会议/QQ/微信等此类应用
    • eslint插件开发教程
    • ServerLess之云函数实践-天气API
    • 移动端阻止弹窗下层页面被滑动
    • 小技巧:for of中获取index
    • Git常用的一些基本操作
    • 向页面注入js实现为图片和文字元素添加透明蒙层
    • 实践:使用jsencrypt配合axios实现非对称加密传输的数据
    • 封装dotenv库实现类似Vite加载环境变量的行为
    • 30行代码实现合并指定目录下的所有文件的内容
    • 马上中秋了,把鼠标指针变为小玉兔
    • Node中require与fs.readFile读取JSON文件的对比
    • 使用免费的七牛云OSS(10G)搭建个人的在线图床
    • 分享封装的一些七牛云OSS操作方法
    • 本地配置SSH免密远程登录服务器
    • 工具方法汇总
    • 腾讯云Serverless实践-Node.js服务部署
    • 腾讯云Serverless实践-静态网站托管
    • 为什么'\x1B'.length === 1?\x与\u知识延伸
    • Vite插件开发纪实:vite-plugin-monitor(上)
    • Vite插件开发纪实:vite-plugin-monitor(中)
    • Vite插件开发纪实:vite-plugin-monitor(下)
    • 解决Vite-React项目中js使用jsx语法报错的问题
    • webpack 项目接入Vite的通用方案介绍
    • webpack 项目接入Vite的通用方案介绍-草稿
    • 优雅的处理挂载window上的函数可能不存在的情况
    • Mac上抓包秒通关羊了个羊

移动端阻止弹窗下层页面被滑动

vuePress-theme-reco 粥里有勺糖    2018 - 2023

移动端阻止弹窗下层页面被滑动

粥里有勺糖 2022-01-22 技术笔记技术教程移动端开发

# 移动端阻止弹窗下层页面被滑动

在做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 (opens new window)属性是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演示

图片

  • demo地址 (opens new window)
  • PC扫码体验
    • 图片
  • demo源码地址 (opens new window)
Edit this page (opens new window)
Last Updated: 2022/5/15 12:46:34