【问题标题】:Best practices for where to add event listeners在何处添加事件侦听器的最佳实践
【发布时间】:2014-11-24 02:51:25
【问题描述】:

在我的页面上,用户单击一个元素以对其进行编辑。为此,我将editable 类分配给所有此类元素。

我应该如何监听所有这些元素的点击?目前,我正在这样做:

document.body.addEventListener("click", (event) => {
  if (event.target.classList.contains("editable")) {
    // do stuff
  }
});

另一种方法是在每个元素上设置一个侦听器,如下所示:

const editables = document.getElementsByClassName("editable");
for (const editable of editables) {
    editable.addEventListener("click", editElement);
}

在我看来,第一种方式在性能方面肯定更好,因为它只监听一个元素,但是否可以通过将所有此类事件附加到 body 元素来降低性能?是否还有其他我忽略的考虑因素(例如事件处理的浏览器实现)建议采用第二种方式?

【问题讨论】:

  • 请注意,您的第一个 sn-p 不会捕获对可编辑项的子元素的任何点击,而第二个 sn-p 会。

标签: javascript event-handling event-bubbling


【解决方案1】:

简答:绝对是第一种方式。事件委托的性能要好得多,但在代码中需要额外的条件,因此它基本上是复杂性与性能的权衡。

更长的答案:对于少量元素,添加单独的事件处理程序可以正常工作。但是,随着您添加越来越多的事件处理程序,浏览器的性能开始下降。原因是监听事件会占用大量内存。

但是,在 DOM 中,事件从最具体的目标“冒泡”到最普遍的目标,一路上触发任何事件处理程序。这是一个例子:

<html>
    <body>
        <div>
            <a>
                <img>
            </a>
         </div>
    </body>
</html>

如果您单击了&lt;img&gt; 标记,则该单击事件将按此顺序触发任何事件处理程序:

  1. img
  2. 一个
  3. div
  4. 身体
  5. html
  6. document对象

事件委托 是一种为一堆事件处理程序而不是您关心的特定元素(例如&lt;img&gt;)侦听父项(例如&lt;div&gt;)的技术。事件对象将有一个 target 属性,该属性指向事件起源的特定 dom 元素(在本例中为 &lt;img&gt;)。

您的事件委托代码可能如下所示:

$(document).ready(function(){
    $('<div>').on('click', function(e) {
        // check if e.target is an img tag
        // do whatever in response to the image being clicked
    });
});

如需了解更多信息,请查看 Dave Walsh 的 blog post on Event Delegation 或 duckduckgo“事件委托”。

OP 中代码示例的注意事项: 在第一个示例中,target.hasClass('editable') 表示单击的特定事物必须具有可编辑的类才能执行 if 块。正如其中一位评论者指出的那样,这可能不是您想要的。您可能想尝试以下方式:

$(document).on('click', function(e) {
   if ($(e.target).parents(".editable").length) {
       // Do whatever
   }
});

让我们分解一下:

  • $(e.target) - 页面上被点击的任何内容都转换为 jQuery
  • .parents(".editable") - 找到被点击元素的所有祖先,然后过滤以只包含类“可编辑”的元素
  • .length - 这应该是一个整数。如果为 0,这意味着没有“可编辑”类的父母

【讨论】:

    【解决方案2】:

    第一种方法的另一个优点

    我正在使用您提到的第二种(替代)方法,我注意到当加载 ajax 时......新创建的元素没有监听事件。我每次都必须在 ajax 之后重做 for 循环。

    我的代码中的第一种方法也适用于 ajax。

    document.addEventListener('click', function (e) {
        if (hasClass(e.target, 'classname')) { 
            // do stuff
        } 
    }, false);
    

    所以第一个更好

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 2012-05-05
      • 1970-01-01
      • 2014-10-13
      • 1970-01-01
      • 2017-12-26
      • 1970-01-01
      相关资源
      最近更新 更多