什么是 DOM 事件委派?

任何人都可以解释一下JavaScript中的事件委托,以及它有什么用处吗?


答案 1

DOM 事件委派是一种通过单个公共父级而不是每个子级响应 ui 事件的机制,通过事件“冒泡”(也称为事件传播)的魔力。

在元素上触发事件时,将发生以下情况

将事件调度到其目标,并触发在那里找到的任何事件侦听器。然后,冒泡事件将触发通过向上跟踪的父链找到的任何其他事件侦听器,检查在每个连续的事件目标上注册的任何事件侦听器。这种向上传播将一直持续到 .EventTargetEventTargetDocument

事件冒泡为浏览器中的事件委派提供了基础。现在,您可以将事件处理程序绑定到单个父元素,并且只要事件在其任何子节点(以及它们的任何子节点)上发生,该处理程序就会被执行。这是事件委派。下面是实践中的一个示例:

<ul onclick="alert(event.type + '!')">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
</ul>

使用该示例,如果要单击任何子节点,您将看到 警报 ,即使没有将单击处理程序绑定到您单击的 节点也是如此。如果我们绑定到每个,你会得到相同的效果。<li>"click!"<li>onclick="..."<li>

那么有什么好处呢?

想象一下,您现在需要通过 DOM 操作将新项动态添加到上面的列表中:<li>

var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);

如果不使用事件委派,则必须将事件处理程序“重新绑定”到新元素,以便它的行为方式与其同级元素相同。使用事件委派,您无需执行任何操作。只需将新内容添加到列表中即可完成。"onclick"<li><li>

对于将事件处理程序绑定到许多元素的Web应用程序来说,这绝对是非常棒的,其中新元素是在DOM中动态创建和/或删除的。使用事件委派,通过将事件绑定移动到公共父元素,可以大大减少事件绑定的数量,并且可以动态创建新元素的代码与绑定其事件处理程序的逻辑分离。

事件委派的另一个好处是事件侦听器使用的总内存占用量会减少(因为事件绑定的数量会减少)。对于经常卸载的小页面(即用户经常导航到不同的页面),它可能没有太大区别。但对于长期应用来说,它可能很重要。当从 DOM 中删除的元素仍然声明内存(即它们泄漏)时,存在一些非常难以跟踪的情况,并且通常此泄漏的内存与事件绑定相关联。通过事件委派,您可以自由销毁子元素,而不会忘记“解除绑定”其事件侦听器(因为侦听器位于祖先上)。然后可以遏制这些类型的内存泄漏(如果不消除,有时很难做到。IE我在看着你)。

以下是事件委派的一些更好的具体代码示例:


答案 2

事件委派允许您避免将事件侦听器添加到特定节点;而是将事件侦听器添加到一个父级。该事件侦听器分析气泡事件以查找子元素上的匹配项。

JavaScript 示例 :

假设我们有一个包含多个子元素的父 UL 元素:

<ul id="parent-list">
  <li id="post-1">Item 1</li>
  <li id="post-2">Item 2</li>
  <li id="post-3">Item 3</li>
  <li id="post-4">Item 4</li>
  <li id="post-5">Item 5</li>
  <li id="post-6">Item 6</li>
</ul>

我们还假设在单击每个子元素时需要发生某些事情。您可以为每个单独的 LI 元素添加一个单独的事件侦听器,但是如果 LI 元素经常在列表中添加和删除,该怎么办?添加和删除事件侦听器将是一场噩梦,尤其是当添加和删除代码位于应用内的不同位置时。更好的解决方案是将事件侦听器添加到父 UL 元素。但是,如果将事件侦听器添加到父级,您将如何知道单击了哪个元素?

简单:当事件冒泡到 UL 元素时,检查事件对象的目标属性以获取对实际单击的节点的引用。下面是一个非常基本的 JavaScript 代码段,它说明了事件委派:

// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click", function(e) {
  // e.target is the clicked element!
  // If it was a list item
  if(e.target && e.target.nodeName == "LI") {
    // List item found!  Output the ID!
    console.log("List item ", e.target.id.replace("post-"), " was clicked!");
   }
});

首先向父元素添加单击事件侦听器。触发事件侦听器时,检查事件元素以确保它是要响应的元素类型。如果是LI元件,轰:我们有我们需要的东西!如果它不是我们想要的元素,则可以忽略该事件。这个例子非常简单 - UL和LI是一个直接的比较。让我们尝试一些更困难的事情。让我们有一个包含许多子类的父 DIV,但我们所关心的只是一个带有 classA CSS 类的 A 标记:

// Get the parent DIV, add click listener...
document.getElementById("myDiv").addEventListener("click",function(e) {
// e.target was the clicked element
  if(e.target && e.target.nodeName == "A") {
    // Get the CSS classes
    var classes = e.target.className.split(" ");
    // Search for the CSS class!
    if(classes) {
        // For every CSS class the element has...
        for(var x = 0; x < classes.length; x++) {
            // If it has the CSS class we want...
            if(classes[x] == "classA") {
                // Bingo!
                console.log("Anchor element clicked!");
                // Now do something here....
            }
        }
    }
  }
});

http://davidwalsh.name/event-delegate