文档的听众太多?

问题描述 投票:1回答:1

设置

我有一个可重用的自定义下拉菜单,大致框架是这样的。

var List = (function() {

    // Custom list prototype stuffs...

    function ListObj(el, options) {     
        this._init(el);
    }

    ListObj.prototype._init = function(el) {
        var self = this;
        self.menu = el;
    }   

    // Expose an init function
    return {
        init : function(el, options) {
            new ListObj(el, options);
        }
    };

})();

每个菜单的初始化是这样的:

var ddlist = document.querySelector('.ddlist');
DDList.init(ddlist, options);

这一切都和预期的一样

我想要的是

我希望每个菜单都有一个行为,那就是 自动减少 如果点击不是在菜单本身发生的。

我的做法是

我决定在文档中添加文档点击监听器,而不是为文档添加一个单一的点击监听器,然后必须添加每个菜单,并检查点击的位置,我决定在 _init 方法。

ListObj.prototype._init = function(el) {
    var self = this;
    self.menu = el;

    document.addEventListener('click', function(e) {
        var el = e.target;

        if(self.menu === el || self.menu.contains(el)) {
            // Click was inside menu
            // Perform whatever tasks
        }else {
            // Click was outside of (this) menu, so minimize
            self.menu.minimize();
        }
    });

}

这样,每个单独的菜单都会自动初始化为监控文档点击的行为,如果点击不在菜单内,则最小化,或者执行任何需要的任务。

这样就完美的工作了。我特别感兴趣的是,我可以动态地创建新的下拉菜单,而不必将它们添加到文档点击监听器中,我也不必与 event.stopPropagation(); (我宁愿砍掉我的脚)。

可是... 我的电脑会爆炸吗?

这都是针对一个单页的web应用,这意味着可能会创建(和删除)几十个或几百个这样的菜单。我担心的是,所有这些文档点击监听器会堆积起来,导致性能问题。

如果我做一些像...

document.getElementById('someMenu').remove();

JS垃圾收集会不会知道它可以取消一个文档点击监听器?还是说这个监听器会一直存在到天荒地老?如果是后者,有什么办法可以让我在删除菜单时删除那个特定的监听器吗?

一个重要的问题是,菜单很可能永远不会被直接移除,而是它的父级菜单会被移除--因此 .remove() 将永远不会直接作用于菜单。

非常感谢!

刚刚测试了...

我创建了两个菜单,然后删除了其中的一个(.remove()). 即使去掉了一个菜单,但里面的文档点击监听器仍然会对每次点击发出火光。这似乎表明,垃圾收集将 处理好这个烂摊子,我就会有无数的听众。

那么,现在该怎么办呢?

javascript garbage-collection dom-events event-listener
1个回答
1
投票

给函数起个名字,这样你就可以删除事件监听器了。

ListObj.prototype._init = function(el) {
    var self = this;
    self.menu = el;
    self.click_handler = function(e) {
        var el = e.target;
        if (!document.contains(self.menu)) { // This element has been removed from DOM
            document.removeEventListener("click", self.click_handler);
            return;
        }
        if(self.menu === el || self.menu.contains(el)) {
            // Click was inside menu
            // Perform whatever tasks
        }else {
            // Click was outside of (this) menu, so minimize
            self.menu.minimize();
        }
    };
    document.addEventListener("click", self.click_handler);

}
© www.soinside.com 2019 - 2024. All rights reserved.