动态绑定事件处理程序的最有效方法

问题:我需要在动态运行时将任意数量的事件处理程序绑定到任意数量的元素(DOM节点, windowdocument ),并且我需要能够在我的生命周期内更新动态创建(或销毁)节点的事件绑定。页。 我可以看到有三个选项来解决这个问题:

I) window事件委托
II)每个节点上的直接事件绑定
III)共同祖先的事件委托(直到运行时才会知道,并且在DOM被更改时可能需要重新计算)

这样做最有效的方法是什么?

一点背景

我正在处理一组需要对用户事件进行分析跟踪(点击,滚动等)的页面,我希望能够在一堆页面中轻松配置这些事件处理程序,而无需编写脚本来处理事件每个实例的绑定。 此外,因为我可能需要在将来跟踪新事件,或者跟踪动态添加到页面/从页面中删除的元素上的事件,我需要能够考虑在生命周期中发生的DOM中的更改的页面。

作为我目前正在考虑的一个例子,我想创建一个接受配置对象的函数,该对象允许程序员为每个事件指定默认处理程序,并允许它们为特定元素覆盖它们:

 Analytics.init({ // default handlers for each event type defaultHandlers: { "click": function(e) { ... }, "focus": function(e) { ... } }, // elements to listen to targetElements: { // it should work with non-DOM nodes like 'window' and 'document' window: { // events for which the default handlers should be called useDefaultHandlers: ['click'], // custom handler "scroll": function(e) { ... } }, // it should work with CSS selectors "#someId": { useDefaultHandlers: ['click', 'focus'], "blur": function(e) { ... } } } }); 

来源

  • SO:所有jQuery事件都应绑定到文档吗?
  • SO:如何找到两个或更多节点的最近共同祖先
  • jQuery文档:$ .fn.on()

我通常在document.documentElement对象上委托事件,因为:

  1. 它代表页面上的元素, 它包含用户可以与之交互的所有HTML标记的所有内容。
  2. 它可以在JavaScript开始执行时使用,无需窗口加载或DOM就绪事件处理程序
  3. 您仍然可以捕获“滚动”事件

至于事件委托的效率,事件必须冒出的节点越多,所需的时间越长,但我们正在谈论~1到2毫秒的时差 – 也许 。 用户难以察觉。 通常是处理DOM事件会导致性能损失,而不是事件从一个节点冒泡到另一个节点。

我发现以下事情通常会对JavaScript性能产生负面影响:

  1. 您在文档树中拥有的节点越多,浏览器操作它的时间就越长。
  2. 页面上事件处理程序的数量越多,JavaScript越慢,但是需要100个处理程序来真正看到差异。

主要是,#1影响最大。 我认为在大多数情况下,尝试在事件处理中提高性能是一种不成熟的优化。 我看到优化事件处理代码的唯一情况是当你有一个每秒触发多次的事件时(例如“滚动”和“鼠标移动”事件)。 事件委派的附加好处是您不必清理将与文档树分离的DOM节点上的事件处理程序,从而允许浏览器对该内存进行垃圾收集。

(来自下面的评论)wvandell说:

事件委派的性能成本与事件的实际“冒泡”几乎没有关系……将许多选择器委派给单个父级时会产生性能损失。

这是事实,但让我们考虑一下感知到的表现。 委派许多点击事件对用户来说是不明显的。 如果您委托scrollscroll移动等事件,每秒可以触发50次以上(剩余20毫秒来处理事件),则用户可以感知到性能问题。 这又回到了我反对过早优化事件处理程序代码的论点。

可以在共同的祖先上委派许多单击事件,例如document.documentElement 。 我会在那里委托“mousemove”活动吗? 也许。 这取决于发生了什么,以及委托“mousemove”事件是否足够响应。