移动端阻止弹窗下层页面被滑动方法介绍
在做H5开发时,很多场景下都需要弹窗
当出现弹窗时,大部分场景下是期望弹窗下层页面不能被滑动的
当然也不期望弹窗被滑动
近期肝页面又碰到了这个问题
下面介绍几种最常用的方式,以及一些边界情况与应对策略
overflow:hidden
流传最广的方式就是 给元素设置 overflow:hidden
给body
设置,就能达到阻止页面滑动的目的
document.body.style.overflow = 'hidden' document.body.style.overflow = 'visible'
大部分情况下这个是能有效果的
但在部分机器上,这个是不生效的
:
弹窗遮罩
还有一种情况如下,页面部分元素有局部滑动
<body style="overflow:hidden;"> <div style="overflow:scroll;height:100%;"> <!-- more element --> </div> </body>
当前情况给body
设置 overflow:hidden
依旧是无效果的
此时给弹窗加上遮罩如下,正常情况下,下层元素就不会收到touchmove
事件
<body style="overflow:hidden;"> <div style="overflow:scroll;height:100%;"> <!-- more element --> </div> <!-- dialog --> <div class="dialog"> <!-- 遮罩 --> <div class="mask" style="position:fixed;inset:0;"></div> <div class="content"></div> </div> </body>
其中inset属性是left,top,right,bottom
的简写
但在部分机型下,下层元素仍然会收到touchmove
事件,因此会跟着滑动
于是需要祭出下面的方法
prevent touchmove
阻止触摸滑动事件touchmove
的默认行为
const touchHandle = function(e) { e.preventDefault() } // 弹窗的事件 { onShow(){ document.body.addEventListener('touchmove', preventDefault, { passive: false, }); }, onHide(){ document.body.removeEventListener('touchmove', preventDefault); } }
在弹窗打开时直接阻止目标元素的滑动事件的默认行为
弹窗内容是不可滑动的话,那么这种方法是最省事高效的
如果弹窗中有可滑动的内容,且滑动的内容比较复杂
那么通过touchmove
去细力度的控制阻止滑动事件时就很麻烦
position:fixed
还有一种常用的就是position:fixed
在弹窗打开时,将目标元素进行固定,在关闭时恢复
由于定位会改变元素在页面上的位置,所以需要再fixed
前记录元素的位置
取消fixed
之后将元素又滚动到原来的位置
// 弹窗的事件 { onShow(){ document.body.style.top = `${ document.body.getClientRects()[0].top }px`; document.body.style.position = 'fixed'; document.body.style.left = '0'; document.body.style.right = '0'; }, onHide(){ document.body.style.position = 'visible'; window.scrollTo( 0, Math.abs(+document.body.style.top.replace('px', '')) ); } }
使用class代替style
这个也是碰巧发现的,在iOS
低端机将上述方式都尝试后
仍发现一个问题,现象如下(TODO:补图)
下层页面不会被滑动了,但遮罩和弹窗整体还能被下拉
弹窗是一个下拉列表弹窗,其出现的位置需要动态的计算,如下结构
<body style="overflow:hidden;"> <!-- dialog --> <div class="dialog" style="top:88px;"> <!-- 遮罩 --> <div class="mask" style="position:fixed;inset:0;"></div> <!-- 内容 --> <div class="content"></div> </div> </body>
最终发现是由于style
与class
设置的样式在这个机型上展示虽然一致
但实际交互起来的表现却不一致
修复后的html
结构如下,在元素里插入了一个style
标签,使用class
选择器与!important
重载这个距离的样式
<body style="overflow:hidden;"> <!-- dialog --> <div class="dialog" style="top:88px;"> <!-- 遮罩 --> <div class="mask" style="position:fixed;inset:0;"></div> <!-- 内容 --> <div class="content"></div> <style> .dialog{ top:88px !important; } </style> </div> </body>
代码如下
{ onShow(){ setTimeout(() => { const dialogEl = document.querySelector<HTMLElement>('.dialog') if (!dialogEl) { return } const style = document.createElement('style') style.textContent = ` .dialog{ top:${dialogEl.style.top} !important; } ` dialogEl.append(style) }, 200) } }
非常令人迷惑的一个操作,但就是解决了问题
小结
针对移动端弹窗下层页面可被滑动的异常场景
本文介绍了4种常见
解决方法,与1种"谜之操作"
demo演示
- PC扫码体验