蒙层禁止页面滚动的方案

弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互。但是在蒙层出现的时候滚动页面,如果不加处理,蒙层底部的页面会开始滚动,实际上我们是不希望他进行滚动的,因此需要阻止这种行为。当弹出蒙层时禁止蒙层下的页面滚动,也可以称为滚动穿透的问题,文中介绍了一些常用的解决方案。

实现

首先需要实现一个蒙层下滚动的效果示例,当我们点击弹窗按钮显示蒙层之后,再滚动鼠标的话能够看到蒙层下的页面依旧是能够滚动的。如果在蒙层的内部进行滚动,当蒙层内滚动条滚动到底部的时候再继续滚动的话,蒙层下的页面也是能够滚动的,这样的交互就比较混乱,文中内容的测试环境是Chrome 96.0.4664.110

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>蒙层禁止页面滚动的方案</title><style type="text/css">#mask{position: fixed;height: 100vh;width: 100vw;background: rgba(0, 0, 0, 0.6);top: 0;left: 0;display: flex;align-items: center;justify-content: center;}.hide{display: none !important;}.long-content > div{height: 300px;}.mask-content{width: 300px;height: 100px;overflow-x: auto;background: #fff;}.mask-content > div{height: 300px;}</style>
</head>
<body><button id="btn">弹窗</button><div class="long-content"><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div></div><div id="mask" class="hide"><div class="mask-content"><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div></div></div>
</body><script type="text/javascript">(() => {const btn = document.getElementById("btn");const mask = document.getElementById("mask");btn.addEventListener("click", e => {mask.classList.remove("hide");})mask.addEventListener("click", e => {mask.classList.add("hide");})})();</script>
</html>

body hidden

此方案是一种比较常用的方案,即打开蒙层时给body添加overflow: hidden;,在关闭蒙层时就移除这个样式,例如思否的登录弹窗、antdModal对话框就是这样的方式。 这种方案的优点是简单方便,只需添加css样式,没有复杂的逻辑。缺点是在移动端的适配性差一些,部分安卓机型以及safari中,无法阻止底部页面滚动,另外有些机型可能需要给根节点<html>添加overflow: hidden;样式才有效果,此外由于实际上是将页面的内容给裁剪了,所以在设置这个样式的时候滚动条会消失,而移除样式的时候滚动条又会出现,所以在视觉上是会有一定的闪烁现象的,当然也可以定制滚动条的样式,但滚动条样式就是另一个兼容性的问题了,还有同样是因为裁剪。

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>蒙层禁止页面滚动的方案</title><style type="text/css">#mask{position: fixed;height: 100vh;width: 100vw;background: rgba(0, 0, 0, 0.6);top: 0;left: 0;display: flex;align-items: center;justify-content: center;}.hide{display: none !important;}.long-content > div{height: 300px;}.body-overflow-hidden{overflow: hidden;}.mask-content{width: 300px;height: 100px;overflow-x: auto;background: #fff;}.mask-content > div{height: 300px;}</style>
</head>
<body><button id="btn">弹窗</button><div class="long-content"><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div></div><div id="mask" class="hide"><div class="mask-content"><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div></div></div>
</body><script type="text/javascript">(() => {const btn = document.getElementById("btn");const mask = document.getElementById("mask");const body = document.body;btn.addEventListener("click", e => {mask.classList.remove("hide");body.classList.add("body-overflow-hidden");})mask.addEventListener("click", e => {mask.classList.add("hide");body.classList.remove("body-overflow-hidden");})})();</script>
</html>

touch preventDefault

上边的方案对于移动端的效果不是很理想,如果需要在移动端进行处理的话,可以利用移动端的touch事件,来阻止默认行为,当然这是适用于移动端的方式,另外要是把手机通过蓝牙也好转接线也好接上鼠标的话,那就是另一回事了。假如蒙层内容不会有滚动条,那么上述方法是没有问题的,但是假如蒙层内容有滚动条的话,那么它再也无法动弹了。所以如果在蒙层内部有元素需要滚动的话,需要用Js控制其逻辑,但是逻辑控制起来又是比较复杂的,我们可以判断事件的event.target元素,如果touch的目标是弹窗不可滚动区域即背景蒙层就禁掉默认事件,反之就不做控制,之后又出现了问题,需要判断滚动到顶部和滚动到底部的时候禁止滚动,否则触碰到上下两端,弹窗可滚动区域的滚动条到了顶部或者底部,依旧穿透到body,使得body跟随弹窗滚动,这样的话逻辑的复杂程度就比较高了。

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>蒙层禁止页面滚动的方案</title><style type="text/css">#mask{position: fixed;height: 100vh;width: 100vw;background: rgba(0, 0, 0, 0.6);top: 0;left: 0;display: flex;align-items: center;justify-content: center;}.hide{display: none !important;}.long-content > div{height: 300px;}.mask-content{width: 300px;height: 100px;overflow-x: auto;background: #fff;}.mask-content > div{height: 300px;}</style>
</head>
<body><button id="btn">弹窗</button><div class="long-content"><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div><div>long content</div></div><div id="mask" class="hide"><div class="mask-content"><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div></div></div>
</body><script type="text/javascript">(() => {const btn = document.getElementById("btn");const mask = document.getElementById("mask");const body = document.body;const scrollerContainer = document.querySelector(".mask-content");let targetY = 0; // 记录下第一次按下时的`clientY`scrollerContainer.addEventListener("touchstart", e => {targetY = Math.floor(e.targetTouches[0].clientY);});const touchMoveEventHandler = e => {if(!scrollerContainer.contains(e.target)) {e.preventDefault();}let newTargetY = Math.floor(e.targetTouches[0].clientY); //本次移动时鼠标的位置,用于计算let scrollTop = scrollerContainer.scrollTop; // 当前滚动的距离let scrollHeight = scrollerContainer.scrollHeight; // 可滚动区域的高度let containerHeight = scrollerContainer.clientHeight; //可视区域的高度if (scrollTop <= 0 && newTargetY - targetY > 0) { // 到顶console.log("到顶");if(e.cancelable)  e.preventDefault(); // 必须判断`cancelable` 否则容易出现滚动正在进行无法取消的`error`} else if (scrollTop >= scrollHeight - containerHeight && newTargetY - targetY < 0 ) { // 到底console.log("到底");if(e.cancelable) e.preventDefault(); // 必须判断`cancelable` 否则容易出现滚动正在进行无法取消的`error`}}btn.addEventListener("click", e => {mask.classList.remove("hide");body.addEventListener("touchmove", touchMoveEventHandler, { passive: false });})mask.addEventListener("click", e => {mask.classList.add("hide");body.removeEventListener("touchmove", touchMoveEventHandler);})})();</script>
</html>

body fixed

目前常用的方案就是该方案了,要阻止页面滚动,可以将其固定在视图中即position: fixed,这样它就无法滚动了,当蒙层关闭时再释放,当然还有一些细节要考虑,将页面固定视图后内容会回头最顶端,这里我们需要记录一下用来同步top值,这样就可以得到一个兼容移动端与PC端的较为完善的方案了,当然对于浏览器的api兼容性是使用document.documentElement.scrollTop控制还是window.pageYOffset + window.scrollTo控制就需要另行适配了。在示例中为了演示弹窗时不会导致视图重置到最顶端,将弹窗按钮移动到了下方。

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>蒙层禁止页面滚动的方案</title><style type="text/css">#mask{position: fixed;height: 100vh;width: 100vw;background: rgba(0, 0, 0, 0.6);top: 0;left: 0;display: flex;align-items: center;justify-content: center;}.hide{display: none !important;}.long-content > div{height: 300px;}.mask-content{width: 300px;height: 100px;overflow-x: auto;background: #fff;}.mask-content > div{height: 300px;}</style>
</head>
<body><div class="long-content"><div>long content</div><div>long content</div><div>long content</div><button id="btn">弹窗</button><div>long content</div><div>long content</div><div>long content</div><div>long content</div></div><div id="mask" class="hide"><div class="mask-content"><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div><div>mask-content</div></div></div>
</body><script type="text/javascript">(() => {const btn = document.getElementById("btn");const mask = document.getElementById("mask");const body = document.body;let documentTop = 0; // 记录按下按钮时的 `top`btn.addEventListener("click", e => {mask.classList.remove("hide");documentTop = document.scrollingElement.scrollTop;body.style.position = "fixed"body.style.top = -documentTop + "px";})mask.addEventListener("click", e => {mask.classList.add("hide");body.style.position = "static";body.style.top = "auto";document.scrollingElement.scrollTop = documentTop;})})();</script>
</html>

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://zhuanlan.zhihu.com/p/373328247
https://ant.design/components/modal-cn/
https://juejin.cn/post/6844903519636422664
https://segmentfault.com/a/1190000038594173
https://www.cnblogs.com/padding1015/p/10568070.html
https://blog.csdn.net/licanty/article/details/86590360
https://blog.csdn.net/xiaonuanli/article/details/81015131

蒙层禁止页面滚动的方案相关推荐

  1. 移动端弹出遮罩层禁止页面滚动,遮罩里面的框允许滚动如何实现。

    因为有的弹窗上面有滑动,但是滑动部分滑动时会带动底下的页面滚动,如何解决? 1.弹窗弹起时 activityAgreement:function(){vm.agreementsDailog=true; ...

  2. 蒙层禁止下方页面滚动防抖动完美方案

    文章目录 学习链接 前言 蒙层禁止页面滚动的方案 实现 body hidden touch preventDefault body fixed 解决js 禁止滚动条滚动,并且滚动条不消失,页面大小不闪 ...

  3. 禁止蒙层底部页面跟随滚动

    场景概述 弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互.但是,在蒙层元素中滑动的时候,滑到内容的尽头时,再继续滑动,蒙层底部的页面会开始滚动,显然这 ...

  4. 禁止蒙层底部页面跟随滚动 1

    场景概述 弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互.但是,在蒙层元素中滑动的时候,滑到内容的尽头时,再继续滑动,蒙层底部的页面会开始滚动,显然这 ...

  5. vue弹层时禁止页面滚动

    创建组件时禁止页面滚动: created(){//禁止页面滑动document.body.addEventListener('touchmove', this.bodyScroll, { passiv ...

  6. html右侧浮动栏随着滚动,jQuery实现div浮动层跟随页面滚动效果

    jQuery实现浮动层跟随页面滚动效果 #main{height:2000px} .demo{width:180px; height:250px; margin:10px; border:2px so ...

  7. 弹窗时候禁止页面滚动

    2019独角兽企业重金招聘Python工程师标准>>> 1.依靠css 将页面 document.documentElement.style.overflow='hidden';   ...

  8. # 解决微信小程序遮罩层底部页面滚动

    解决微信小程序遮罩层底部页面滚动 一.wxml文件添加catchtouchmove="move". <view class="" class=" ...

  9. html js控制页面蒙版,javascript实现蒙版与禁止页面滚动

    本文实例为大家分享了javascript实现蒙版与禁止页面滚动的具体代码,供大家参考,具体内容如下 项目需求:页面很长,要求加个蒙版,点击特定位置蒙版消失可以滑动页面,否则蒙版存在页面不可以滑动:要同 ...

最新文章

  1. 学以致用七---Centos7.2+python3.6.2+django2.1.1 --搭建一个网站(补充)
  2. Unity3D第三人称摄像机控制脚本
  3. 解决cvc-complex-type.2.4.a: Invalid content was found starting with element
  4. Python学习三——列表
  5. 模型验证的常用武器k-s
  6. ExoPlayer简单使用
  7. 排序算法之low B三人组
  8. [转] Windows Server 2012 Beta Cluster (Hyper-V 3.0)-iSCSI篇
  9. (011)XHTML文档之列表
  10. Leetcode 538.二叉树转换为累加树
  11. .Net 程序员应该知道的工具和网站
  12. httphandler java_java – 使用HTTPHandler上传文件
  13. MongoDB——客户端Robo 3T v1.4.3 安装
  14. C#UDP广域网,局域网通信-原理分析
  15. 陶大程招收博士计算机视觉,回顾优必选AI首席科学家陶大程博士获IEEE ICDM研究贡献奖的相关事件...
  16. 看我如何自制安全的远程控制工具
  17. 云服务器部署SpringBoot工程-瑞吉外卖项目
  18. 激活synopsys命令
  19. ctfshow 89-115 php特性 wp
  20. 信息学奥赛一本通1258:【例9.2】数字金字塔题解

热门文章

  1. onvif规范的实现:server端Discovery实现,通过OnvifTestTool12.06测试
  2. 什么是 gRPC ?
  3. Java-NIO实战多人聊天室
  4. cmd窗口pip显示不是内部或外部命令,也不是可运行的程序或批处理文件
  5. mysql中常用的时间工具
  6. Redis常问面试题及答案
  7. docker搭建mysql主从
  8. PHP扩展库PEAR被攻击,近半年下载者或被影响
  9. java循环 排序 查找
  10. Collections.sort的两个方法