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

wva*_*aal 8 javascript jquery google-analytics javascript-events event-delegation

问题:我需要的任何数量的事件处理程序(,DOM节点绑定到任意数量的元素window,document),在动态运行时,我需要能够更新事件我的网页的生命周期中动态创建(或损坏)节点结合.我可以看到有三个选项来解决这个问题:

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) { ... }
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

来源

Gre*_*rdt 1

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

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

至于事件委托的效率,事件必须冒泡的节点越多,花费的时间就越长,但是我们谈论的是〜1到2毫秒的时间差——也许是。用户无法察觉。通常,DOM 事件的处理会带来性能损失,而不是事件从一个节点冒泡到另一个节点。

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

  1. 文档树中的节点越多,浏览器操作它所花费的时间就越多。
  2. 页面上的事件处理程序数量越多,JavaScript 的速度就越慢,尽管您需要数百个处理程序才能真正看到差异。

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

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

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

这是事实,但是让我们考虑一下感知的性能。用户不会注意到委托许多点击事件。scroll如果您委托诸如或 之类的事件mousemove,该事件每秒最多触发 50 次(留出 20 毫秒来处理该事件),那么用户可以感知到性能问题。这又回到了我反对过早优化事件处理程序代码的论点。

许多点击事件可以毫无问题地委托给共同的祖先,例如document.documentElement. 我会在那里委托一个“mousemove”事件吗?或许。这取决于其他情况,以及委托的“mousemove”事件是否具有足够的响应能力。