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


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 相关文章推荐
jQuery 使用个人心得
Feb 26 Javascript
jQuery对象和DOM对象相互转化
Apr 24 Javascript
js中复制行和删除行的操作实例
Jun 25 Javascript
使用 js+正则表达式为关键词添加链接
Nov 11 Javascript
Bootstrap学习笔记之css样式设计(2)
Jun 07 Javascript
jquery仿京东侧边栏导航效果
Mar 02 Javascript
vue2.0构建单页应用最佳实战
Apr 01 Javascript
微信小程序wx.request实现后台数据交互功能分析
Nov 25 Javascript
微信小程序学习笔记之获取位置信息操作图文详解
Mar 29 Javascript
JS将时间秒转换成天小时分钟秒的字符串
Jul 10 Javascript
JS实现的进制转换,浮点数相加,数字判断操作示例
Nov 09 Javascript
vue项目或网页上实现文字转换成语音播放功能
Jun 09 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 curl模拟post提交数据示例
2013/12/31 PHP
Yii框架弹出窗口组件CJuiDialog用法分析
2017/01/07 PHP
Laravel框架Blade模板简介及模板继承用法分析
2019/12/03 PHP
JS 按钮点击触发(兼容IE、火狐)
2013/08/07 Javascript
浅谈JSON和JSONP区别及jQuery的ajax jsonp的使用
2014/11/23 Javascript
JS+CSS实现带有碰撞缓冲效果的竖向导航条代码
2015/09/15 Javascript
JavaScript中Window对象的属性及事件
2015/12/25 Javascript
jquery form表单获取内容以及绑定数据
2016/02/24 Javascript
jQuery插件扩展测试实例
2016/06/21 Javascript
浅谈JsonObject中的key-value数据解析排序问题
2017/12/06 Javascript
angular1配合gulp和bower的使用教程
2018/01/19 Javascript
利用angular自动编译andriod APK的绕坑经历分享
2019/03/08 Javascript
详解用vue2.x版本+adminLTE开源框架搭建后台应用模版
2019/03/15 Javascript
[03:49]2016完美“圣”典风云人物:AMS专访
2016/12/06 DOTA
python模拟登录百度代码分享(获取百度贴吧等级)
2013/12/27 Python
pygame加载中文名mp3文件出现error
2017/03/31 Python
Python实现对一个函数应用多个装饰器的方法示例
2018/02/09 Python
wtfPython—Python中一组有趣微妙的代码【收藏】
2018/08/31 Python
Python数据可视化教程之Matplotlib实现各种图表实例
2019/01/13 Python
Python Gitlab Api 使用方法
2019/08/28 Python
Python面向对象之继承原理与用法案例分析
2019/12/31 Python
python文件处理fileinput使用方法详解
2020/01/02 Python
关于Pytorch的MNIST数据集的预处理详解
2020/01/10 Python
python爬取本站电子书信息并入库的实现代码
2020/01/20 Python
python库skimage给灰度图像染色的方法示例
2020/04/27 Python
澳大利亚珍珠首饰购物网站:Vayo Pearls
2019/03/11 全球购物
查询优化的一般准则有哪些
2015/03/08 面试题
秘书英文求职信范文
2014/01/31 职场文书
市场营销方案范文
2014/03/11 职场文书
中职生求职信
2014/07/01 职场文书
目标责任书格式
2014/07/28 职场文书
质量保证书格式
2015/02/27 职场文书
学生犯错保证书
2015/05/09 职场文书
高中语文教学反思范文
2016/02/16 职场文书
Python打包exe时各种异常处理方案总结
2021/05/18 Python
Android自定义双向滑动控件
2022/04/19 Java/Android