关于弹出层滑动问题

关于弹出层滑动问题

// 2019-10-23 15:11:06

关于弹出层滑动一直以来都是一个令人难受的问题。我们一直在探索解决的方法与途径,虽然没有真正意义上的成功,也从未停止探索的步伐,直到写这篇文章的时候,我们仍未找到最完美的方法。这里记录一些探索的经验,以便后人参考。

问题阐述

先来看这样一个GIF

在这里插入图片描述

虽然这个例子已近是最后面对的困难,但他能更直观得提现出问题所在。

下面的弹出框是一个position:fixed;的一个div,他与他的父元素一样都属于内容比较多,需要overflow:auto的元素。于是在为做任何处理的情况下,就有这种,滑动穿透的问题出现。

这种问题还出现在iframe中,弹出的iframe滑动到底部的时候,仍然执行向下滑动的操作,滑动穿透会继续将其父元素向下滑动,造成很糟糕的用户体验。

方案一:通过传值解决问题

这种方法常见于iframe中,因为新弹出的页面,往往把父页面整个遮挡,用户只看到子页面的内容,而不知父页面到底发生了什么。

基于用户看不到父页面的特性,我们一般采用——”滚动条获取并重新赋值“的方法

  • 第一步,在子页面弹出的时候将父页面的滚动条高度保存到子页面
  • 第二步,当子页面关闭时,将父页面原本的滚动条高度,重新赋予它

通过以上两步,基本能将问题解决。

关于取值方法

关于取值方法,其实有很多种,这里只做一些简单的介绍:

  1. 直接通过变量的传输。

    a.在如果是弹出普通的div(在同一个页面),这样子就可以通过全局申明一个变量解决问题。

    b.如果在iframe

    ​ 父页面调用子元素方法(直接从子元素方法中获取数值)

    document.getElementById('').contentWindow.

    ​ 子页面获取父元素的方法

    window.parent.父元素的方法();

  2. 将数据存在浏览器本地

    ​ 如果从我现在用的谷歌浏览器来讲的话,一共有8种储存数据的方法。但必须要提的是,请注意兼容性的问题,这里有相当一部分是谷歌浏览器所独有的储存方式,不兼容其他内核的浏览器,不要为了此刻图方便而采用他们。

这样做的局限性

这里只讨论了父页面被全面覆盖的情况,其实还存在上面第一张GIF所至展示的,父页面被覆盖了部分,这样采用方案一就容易被发现。

方案二:先不管他

在实际使用过程中,虽然他令人难受,但在绝大多数情况下并不致命。

所以,在限制条件过多的情况下,我们可以考虑暂时先放弃这块硬骨头。

方案三:阻止默认事件

核心

方案三的思路是从事件的角度出发,解决问题的。

https://www.zhangxinxu.com/wordpress/2016/12/web-mobile-scroll-prevent-window-js-css/

先通过给整个body添加一个禁止滚动属性,再通过判断,选择禁止默认属性的元素,从而达到一部分元素可以滚动,一部分元素不能滚动的效果。

核心代码分为两块css 和js

.noscroll,
.noscroll body {
    overflow: hidden;
}
.noscroll body {
    position: relative;
}

先注意一下上面这两个类,他是给HTML加的,子页面出来的时候,将类名加给 HTML。再当子页面消失的时候,将类删除。

再来看一下,下面这个方法他的使用方法是$.smartScroll.(子页面整个元素,‘滑动部件的类名’)。

请注意,第一个是元素【最好用id选择器,因为只有一个】,第二个是类名(一个字符串)

举个例子: . s m a r t S c r o l l ( .smartScroll( .smartScroll((’#after-page’), ‘.content’);

$.smartScroll = function(container, selectorScrollable) {
    // 如果没有滚动容器选择器,或者已经绑定了滚动时间,忽略
    if (!selectorScrollable || container.data('isBindScroll')) {
        return;
    }

    // 是否是搓浏览器
    // 自己在这里添加判断和筛选
    var isSBBrowser;

    var data = {
        posY: 0,
        maxscroll: 0
    };

    // 事件处理
    container.on({
        touchstart: function (event) {
            var events = event.touches[0] || event;

            // 先求得是不是滚动元素或者滚动元素的子元素
            var elTarget = $(event.target);

            if (!elTarget.length) {
                return;
            }

            var elScroll;

            // 获取标记的滚动元素,自身或子元素皆可
            if (elTarget.is(selectorScrollable)) {
                elScroll = elTarget;
            } else if ((elScroll = elTarget.parents(selectorScrollable)).length == 0) {
                elScroll = null;
            }

            if (!elScroll) {
                return;
            }

            // 当前滚动元素标记
            data.elScroll = elScroll;

            // 垂直位置标记
            data.posY = events.pageY;
            data.scrollY = elScroll.scrollTop();
            // 是否可以滚动
            data.maxscroll = elScroll[0].scrollHeight - elScroll[0].clientHeight;
        },
        touchmove: function (event) {
            // 如果不足于滚动,则禁止触发整个窗体元素的滚动
            if (data.maxscroll <= 0 || isSBBrowser) {
                // 禁止滚动
                event.preventDefault();
            }
            // 滚动元素
            var elScroll = data.elScroll;
            // 当前的滚动高度
            var scrollTop = elScroll.scrollTop();

            // 现在移动的垂直位置,用来判断是往上移动还是往下
            var events = event.touches[0] || event;
            // 移动距离
            var distanceY = events.pageY - data.posY;

            if (isSBBrowser) {
                elScroll.scrollTop(data.scrollY - distanceY);
                elScroll.trigger('scroll');
                return;
            }

            // 上下边缘检测
            if (distanceY > 0 && scrollTop == 0) {
                // 往上滑,并且到头
                // 禁止滚动的默认行为
                event.preventDefault();
                return;
            }

            // 下边缘检测
            if (distanceY < 0 && (scrollTop + 1 >= data.maxscroll)) {
                // 往下滑,并且到头
                // 禁止滚动的默认行为
                event.preventDefault();
                return;
            }
        },
        touchend: function () {
            data.maxscroll = 0;
        }
    });

    // 防止多次重复绑定
    container.data('isBindScroll', true);
};

局限性

虽然这已经是目前为止找到的最能满足要求的方法,但是他仍然存在令人不满意的问题。而且是让人很难受的那种。

其在ios(iPhone6s ios13)上表现完美,没有值得挑刺的地方,但在安卓上则有些小问题。

虽然他并不像开头那个图片那样明显,也不会有大的位置改变,但这仍然是是一个值得去解决的问题。

在这里插入图片描述

值得注意的问题

方案三还是有一个问题是是浏览器报错。

[Intervention] Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.

[干预]忽略了取消cancelable=false的touchMove事件的尝试,例如,因为滚动正在进行且无法中断。

我不知道这个错是什么引起的,也不知道他是否必须去解决,但是我知道他挺麻烦的

在这里插入图片描述

最后给出一个完整的例子

这里用的是方案三

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <meta charset="UTF-8">
    <title>用来尝试到底行不行</title>
    <style>
        /**{touch-action: none;}*/
        .noscroll,
        .noscroll body {
            overflow: hidden;
        }

        .noscroll body {
            position: relative;
        }
        .main{
            text-align: center;
        }
        .after-page{
            position: fixed;
            bottom: 0px;
            width: 100%;
            height: 400px;
            background-color: aqua;
            text-align: center;
        }
        .content{
            position: absolute;
            width: 100%;
            height: 400px;
        }
    </style>
</head>
<body>
    <div class="main">
        <p>1前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>2前页的内容</p>
        <input class="page-btn" type="button" value="getNext">
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>3前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>3前页的内容</p>
        <p>前页的内容</p>
        <input class="page-btn" type="button" value="getNext">
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>4前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <p>前页的内容</p>
        <input class="page-btn" type="button" value="getNext">
        <p>前页的内容</p>
    </div>
    <div class="after-page" id="after-page" style="display: none; overflow: hidden">
        <h1>我是大标题</h1>
        <input type="button" class="close-btn" value="close">
        <div class="content" style="overflow: auto;  -webkit-overflow-scrolling: touch;">
           <ul>
               <li>1后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>2后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>3后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>4后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>5后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>6后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
               <li>后页的内容</li>
           </ul>
        </div>
    </div>
</body>

<script type="text/javascript" src="jquery-3.4.1.min.js"></script>
<!--<script src="//qidian.gtimg.com/yuewen/v1/js/zepto.min.js"></script>-->
<script>
    $('.page-btn').off().on('click',function () {
        $('.after-page').show();
        $('html').addClass('noscroll');
        $.smartScroll($('#after-page'), '.content');
    });

    $('.close-btn').off().on('click',function () {
        $('.after-page').hide();
        $('html').removeClass('noscroll');
    })

    // 构造函数
    $.smartScroll = function (container, selectorScrollable) {
        // 如果没有滚动容器选择器,或者已经绑定了滚动时间,忽略
        console.log(selectorScrollable);
        if (!selectorScrollable || container.data('isBindScroll')) {
            return;
        }

        // 是否是搓浏览器
        // 自己在这里添加判断和筛选
        var isSBBrowser;

        var data = {
            posY: 0,
            maxscroll: 0
        };

        // 事件处理
        container.on({
            touchstart: function (event) {
                // var events = event.touches[0] || event;
                var events = event.originalEvent.targetTouches[0] || event;

                // 先求得是不是滚动元素或者滚动元素的子元素
                var elTarget = $(event.target);

                if (!elTarget.length) {
                    return;
                }

                var elScroll;

                // 获取标记的滚动元素,自身或子元素皆可
                if (elTarget.is(selectorScrollable)) {
                    elScroll = elTarget;
                } else if ((elScroll = elTarget.parents(selectorScrollable)).length === 0) {
                    elScroll = null;
                }

                if (!elScroll) {
                    return;
                }

                // 当前滚动元素标记
                data.elScroll = elScroll;

                // 垂直位置标记
                data.posY = events.pageY;
                data.scrollY = elScroll.scrollTop();
                // 是否可以滚动
                data.maxscroll = elScroll[0].scrollHeight - elScroll[0].clientHeight;
            },
            touchmove: function (event) {
                // 如果不足于滚动,则禁止触发整个窗体元素的滚动
                if (data.maxscroll <= 0 || isSBBrowser) {
                    // 禁止滚动
                    event.preventDefault();
                }
                // 滚动元素
                var elScroll = data.elScroll;
                // 当前的滚动高度
                var scrollTop = elScroll.scrollTop();

                // 现在移动的垂直位置,用来判断是往上移动还是往下
                var events = event.touches[0] || event;
                // var events = event.originalEvent.targetTouches[0] || event;

                // 移动距离
                var distanceY = events.pageY - data.posY;

                if (isSBBrowser) {
                    elScroll.scrollTop(data.scrollY - distanceY);
                    elScroll.trigger('scroll');
                    return;
                }

                // 上下边缘检测
                if (distanceY > 0 && scrollTop == 0) {
                    // 往上滑,并且到头
                    // 禁止滚动的默认行为
                    event.preventDefault();
                    return;
                }

                // 下边缘检测
                if (distanceY < 0 && (scrollTop + 1 >= data.maxscroll)) {
                    // 往下滑,并且到头
                    // 禁止滚动的默认行为
                    event.preventDefault();
                    return;
                }
            },
            touchend: function () {
                data.maxscroll = 0;
            }
        });

        // 防止多次重复绑定
        container.data('isBindScroll', true);
    };

</script>

</html>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值