为什么不把Javascript事件委托带到极致?

时间:2012-03-14 22:14:31

标签: javascript javascript-events

到目前为止,该网站上的大多数人可能都知道:

$("#someTable TD.foo").click(function(){
    $(e.target).doSomething();
});

的表现会比以下更糟糕:

$("#someTable").click(function(){
    if (!$(e.target).is("TD.foo")) return;
    $(e.target).doSomething();
});

现在有多糟糕将取决于你的桌子有多少TD,但只要你有至少几个TD,这个一般原则应该适用。 (注意:当然,聪明的事情是使用jQuery委托而不是上面的代码,但我只是试图做一个明显区别的例子。)

无论如何,我向同事解释了这个原则,他们的回答是“嗯,对于站点范围的组件(例如,日期选择INPUT),为什么停在那里?为什么不为每种类型的绑定一个处理程序? BODY本身的组成部分?“我没有一个好的答案。

显然,使用委托策略意味着重新思考如何阻止事件,这是一个缺点。此外,假设你有一个页面,你有一个“TD.foo”,不应该有一个事件连接到它。但是,如果您理解并愿意解决事件冒泡变化,并且如果您执行“如果您将.foo放在TD上,它始终会将事件连接起来”的策略,这些似乎都不是很重要。

我觉得我必须遗漏一些东西,所以我的问题是:将所有站点范围的组件的所有事件委托给BODY还有其他任何缺点(而不是直接将它们绑定到所涉及的HTML元素,或将它们委托给非BODY父元素)?

3 个答案:

答案 0 :(得分:22)

您缺少的是表演中有不同的元素。

您的第一个示例在设置点击处理程序时表现更差,但在触发实际事件时表现更好。

您的第二个示例在设置点击处理程序时表现更好,但在触发实际事件时表现更差。

如果所有事件都放在顶级对象(如文档)上,那么您将拥有一个巨大的选择器列表来检查每个事件,以便找到它所处理的处理函数。这个问题是jQuery弃用.live()方法的原因,因为它查找文档对象上的所有事件,并且当注册了大量.live()事件处理程序时,每个事件的性能都很差,因为它必须比较每个事件都有很多选择器来为该事件找到合适的事件处理程序。对于大规模工作,将事件绑定到接近触发事件的实际对象的效率要高得多。如果对象不是动态的,则将事件绑定到将触发它的对象。当您第一次绑定事件时,这可能会花费更多的CPU,但实际的事件触发将会很快并且会缩放。

jQuery' .on().delegate()可用于此目的,但建议您找到尽可能接近触发对象的祖先对象。这可以防止在一个顶级对象上累积大量动态事件,并防止事件处理的性能下降。

在上面的例子中,这样做非常合理:

$("#someTable").on('click', "td.foo", function(e) {
    $(e.target).doSomething();
});

这将为您提供所有行的单击处理程序的紧凑表示,即使您添加/删除行,它也会继续工作。

但是,这并没有多大意义:

$(document).on('click', "#someTable td.foo", function(e) {
    $(e.target).doSomething();
});

因为当没有真正需要时,这会将表事件与页面中的所有其他顶级事件混合在一起。您只是在事件处理中询问性能问题而没有处理事件处理的任何好处。

所以,我认为对你的问题的简短回答是,当事件被触发时,处理一个顶级位置的所有事件会导致性能问题,因为代码必须在有很多事情时理清哪个处理程序应该获得事件在同一个地方处理的事件。尽可能接近生成对象处理事件会使事件处理更有效。

答案 1 :(得分:3)

如果您使用普通JavaScript进行此操作,则触发事件的页面上随机点击的影响几乎为零。但是在jQuery中,由于必须运行原始JS命令以产生相同的效果,结果可能会更大。

就个人而言,我发现一个小团队很好,但是太多的团队会开始造成比它解决的更多的问题。

答案 2 :(得分:3)

  • 如果删除节点,则不会自动删除相应的侦听器。
  • 有些事件不会冒泡
  • 不同的库可能会通过停止事件传播来破坏系统(猜测你提到了那个)