zoukankan      html  css  js  c++  java
  • 【JavaScript(webapi)】记一次Debug,关于 removeEventListener 方法莫名失效

    先上一段代码

    这个函数用于以反比例曲线平滑滚动页面:
    在这里插入图片描述
    这里用闭包实现是为了能如下图这样方便地 在添加回调事件的同时传参
    在这里插入图片描述


    但是这个函数存在一个影响用户体验的滚动问题,上动图:
    在这里插入图片描述
    我在页面自动滚动快结束的时候(有时候就想快点翻),向下滑动鼠标滚轮,然而这个函数却是不到目标位置不罢休
    由于我向下滚动略过了锚定位置,而 Interval 并没有捕捉到路过锚定的状态,所以页面反而会向上滚动。


    解决方法很简单,我立刻想到了 event.preventDefault() 。
    我只要在开始动画前监听 mousewheel 事件,然后在回调函数中写下这一行来阻止默认行为即可。
    当然,动画结束时一定要 removeEventListener 清除监听。

    经过修改后,这个函数是这样的:
    在这里插入图片描述
    注:关于 addEventListener 方法的第三个参数{ passive: false },用于成功调用 e.preventDefault().
    详情移步 → https://blog.csdn.net/csdnXiaoZou/article/details/87276026,这位博主写的很详细

    上面的代码很简单,
    当动画结束时,清除当前定时器,并移除当前 mousewheel 回调事件。
    当新动画开始时,清除上一个定时器,并移除上一个 mousewheel 回调事件。

    然后就出了问题,当我连续触发两次动画后,滚动鼠标滚轮就没用了,同时控制台在滚动时输出1。
    这代表上一个回调事件并没有被正确清除。

    于是我下断点跟着走了一遍,代码确实跑对了地方,那问题就出在闭包中。
    分析了半天,还是没有头绪。万般无奈之下,开始查文档,百度 + Google。

    查资料的时候突然看到一句话,大概意思是,必须保证 add 和 remove 时的函数指针相同
    这我当然是早就知道的,但我思考了一下,问题只能出现在这里,于是试着调试了一下函数指针
    在这里插入图片描述
    输出:
    在这里插入图片描述
    恍然大悟,每次调用函数都重新定义了一遍 stop_scroll ,于是后一次的 stop_scroll 与前一次并不相同。
    那么只要把函数拿出来就好了。

    于是改成这样:
    在这里插入图片描述
    问题解决,但总感觉这样不够优雅,毕竟我是封装了一个函数,这多出来一个函数算怎么回事?

    于是我又试着改了改,这样就可以了:
    在这里插入图片描述
    函数内声明一个全局变量来存这个回调函数。
    至此完美解决!


    还有一个思路,这里直接强硬地禁止用户滚动页面,而实际上我们可以当用户滚动滚轮时直接停止动画。

    另一个,再参考大多数网站的做法,即滚动位置不依赖于当前的 window,pageYOffset,而是自己维护一个变量,记录这一步到哪了。


    加了一个参数,代表滚动过程中禁止用户滚动还是用户滚动即停
    接口优雅了许多,但函数一点也不优雅…
    甚至有些地方用到了两层的闭包,但是也是为了防止写更多代码(小声bb… addEventListener 没有给出传参的方法)
    在这里插入图片描述

    代码:

    		//...
            function scroll_smooth(y, velocity, interval, callback, stopScroll = true) {
                if (stopScroll) {
                    if (typeof stop_scroll === 'undefined') { 
                        stop_scroll = function(e) {// 事件回调函数
                            e.preventDefault();
                        };
                    }
                } else {
                    if (typeof get_state === 'undefined') {
                        var fun_list = function() {
                            var state = false; // false 用户未滚动
                            return [
                                function() {
                                    return state;
                                },
                                function(state_) {
                                    return function() {
                                        console.log(state);
                                        state = state_;
                                    }
                                }
                            ];
                        }();
                        var get_state = fun_list[0];
                        var set_state = fun_list[1];
                    }
                }
    
                function animate(y, velocity, interval, callback) {
                    if (stopScroll) {
                        document.addEventListener('mousewheel', stop_scroll, {
                            passive: false
                        });
                    } else {
                        temp_fun = set_state(true);
                        document.addEventListener('mousewheel', temp_fun);
                    }
                    this.timer = setInterval(() => {
                        if (!stopScroll) {
                            if (get_state() === true) { // 判断用户滚动
                                clearInterval(this.timer);
                                document.removeEventListener('mousewheel', temp_fun);
                                callback && callback();
                                return;
                            }
                        }
                        if (window.pageYOffset != y) { // 滚动过程
                            var step = (y - window.pageYOffset) / velocity
                            window.scroll(0, window.pageYOffset + (step < 0 ? Math.floor(step) : Math.ceil(step)));
                        } else {
                            callback && callback();
                            clearInterval(this.timer);
                            if (stopScroll) {
                                document.removeEventListener('mousewheel', stop_scroll);
                            }
                        }
                    }, interval);
                }
                return function() {
                    clearInterval(this.timer);
                    if (stopScroll) { // 清除事件回调
                        document.removeEventListener('mousewheel', stop_scroll);
                    } else {
                        if (typeof temp_fun !== 'undefined') {
                            document.removeEventListener('mousewheel', temp_fun);
                        }
                    }
    
                    animate(y, velocity, interval, callback);
                };
            }
    
  • 相关阅读:
    JavaScript 的定时(Timing )事件——setTimeout()与setInterval()
    HTML5+CSS3制作无限滚动与文字跳动效果
    动画属性与过渡属性与渐变属性(全)
    JavaScript 数组2—关联数组
    JavaScript 数组1—索引数组
    什么是JavaScript循环结构?
    JavaScript分支结构Ⅱ—switch-case
    JavaScript分支结构Ⅰ—IF-ELSE
    JavaScript 正则表达式——预定义类,边界,量词,贪婪模式,非贪婪模式,分组,前瞻
    SEO搜索引擎优化是什么?
  • 原文地址:https://www.cnblogs.com/gaolihai/p/13149759.html
Copyright © 2011-2022 走看看