禁止弹窗中蒙层底部页面跟随滚动的几种方法


Posted in Javascript onDecember 07, 2017

场景概述

众所周知,弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互。但是,在蒙层元素中滑动的时候,滑到内容的尽头时,再继续滑动,蒙层底部的页面会开始滚动,显然这不是我们想要的效果,因此需要阻止这种行为。

那么,如何阻止呢?请看以下分析:

方案分析

方案一

打开蒙层时,给body添加样式:

overflow: hidden;
height: 100%;

在某些机型下,你可能还需要给根节点添加样式:

overflow: hidden;

关闭蒙层时,移除以上样式。

优点:

简单方便,只需添加css样式,没有复杂的逻辑。

缺点:

兼容性不好,适用于pc,移动端就尴尬了。

部分安卓机型以及safari中,无法无法阻止底部页面滚动。

如果需要应用于移动端,那么你可能需要方案二。

方案二

就是利用移动端的touch事件

Touch对象代表一个触点,可以通过event.touches[0]获取,每个触点包含位置,大小,形状,压力大小,和目标 element属性。

{
screenX: 511, 
screenY: 400,//触点相对于屏幕左边沿的Y坐标
clientX: 244.37899780273438, 
clientY: 189.3820037841797,//相对于可视区域
pageX: 244.37, 
pageY: 189.37,//相对于HTML文档顶部,当页面有滚动的时候与clientX=Y 不等
force: 1,//压力大小,是从0.0(没有压力)到1.0(最大压力)的浮点数
identifier: 1036403715,//一次触摸动作的唯一标识符
radiusX: 37.565673828125, //能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径
radiusY: 37.565673828125,
rotationAngle: 0,//它是这样一个角度值:由radiusX 和 radiusY 描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面
target: {} // 此次触摸事件的目标element
}

利用移动端的touch事件来阻止默认行为(这里可以理解为页面滚动就是默认行为)。

// node为蒙层容器dom节点
node.addEventListener('touchstart', e => {
 e.preventDefault()
}, false)

简单粗暴,滚动时底部页面也无法动弹了。假如你的蒙层内容不会有滚动条,那么上述方法prefect。

但是,最怕空气突然安静,假如蒙层内容有滚动条的话,那么它再也无法动弹了。因此我们需要写一些js逻辑来判断要不要阻止默认行为,复杂程度明显增加。

具体思路:判定蒙层内容是否滚动到尽头,是则阻止默认行为,反之任它横行。

Tip:这里我发现了一个小技巧,可以省略不少代码。在一次滑动中,若蒙层内容可以滚动,则蒙层内容滚动,过程中即使蒙层内容已滚至尽头,只要不松手(可以理解为touchend事件触发前),继续滑动时页面内容不会滚动,此时若松手再继续滚动,则页面内容会滚动。利用这一个小技巧,我们可以精简优化我们的代码逻辑。

示例代码如下:

<body>
 <div class="page">
 <!-- 这里多添加一些,直至出现滚动条 -->
 <p>页面</p>
 <p>页面</p>
 <button class="btn">打开蒙层</button>
 <p>页面</p>
 </div>
 <div class="container">
 <div class="layer"></div>
 <div class="content">
  <!-- 这里多添加一些,直至出现滚动条 -->
  <p>蒙层</p>
  <p>蒙层</p>
  <p>蒙层</p>
 </div>
 </div>
</body>
body {
 margin: 0;
 padding: 20px;
}

.btn {
 border: none;
 outline: none;
 font-size: inherit;
 border-radius: 4px;
 padding: 1em;
 width: 100%;
 margin: 1em 0;
 color: #fff;
 background-color: #ff5777;
}

.container {
 position: fixed;
 top: 0;
 left: 0;
 bottom: 0;
 right: 0;
 z-index: 1001;
 display: none;
}

.layer {
 position: absolute;
 top: 0;
 left: 0;
 bottom: 0;
 right: 0;
 z-index: 1;
 background-color: rgba(0, 0, 0, .3);
}

.content {
 position: absolute;
 bottom: 0;
 left: 0;
 right: 0;
 height: 50%;
 z-index: 2;
 background-color: #f6f6f6;
 overflow-y: auto;
}
const btnNode = document.querySelector('.btn')
const containerNode = document.querySelector('.container')
const layerNode = document.querySelector('.layer')
const contentNode = document.querySelector('.content')
let startY = 0 // 记录开始滑动的坐标,用于判断滑动方向
let status = 0 // 0:未开始,1:已开始,2:滑动中

// 打开蒙层
btnNode.addEventListener('click', () => {
 containerNode.style.display = 'block'
}, false)

// 蒙层部分始终阻止默认行为
layerNode.addEventListener('touchstart', e => {
 e.preventDefault()
}, false)

// 核心部分
contentNode.addEventListener('touchstart', e => {
 status = 1
 startY = e.targetTouches[0].pageY
}, false)

contentNode.addEventListener('touchmove', e => {
 // 判定一次就够了
 if (status !== 1) return

 status = 2

 let t = e.target || e.srcElement
 let py = e.targetTouches[0].pageY
 let ch = t.clientHeight // 内容可视高度
 let sh = t.scrollHeight // 内容滚动高度
 let st = t.scrollTop // 当前滚动高度

 // 已经到头部尽头了还要向上滑动,阻止它
 if (st === 0 && startY < py) {
 e.preventDefault()
 }

 // 已经到低部尽头了还要向下滑动,阻止它
 if ((st === sh - ch) && startY > py) {
 e.preventDefault()
 }
}, false)

contentNode.addEventListener('touchend', e => {
 status = 0
}, false)

问题虽然是解决了,但是回头来看,复杂程度和代码量明显增加了一个梯度。

本着简单方便的原则,我们是不是还可以探索其他的方案呢?

既然touch事件判定比较复杂,何不跳出这个框框,另辟蹊径,探索更加合适的方案。

于是,便有了我们的方案三。

方案三

来讲讲我的思路,既然我们要阻止页面滚动,那么何不将其固定在视窗(即position: fixed),这样它就无法滚动了,当蒙层关闭时再释放。

position: fixed应该不用对大家过多介绍了吧,详细的介绍大家可以参考这篇文章:https://3water.com/article/83175.htm

当然还有一些细节要考虑,将页面固定视窗后,内容会回头最顶端,这里我们需要记录一下,同步top值。

示例代码:

let bodyEl = document.body
let top = 0

function stopBodyScroll (isFixed) {
 if (isFixed) {
 top = window.scrollY

 bodyEl.style.position = 'fixed'
 bodyEl.style.top = -top + 'px'
 } else {
 bodyEl.style.position = ''
 bodyEl.style.top = ''

 window.scrollTo(0, top) // 回到原先的top
 }
}

思考总结

  • 若应用场景是pc,推荐方案一,真的是不要太方便
  • 若应用场景是h5,你可以采用方案二,但是我建议你采用方案三
  • 若应用场景是全平台,那么方案三你不容错过

本文到这里也即将结束了,在这里我强烈推荐一下方案三,原因在于简单、方便、兼容性好,一次封装,永久受用。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
js函数使用技巧之 setTimeout(function(){},0)
Feb 09 Javascript
jQuery学习基础知识小结
Nov 25 Javascript
js同比例缩放图片的小例子
Oct 30 Javascript
jquery滚动加载数据的方法
Mar 09 Javascript
4种JavaScript实现简单tab选项卡切换的方法
Jan 06 Javascript
javascript css红色经典选项卡效果实现代码
May 17 Javascript
强大Vue.js组件浅析
Sep 12 Javascript
KnockoutJS 3.X API 第四章之数据控制流if绑定和ifnot绑定
Oct 10 Javascript
详解基于Vue2.0实现的移动端弹窗(Alert, Confirm, Toast)组件
Aug 02 Javascript
详解Vue、element-ui、axios实现省市区三级联动
May 07 Javascript
Angular 2使用路由自定义弹出组件toast操作示例
May 10 Javascript
编写更好的JavaScript条件式和匹配条件的技巧(小结)
Jun 27 Javascript
Vue通过URL传参如何控制全局console.log的开关详解
Dec 07 #Javascript
Vue中添加手机验证码组件功能操作方法
Dec 07 #Javascript
react学习笔记之state以及setState的使用
Dec 07 #Javascript
React Native 截屏组件的示例代码
Dec 06 #Javascript
判断jQuery是否加载完成,没完成继续判断的解决方法
Dec 06 #jQuery
mui back 返回刷新页面的实例
Dec 06 #Javascript
javascript实现Emrips反质数枚举的示例代码
Dec 06 #Javascript
You might like
php源码加密 仿微盾PHP加密专家(PHPCodeLock)
2010/05/06 PHP
PHP几个数学计算的内部函数学习整理
2011/08/06 PHP
ci检测是ajax还是页面post提交数据的方法
2014/11/10 PHP
WordPress中获取指定分类及其子分类下的文章数目
2015/12/31 PHP
ECshop 迁移到 PHP7版本时遇到的兼容性问题
2016/02/15 PHP
JQuery上传插件Uploadify使用详解及错误处理
2010/04/27 Javascript
jquery动态增加删除表格行的小例子
2013/11/14 Javascript
jquery中show()、hide()和toggle()用法实例
2015/01/15 Javascript
JavaScript实现倒计时代码段Item1(非常实用)
2015/11/03 Javascript
jQuery实现的多滑动门,多选项卡效果代码
2016/03/28 Javascript
微信小程序 视图层(xx.xml)和逻辑层(xx.js)详细介绍
2016/10/13 Javascript
使用JavaScript解决网页图片拉伸问题(推荐)
2016/11/25 Javascript
es6+angular1.X+webpack 实现按路由功能打包项目的示例
2017/08/16 Javascript
bootstrap-table组合表头的实现方法
2017/09/07 Javascript
浅谈JS 数字和字符串之间相互转化的纠纷
2017/10/20 Javascript
js 只比较时间大小的实例
2017/10/26 Javascript
vue-自定义组件传值的实例讲解
2018/09/18 Javascript
vue使用echarts图表的详细方法
2018/10/22 Javascript
关于ligerui子页面关闭后,父页面刷新,重新加载的方法
2019/09/27 Javascript
[00:17]DOTA2荣耀之路5:It’s a disastah!
2018/05/28 DOTA
python实现矩阵乘法的方法
2015/06/28 Python
python各种语言间时间的转化实现代码
2016/03/23 Python
pytorch构建网络模型的4种方法
2018/04/13 Python
Python Matplotlib 基于networkx画关系网络图
2019/07/10 Python
python聚类算法解决方案(rest接口/mpp数据库/json数据/下载图片及数据)
2019/08/28 Python
pyqt5 QScrollArea设置在自定义侧(任何位置)
2019/09/25 Python
python Xpath语法的使用
2020/11/26 Python
python二维图制作的实例代码
2020/12/03 Python
用python获取txt文件中关键字的数量
2020/12/24 Python
Boston Proper官网:美国女装品牌
2017/10/30 全球购物
自主招生自荐信范文
2013/12/04 职场文书
离婚财产处理协议书
2014/09/30 职场文书
集团财务总监岗位职责
2015/04/03 职场文书
餐厅收银员岗位职责
2015/04/07 职场文书
导游词之茶卡盐湖
2019/11/26 职场文书
利用正则表达式匹配浮点型数据
2022/05/30 Java/Android