添加事件侦听器的最佳做法

Chr*_*ton 3 javascript event-handling event-bubbling

在我的页面上,用户单击一个元素以进行编辑.为了方便这一点,我将类分配editable给所有这些元素.

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

$lib.addEventListener(document.body, "click", function(event) {
  if($lib.hasClass(event.target, "editable") {
    // do stuff
  }
});
Run Code Online (Sandbox Code Playgroud)

这里$lib是一个小JS库我使用.

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

var editables = document.getElementsByClassName("editable");
for(var i = 0; i < editables.length; i++) {
    $lib.addEventListener(editables[i], "click", editElement);
}
Run Code Online (Sandbox Code Playgroud)

在我看来,第一种方式必须更好的性能,因为它只是一个元素被听,但是通过将所有这些事件附加到body元素是否可能降低性能?是否有任何其他考虑因素(例如,事件处理的浏览器实现)我忽略哪些会建议以第二种方式进行?

Rus*_*ore 7

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

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

但是,在DOM中,事件从最特定的目标"冒泡"到最常见的触发任何事件处理程序.这是一个例子:

<html>
    <body>
        <div>
            <a>
                <img>
            </a>
         </div>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

如果单击<img>标记,则该单击事件将按此顺序触发任何事件处理程序:

  1. IMG
  2. 一个
  3. DIV
  4. 身体
  5. HTML
  6. document 宾语

事件委托是一种监听父(例如<div>)一堆事件处理程序而不是你关心的特定元素的技巧(比方说<img>).事件对象将具有一个target属性,该属性指向事件源自的特定dom元素(在本例中<img>).

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

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

有关更多信息,请查看Dave Walsh 关于事件委派或duckduckgo"事件委托" 的博客文章.

OP中代码示例的注释:在第一个示例中,target.hasClass('editable')表示单击的特定内容必须具有可编辑的类才能执行if块.正如其中一位评论者指出的那样,这可能不是你想要的.你可能想尝试沿着这些方向尝试:

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

让我们稍微打破一下:

  • $(e.target) - 点击页面上转换为jQuery的任何内容
  • .parents(".editable") - 找到所单击元素的所有祖先,然后过滤为仅包含"可编辑"类的元素
  • .length - 这应该是一个整数.如果为0,则表示没有父母具有"可编辑"类