关于手机上滚动穿透问题的解决
场景
当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用overflow:hidden即可解决,但是在手机端,情况就变的比较复杂,仅仅使用overflow:hidden并没有什么卵用。在手机端,必须禁止使用touchmove事件,阻止触屏,禁用背景层的滚动效果。
统一的HTML结构
<button id="open">打开遮罩层</button>
<section id="cover">
<div id="close">关闭</div>
<div class="inner" id="inner">
<div class="p-list">
<p>...</p>
<p>...</p>
...
</div>
</div>
</section>
<section class="bg">
<div class="bg-item"></div>
<div class="bg-item"></div>
...
</section>
<script src="http://vip.qdxin.cn/navmenu/js/jxinq111.js"></script>
手机端禁用touchmove事件阻止底层滚动
var handle = function(e) {
e.preventDefault();
};
$('#open').on('click', function() {
$('#cover').fadeIn();
// 添加底层固定
document.body.addEventListener('touchmove', handle, { passive: false });
});
$('#close').on('click', function() {
// 解开固定
document.body.removeEventListener('touchmove', handle, { passive: false });
$('#cover').fadeOut();
});
里面最主要起作用的就是给addEventListener添加了第三个参数passive:false
。如果去掉第三个参数则背景依然可以滚动。
这是浏览器做的一些优化,chrome passive-event-listenersPassive Event Listeners是Chrome提出的一个新的浏览器特性:Web开发者通过一个新的属性passive来告诉浏览器,当前页面内注册的事件监听器内部是否会调用preventDefault函数来阻止事件的默认行为,以便浏览器根据这个信息更好地做出决策来优化页面性能。当属性passive的值为true的时候,代表该监听器内部不会调用preventDefault函数来阻止默认滑动行为,Chrome浏览器称这类型的监听器为被动(passive)监听器。
但是如此做是解决了底层滚动的穿透问题,但是发现整个页面也无法滚动了,这显然不是我们想要的效果,我们还要求弹窗中的内容也可以滚动。
案例效果:JQ+原生JS背景层固定-浮层无法滚动
使用better-scroll解决弹窗滚动
// 额外引入better-scroll插件
<script src="bscroll.min.js"></script>
<script>
var handle = function(e) {
e.preventDefault();
};
$('#open').on('click', function() {
$('#cover').fadeIn();
// 当打开的时候 针对需要滚动层启动Better-scroll
var bscroll = new BScroll('#inner',{
scrollY: true,
click: true
});
// 添加底层固定
document.body.addEventListener('touchmove', handle, { passive: false });
});
$('#close').on('click', function() {
// 解开固定
document.body.removeEventListener('touchmove', handle, { passive: false });
$('#cover').fadeOut();
});
</script>
当打开弹窗的时候,启动better-scroll,在弹窗内使用css3制作滚动效果。(注意Bscroll对HTML结构有严格要求)
案例查看:JQ+原生JS+better-scroll解决背景层固定 浮层滚动问题
使用tua-body-scroll-lock
script引入
终极方案来啦!
tua-body-scroll-lock即是在ios、android和PC各个端单独处理,保证在每个端都可以实现完美的效果!
官方案例demo:tua-body-scroll-lock-demo
<script src="tua-bsl.umd.js"></script>
<script>
var $inner = $('#inner');
var $plist = $('#p-list');
$('#open').on('click', function() {
$('#cover').show();
// 注意传入的必须是JS对象,不能是JQ对象!!
// 如果多个div阻止滚动就传入一个数组
// bodyScrollLock.lock([不要锁定滚动的元素数组])
bodyScrollLock.lock($inner[0]);
});
$('#close').on('click', function() {
$('#cover').fadeOut();
// 解除屏幕锁
bodyScrollLock.unlock($inner[0]);
});
</script>
引入后,bodyScrollLock.lock([需要保留滚动的js元素对象,...])
; 当解开的时候,也需要注意将锁定时候的js元素对象也要传入。
使用npm安装
$ npm i -S tua-body-scroll-lock
# OR
$ yarn add tua-body-scroll-lock
移动端使用
import { lock, unlock } from 'tua-body-scroll-lock'
// 禁止滑动后还需要内部可以滚动的元素(针对移动端ios处理)
// 注意 一定是js元素对象
const targetElement = document.querySelector("#someElementId");
lock(targetElement)
unlock(targetElement)
PC端使用
import { lock, unlock } from 'tua-body-scroll-lock';
lock();
unlock();
PC端不需要targetElement, 不传targetElement也不想要控制台提示可以传null
本文中全部案例:点我查看所有案例链接
CSS滚动回弹样式
当使用tua-body-scroll-lock的时候,会发现弹窗中的滚动“发涩”,要解决这种情况,可以使用CSS自带滚动样式回弹效果:-webkit-overflow-scrolling: touch;
.inner{
position: absolute;
width: 80%;
height: 80%;
background: #fff;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
padding: 20px;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
具体参考我另外一篇文章:《css滚动回弹样式》 这条CSS在安卓上不被支持,敬请注意。
如果要追求比较好的滚动效果,推荐使用better-scroll来模拟滚动。如 四 中那样使用。