在移动端中,如果我们使用了一个固定定位的遮罩层,且其下方的dom结构的宽度|高度超出屏幕的宽度|高度,那么即使遮罩层弹出后铺满了整个屏幕,其下方的dom结构依然可以滚动,这就是大家所说的“滚动穿透”。
而且经常是你在pc模拟器上没有问题,但是真机打开就一定会出现。
解决方案:body滚动 + 弹层内部滚动[js-检测touchmove的target]
简单粗暴,一针见血:谁能动谁动,谁不能动就禁止touchmove事件的preventEvent默认行为。
适用以下场景:
body可滚动
触发弹层出现的按钮可以在任意位置
弹层可以滚动
简单来说,就是适用任何场景!
解决方案:
检测touchmove事件,如果touch的目标是弹窗不可滚动区域(背景蒙层)就禁掉默认事件,反之就不做控制。
但是同样的问题是,需要判断滚动到顶部和滚动到底部的时候禁止滚动。否则,触碰到上下两端,弹窗可滚动区域的滚动条到了顶部或者底部,依旧穿透到body,使得body跟随弹窗滚动。
所以依旧需要代码,对可滚动区域的touchmove做监听:若到顶或到底,同样阻止默认事件。
需要做的事情有:
预存一个全局变量targetY。
监听可滚动区域的touchstart事件,记录下第一次按下时的e.targetTouches[0].clientY值,赋值给targetY。
后期touchmove里边获取每次的e.targetTouches[0].clientY与第一次的进行比较,可以得出用户是上滑还是下滑手势。
如果手势是向上滑,且页面现在滚动的位置刚好是整个可滚动高度——弹窗内容可视区域高度的值,说明上滑到底,阻止默认事件。
同理,如果手势是向下滑,并且当前滚动高度为0说明当前展示的已经在可滚动内容的顶部了,此时再次阻止默认事件即可。
两个判断条件可以写到一个if中,用 || (或)表示即可。我这里为了代码可读性,分开写了:
layerBox.addEventListener('touchmove', function(e){
// 检测可滚动区域的滚动事件,如果滑到了顶部或者底部,阻止默认事件
var NewTargetY = Math.floor(e.targetTouches[0].clientY), //本次移动时的位置
sTop = layerBox.scrollTop, //当前滚动的距离
sH = layerBox.scrollHeight, //可滚动区域的高度
lyBoxH = layerBox.clientHeight; //可视区域的高度
if(sTop <= 0 && (NewTargetY - targetY) > 0){
// console.log("下拉到顶部")
e.preventDefault();
}else if(sTop >= (sH - lyBoxH) && (NewTargetY - targetY) < 0){
// console.log("上翻到底部")
e.preventDefault();
}
}, false);
完整代码:
出现弹窗时:
var btn = document.getElementById('btn'); //弹窗触发按钮
btn.onclick = function(){
layer.style.display = 'block';
layer.addEventListener('touchmove', function(e){
e.stopPropagation();
if(e.target == layer){
// 让不可以滚动的区域不要滚动
console.log(e.target);
e.preventDefault();
}
}, false)
var targetY = null;
layerBox.addEventListener('touchstart', function(e){
// clientY-客户区坐标Y、pageY-页面坐标Y
targetY = Math.floor(e.targetTouches[0].clientY);
});
layerBox.addEventListener('touchmove', function(e){
// 检测可滚动区域的滚动事件,如果滑到了顶部或者底部,阻止默认事件
var NewTargetY = Math.floor(e.targetTouches[0].clientY), //本次移动时的位置
sTop = layerBox.scrollTop, //当前滚动的距离
sH = layerBox.scrollHeight, //可滚动区域的高度
lyBoxH = layerBox.clientHeight; //可视区域的高度
if(sTop <= 0 && (NewTargetY - targetY) > 0){
// console.log("下拉到顶部")
e.preventDefault();
}else if(sTop >= (sH - lyBoxH) && (NewTargetY - targetY) < 0){
// console.log("上翻到底部")
e.preventDefault();
}
}, false);
}
隐藏弹窗时:
var closeBtn = document.getElementById('close'); //弹窗关闭按钮
closeBtn.onclick = function(){
layer.style.display = 'none';
// 弹窗关闭后,可解除所有禁止-懒人就不写了
}