移动端当有fixed蒙层遮罩时,在屏幕上滑动能够滑动背景下的内容,这就是滚动穿透问题。
1、问题还原
<!--html结构-->
<body>
<div>内容部分</div>
<div>弹出框</div>
</body>
css就省略了,内容部分是超过一屏的长度的,弹出框采用绝对定位展示在可视区域的固定位置,遮罩是加在body元素上的。滑动页面会出现滑动穿透的问题,遮罩下的内容会跟着滑动。
2、问题分析
出现滑动穿透的问题是滚动条在body元素上,而滑动的时候body自然是可以响应的。那么将结构改变一下:
<!--html结构-->
<body>
<div class="content">
<div>内容部分</div>
<div>弹出框</div>
</div>
</body>
为了不让滚动条加在body元素上,还需要做如下处理:
body {
height: 100%;
}
.content {
height: 100%;
overflow: auto;
}
. overflow-hidden {
overflow: hidden
}
在弹出框出现时需要把overflow-hidden
类加在根div元素上。弹出消失时移除。
3、总结
这里po一个搜到的常规解决穿透的方案:
解决方案原理:禁用body的滚动条,由于滚动条的位置会丢失,所以需要在展示弹窗之前保存滚动条的位置,隐藏弹窗时恢复滚动条的位置。
首先定义class来禁用滚动条
.modal-open {
position:fixed;
height: 100%;
}
然后完成保存并恢复滚动条的工作:
/**
* ModalHelper helpers resolve the modal scrolling issue on mobile devices
* https://github.com/twbs/bootstrap/issues/15852
* requires document.scrollingElement polyfill https://github.com/yangg/scrolling-element
*/
var ModalHelper = (function(bodyCls) {
var scrollTop;
return {
afterOpen: function() {
scrollTop = document.scrollingElement.scrollTop;
document.body.classList.add(bodyCls);
document.body.style.top = -scrollTop + 'px';
},
beforeClose: function() {
document.body.classList.remove(bodyCls);
// scrollTop lost after set position:fixed, restore it back.
document.scrollingElement.scrollTop = scrollTop;
}
};
})('modal-open');
然后在弹出层显示时调用ModalHelper.afterOpen()
,在隐藏弹出层时调用ModalHelper.beforeClose()
。
不过这种方式较麻烦了,能够采用简单的方式解决还是最好的。
4、两个兼容问题
1.在IOS上惯性滑动失效
.content {
-webkit-overflow-scrolling: touch; /* 解决在IOS上滚动惯性失效的问题 */
}
2.在IOS上非交互性元素的点击事件失效
非交互性元素:没有显式绑定点击等事件处理函数的元素。
简单的解决方案是给元素添加cursor: pointer
使之成为交互元素。
但是这又会带来另一个问题:所有设置cursor: pointer
的元素在webkit内核的浏览器中点击时会有蓝色背景,可以如下设置来取消蓝色背景:
*{
-webkit-tap-highlight-color: transparent; /* 解决设置cursor:pointer的元素点击出现蓝色背景的问题 */
}
后话
在ios中有一个默认的效果是双击title回到顶部,如果滚动条不是在body元素上,那么这个效果将失效。