在我的页面上,用户点击一个元素来编辑它。为了方便起见,我将类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元素,有可能降低性能吗?我是否忽略了任何其他考虑因素(例如事件处理的浏览器实现),这会建议采取第二种方式?
简单回答:肯定是第一种方式。事件委派的性能要高得多,但在代码中需要额外的条件,因此它基本上是复杂性与性能的权衡。
较长的答案:对于少数元素,添加单独的事件处理程序效果良好。但是,随着添加越来越多的事件处理程序,浏览器的性能开始下降。原因是听事件是记忆密集型的。
然而,在DOM中,事件从最特定的目标"冒泡"到最通用的触发过程中的任何事件处理程序。这里有一个例子:
<html>
<body>
<div>
<a>
<img>
</a>
</div>
</body>
</html>
如果单击<img>
标记,则该单击事件将按以下顺序触发任何事件处理程序:
- img
- a
- div
- 车身
- html
document
对象
事件委派是一种为一组事件处理程序而不是您关心的特定元素(如<img>
)侦听父级(例如<div>
)的技术。事件对象将具有一个目标属性,该属性指向事件源自的特定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
});
});
欲了解更多信息,请查看Dave Walsh关于活动代表团或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,则表示没有具有"可编辑"类的父级
第一种方法的另一个优点
我使用的是您提到的第二种(替代)方法,我注意到当ajax加载时。。。新创建的元素没有侦听该事件。每次ajax之后,我都必须重做for
循环。
在我的代码中,第一个方法看起来像下面的方法,它也适用于ajax。
document.addEventListener('click', function (e) {
if (hasClass(e.target, 'classname')) {
// do stuff
}
}, false);
所以第一个是更好的