设置
我有一个可重用的自定义下拉菜单,大致框架是这样的。
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()
). 即使去掉了一个菜单,但里面的文档点击监听器仍然会对每次点击发出火光。这似乎表明,垃圾收集将 不 处理好这个烂摊子,我就会有无数的听众。
那么,现在该怎么办呢?
给函数起个名字,这样你就可以删除事件监听器了。
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);
}