自用的滚屏穿透解决方案

288 阅读1分钟

问题描述

  • H5 全屏遮盖弹窗挂载,弹窗里内容滚动时,遮盖下视图也发生过滚动,滚动事件发生容器穿透。

解决方案

一个理想的滚屏穿透解决方案需要具备的能力:

  1. 解决弹窗内滚屏穿透事件
  2. 弹窗内滚屏出现前后用户视区不发生偏移
  3. 支持多层弹窗

方案实现步骤:

  1. 检查是否已经处理过禁止弹窗内滚屏穿透事件
    • 是,直接返回
    • 否,继续执行
  2. 记录弹窗出现前视图滚动偏离量 originScrollTop
  3. 备份弹窗出现前视图行内样式 style 属性 originBodyStyle
  4. 设置弹窗出现前视图行内样式 style 属性为 特定偏移量下的禁止滚屏样式
let noScrollBodyStyle = `overflow: hidden; height: 100%; position: fixed; top: -${ originScrollTop }px; right:0; left: 0;`
  1. 设置弹窗卸载后的回调函数,用来还原 步骤3 的备份。
  2. 完成禁止弹窗内滚屏穿透事件。

具体实现

仅适用于 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);
            }
        }
    }
};

备注:

  1. 不要在非全屏 DIV 内滚动 里面使用全屏弹窗,这会带来苹果设备的另一个BUG,全屏元素被非全屏内滚动元素截断。
  2. 以上方法适用于 document.body 垂直滚动的移动端页面。