
前言
在开发弹窗组件时,常遇到遮罩层打开后背景页面仍可滚动的'滚动穿透'问题。特别是在移动端,惯性滚动和浏览器内核差异会导致体验不佳。本文深入分析该问题的原理,并提供三种主流解决方案及调试技巧。
问题原理
滚动穿透(Scroll Penetration)是指当固定定位的遮罩层存在时,滑动事件未被拦截而传递到 body 层,导致背景页面跟随移动。iOS 的 WebKit 内核和安卓的 Blink 内核对此行为处理不同。此外,iOS 的'橡皮筋效果'(Rubber Band Effect)可能导致 overflow: hidden 在某些情况下失效。
方案一:修改 Body 样式
这是最通用的方法。思路是弹窗打开时将 body 的 overflow 设为 hidden,并记录当前 scrollTop;关闭时还原。
// 定义状态变量
let scrollLocker = {
scrollTop: 0,
originalStyle: '',
isLocked: false
};
/**
* 锁定背景滚动 - 暴力版
*/
function lockBodyScroll() {
if (scrollLocker.isLocked) return;
// 记录当前滚动位置
scrollLocker.scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const body = document.body;
scrollLocker.originalStyle = body.getAttribute('style') || '';
// 设置 fixed 定位防止页面跳动
body.style.cssText = `
position: fixed;
top: -px;
left: 0;
right: 0;
overflow: hidden;
width: 100%;
height: 100%;
`;
scrollLocker. = ;
}
() {
(!scrollLocker.) ;
body = .;
body.. = scrollLocker.;
.({
: scrollLocker.,
:
});
scrollLocker. = ;
}
{
() {
. = ;
. = ;
}
() {
(.) ;
. = modalElement;
modalElement..();
();
. = ;
. = {
(e. === ) .();
};
.(, .);
}
() {
(!.) ;
();
...();
. = ;
.(, .);
}
}


