【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);
            };
        }
posted @ 2020-03-09 00:42  高厉害  阅读(420)  评论(0编辑  收藏  举报