问题描述
- H5 全屏遮盖弹窗挂载,弹窗里内容滚动时,遮盖下视图也发生过滚动,滚动事件发生容器穿透。
解决方案
一个理想的滚屏穿透解决方案需要具备的能力:
- 解决弹窗内滚屏穿透事件
- 弹窗内滚屏出现前后用户视区不发生偏移
- 支持多层弹窗
方案实现步骤:
- 检查是否已经处理过禁止弹窗内滚屏穿透事件
- 是,直接返回
- 否,继续执行
- 记录弹窗出现前视图滚动偏离量
originScrollTop; - 备份弹窗出现前视图行内样式 style 属性
originBodyStyle; - 设置弹窗出现前视图行内样式 style 属性为
特定偏移量下的禁止滚屏样式
let noScrollBodyStyle = `overflow: hidden; height: 100%; position: fixed; top: -${ originScrollTop }px; right:0; left: 0;`
- 设置弹窗卸载后的回调函数,用来还原
步骤3的备份。 - 完成禁止弹窗内滚屏穿透事件。
具体实现
仅适用于 document.body 垂直滚动的移动端页面
export default {
props: {
visible: {
type: Boolean,
default: false // 是否挂载,默认值 false
}
},
data() {
return {
isReset: false, // 是否还原
bodyOrignTop: 0, // 原偏移量
bodyOrignStyle: "" // 原行内样式
};
},
watch: {
visible: {
handler: "fixScroll", // 执行函数
immediate: true // 立即执行,默认 visible === false
}
},
methods: {
fixScroll(visible) {
// 是否已处理过?
const isFixScroll = !!document.body.dataset.isFixScroll;
// 挂载 + 已处理过,不再处理
if (visible && isFixScroll) return;
// 卸载 + 已处理过 + 不是自己处理的,不再处理
if (!visible && isFixScroll && !this.isReset) return;
// 挂载 + 未处理过,开始处理
if (visible && !isFixScroll) {
this.bodyOrignStyle = document.body.getAttribute("style") || "";
this.bodyOrignTop = document.documentElement.bodyOrignTop || window.pageYOffset || document.body.bodyOrignTop || 0;
document.body.setAttribute( "style", `overflow: hidden; height: 100%; position: fixed; top: -${this.bodyOrignTop}px; right:0; left: 0;` );
document.body.dataset.isFixScroll = "done";
this.isReset = true;
}
// 卸载 + 已处理过 + 是自己处理的,开始还原
if (!visible && isFixScroll && this.isReset) {
delete document.body.dataset.fixScroll;
document.body.setAttribute("style", this.bodyOrignStyle);
window.scrollTo(0, this.bodyOrignTop);
}
}
}
};
备注:
- 不要在非全屏 DIV 内滚动 里面使用全屏弹窗,这会带来苹果设备的另一个BUG,全屏元素被非全屏内滚动元素截断。
- 以上方法适用于 document.body 垂直滚动的移动端页面。