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


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 相关文章推荐
Track Image Loading效果代码分析
Aug 13 Javascript
javascript中数组中求最大值示例代码
Dec 18 Javascript
js函数定时器实现定时读取系统实时连接数
Apr 30 Javascript
javascript里使用php代码实例
Dec 13 Javascript
JSON相关知识汇总
Jul 03 Javascript
js实现跨域的4种实用方法原理分析
Oct 29 Javascript
JS基于构造函数实现的菜单滑动显隐效果【测试可用】
Jun 21 Javascript
移动端使用localStorage缓存Js和css文的方法(web开发)
Sep 20 Javascript
基于vue实现可搜索下拉框定制组件
Mar 26 Javascript
详解puppeteer使用代理
Dec 27 Javascript
微信小程序后端实现授权登录
Feb 24 Javascript
Vue实现导航栏菜单
Aug 19 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 开发工具
2006/12/06 PHP
浅析php中如何在有限的内存中读取大文件
2013/07/02 PHP
PHP中strlen()和mb_strlen()的区别浅析
2014/06/19 PHP
Thinkphp搜索时首页分页和搜索页保持条件分页的方法
2014/12/05 PHP
laravel withCount 统计关联数量的方法
2019/10/10 PHP
firefox插件Firebug的使用教程
2010/01/02 Javascript
jQuery下扩展插件和拓展函数的写法(匿名函数使用的典型例子)
2010/10/20 Javascript
js/jquery获取浏览器窗口可视区域高度和宽度以及滚动条高度实现代码
2012/12/17 Javascript
JS跨域交互(jQuery+php)之jsonp使用心得
2016/07/01 Javascript
JavaScript中ES6 Babel正确安装过程
2016/07/18 Javascript
ionic2 tabs使用 Modal底部tab弹出框
2016/12/30 Javascript
手机端js和html5刮刮卡效果
2020/09/29 Javascript
Node.js常用工具之util模块
2017/03/09 Javascript
JavaScript常见JSON操作实例分析
2018/08/08 Javascript
layer关闭弹出窗口触发表单提交问题的处理方法
2019/09/25 Javascript
详解小程序如何改变onLoad的执行时机
2019/11/01 Javascript
Angular+ionic实现折叠展开效果的示例代码
2020/07/29 Javascript
编写v-for循环的技巧汇总
2020/12/01 Javascript
[42:35]2018DOTA2亚洲邀请赛3月30日 小组赛A组 VG VS OpTic
2018/03/31 DOTA
Python中文编码那些事
2014/06/25 Python
Python中文字符串截取问题
2015/06/15 Python
5种Python单例模式的实现方式
2016/01/14 Python
浅谈keras中的Merge层(实现层的相加、相减、相乘实例)
2020/05/23 Python
python3中确保枚举值代码分析
2020/12/02 Python
纯CSS打造(无图像无js)的非常流行的讲话(语音)气泡效果
2012/12/28 HTML / CSS
俄罗斯电动工具和设备购物网站:Vseinstrumenti.ru
2020/11/12 全球购物
EJB需直接实现它的业务接口或Home接口吗,请简述理由
2016/11/23 面试题
业务经理岗位职责
2013/11/11 职场文书
商务主管岗位职责
2013/12/08 职场文书
2014年高中班主任工作总结
2014/11/08 职场文书
美丽心灵观后感
2015/06/01 职场文书
python自动化调用百度api解决验证码
2021/04/13 Python
Python 的 sum() Pythonic 的求和方法详细
2021/10/16 Python
基于angular实现树形二级表格
2021/10/16 Javascript
php访问对象中的成员的实例方法
2021/11/17 PHP
Vue自定义铃声提示音组件的实现
2022/01/22 Vue.js