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


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 相关文章推荐
pjblog修改技巧汇总
Mar 12 Javascript
使用jquery mobile做幻灯播放效果实现步骤
Jan 04 Javascript
JavaScript获取页面上被选中文字的方法技巧
Mar 13 Javascript
jQuery实现鼠标悬停背景翻转的黑色导航菜单代码
Sep 14 Javascript
JS+CSS实现经典的左侧竖向滑动菜单效果
Sep 23 Javascript
一起学写js Calender日历控件
Apr 14 Javascript
高效Web开发的10个jQuery代码片段
Jul 22 Javascript
详解使用Vue.Js结合Jquery Ajax加载数据的两种方式
Jan 10 Javascript
xmlplus组件设计系列之选项卡(Tabbar)(5)
May 03 Javascript
js实现可以点击收缩或张开的悬浮窗
Sep 18 Javascript
微信小程序实现登录注册tab切换效果
Dec 29 Javascript
JavaScript实现滚动加载更多
Dec 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加密扩展库Mcrypt安装和实例
2013/11/10 PHP
php简单备份与还原MySql的方法
2016/05/09 PHP
THINKPHP3.2使用soap连接webservice的解决方法
2017/12/13 PHP
PHP的mysqli_stat()函数讲解
2019/01/23 PHP
PHP中引用类型和值类型功能与用法示例
2019/02/26 PHP
PHP单例模式数据库连接类与页面静态化实现方法
2019/03/20 PHP
laravel-admin的多级联动方法
2019/09/30 PHP
JavaScript 验证浏览器是否支持javascript的方法小结
2009/05/17 Javascript
javascript学习笔记(二) js一些基本概念
2012/06/18 Javascript
js获取触发事件元素在整个网页中的绝对坐标(示例代码)
2013/12/13 Javascript
理解javascript回调函数
2014/12/28 Javascript
javascript中window.open在原来的窗口中打开新的窗口(不同名)
2015/11/15 Javascript
JavaScript判断浏览器及其版本信息
2017/01/20 Javascript
localStorage的黑科技-js和css缓存机制
2017/02/06 Javascript
JavaScript函数节流的两种写法
2017/04/07 Javascript
教你5分钟学会用requirejs(必看篇)
2017/07/25 Javascript
js module大战
2019/04/19 Javascript
解决vue项目中某一页面不想引用公共组件app.vue的问题
2020/08/14 Javascript
vue 项目@change多个参数传值多个事件的操作
2021/01/29 Vue.js
[02:07]DOTA2超级联赛专访BBC:难忘网吧超神经历
2013/06/09 DOTA
Mac OS X10.9安装的Python2.7升级Python3.3步骤详解
2013/12/04 Python
Python网络爬虫神器PyQuery的基本使用教程
2018/02/03 Python
Python动态导入模块的方法实例分析
2018/06/28 Python
python 实现对数据集的归一化的方法(0-1之间)
2018/07/17 Python
详解android与HTML混合开发总结
2018/06/06 HTML / CSS
Geekbuying波兰:购买中国电子产品
2019/10/20 全球购物
俄罗斯奢侈品牌衣服、鞋子和配饰的在线商店:INTERMODA
2020/07/17 全球购物
C语言面试题
2013/05/19 面试题
档案检查欢迎词
2014/01/13 职场文书
2014两会优秀的心得体会范文
2014/03/17 职场文书
商业项目策划方案
2014/06/05 职场文书
模具专业求职信
2014/06/26 职场文书
镇创先争优活动总结
2014/08/28 职场文书
2014县政府领导班子对照检查材料思想汇报
2014/09/25 职场文书
五年级学生期末评语
2014/12/26 职场文书
SQL Server2019数据库备份与还原脚本,数据库可批量备份
2021/11/20 SQL Server