事件冒泡与事件委托

1. 事件冒泡 (Event Bubbling)

事件冒泡是指当一个事件被触发时,它会从目标元素(事件发生的元素)开始,逐层向上传播到其父元素、祖先元素,最终到达 documentwindow 对象。

事件冒泡的工作流程:
  1. 事件触发:当某个 DOM 元素(例如一个按钮)被点击时,事件会从该元素开始触发。
  2. 向上传播:事件会逐级向上传播到元素的父级、祖先级,直到达 documentwindow
冒泡示意图:
+------------------------+
|        window           |
|   +------------------+  |
|   |      document    |  |
|   |   +----------+   |  |
|   |   |  parent  |   |  |
|   |   |  +----+  |   |  |
|   |   |  |  btn |   |  |
|   |   |  +----+  |   |  |
|   |   +----------+   |  |
|   +------------------+  |
+------------------------+

假设用户点击 btn 按钮,事件首先在 btn 元素上触发,然后依次冒泡到 parentdocument,最终到达 window

事件冒泡的代码示例:

<div id="parent">
  <button id="child">Click me</button>
</div>
 
<script>
  // 父元素
  document.getElementById('parent').addEventListener('click', function () {
    alert('Parent clicked');
  });
 
  // 子元素
  document.getElementById('child').addEventListener('click', function (event) {
    alert('Child clicked');
    event.stopPropagation();  // 阻止事件冒泡
  });
</script>
流程:
  1. 用户点击 #child 按钮,事件首先触发在 #child 上。
  2. 如果没有调用 event.stopPropagation(),事件会继续冒泡到 #parent,并在 #parent 上触发相应的事件。
  3. 如果调用了 event.stopPropagation(),事件会在 #child 上停止,不会冒泡到 #parent

2. 事件委托 (Event Delegation)

事件委托 是利用事件冒泡的特性,将事件监听器绑定到父元素上,而不是直接绑定到每个子元素。父元素接收到事件后,通过判断事件的目标元素(event.target)来处理相应的子元素。

事件委托的工作原理:
  1. 你将事件监听器绑定到父元素上,而不是每个子元素。
  2. 当事件触发时,事件从子元素冒泡到父元素,父元素上的事件监听器会捕获这个事件。
  3. 在父元素的事件处理函数中,通过 event.target 获取实际触发事件的子元素,并做出相应的处理。
委托流程示意图:
+------------------------+
|        window           |
|   +------------------+  |
|   |      document    |  |
|   |   +----------+   |  |
|   |   |  parent  |   |  |
|   |   |   (click) |   |  |
|   |   |   handler |   |  |
|   |   +----------+   |  |
|   +------------------+  |
+------------------------+
  • 事件处理器绑定在 parent 元素上,子元素的点击事件通过冒泡触发到 parent,然后通过 event.target 获取点击的具体子元素。
事件委托的代码示例:
<ul id="parent">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
 
<script>
  // 事件委托绑定到父元素
  document.getElementById('parent').addEventListener('click', function (event) {
    // 判断点击的是否是 li 元素
    if (event.target.tagName === 'LI') {
      alert('Clicked on ' + event.target.textContent);
    }
  });
</script>
委托过程:
  1. 用户点击 #parent 下的任何一个 li 元素,事件会冒泡到父元素 #parent
  2. #parent 上的事件处理器中,event.target 将指向被点击的具体 li 元素,之后你可以根据该元素执行对应的逻辑。

事件委托的优势:

  • 动态添加元素:如果你使用事件委托,即使动态添加了新的 li 元素,事件仍然会被正确捕获。
  • 减少内存消耗:将所有事件处理程序放在父元素上,不需要为每个子元素都绑定事件监听器。
  • 简化代码:避免为每个子元素写单独的事件处理函数,简化了代码结构。

对比:事件冒泡与事件委托

特性事件冒泡事件委托
定义事件从目标元素冒泡到父元素直到根元素。将事件绑定到父元素上,通过冒泡机制处理所有子元素。
使用场景适用于在需要逐层捕获事件的场景。适用于处理大量相似的动态或静态子元素,减少内存使用。
性能每个子元素都需要单独绑定事件。只需要在父元素上绑定一次事件,性能更好。
支持动态元素不支持,需要动态绑定。支持,自动处理动态添加的元素。
代码量多个事件监听器,代码冗余。只需要为父元素绑定一个事件监听器。

事件冒泡 vs 事件委托

  1. 事件冒泡:适合已经存在的静态元素的事件绑定,事件按层级逐步传播。
  2. 事件委托:适合动态添加元素的场景,将事件绑定到父元素,在父元素捕获并处理所有子元素的事件,提升性能和简化代码。

总结

  • 事件冒泡 是浏览器默认的事件传播行为,可以从目标元素逐级向上传播到根元素。你可以通过 event.stopPropagation() 来阻止事件的进一步传播。
  • 事件委托 是通过将事件绑定到父元素上,利用事件冒泡特性来处理子元素的事件,减少内存消耗并简化代码,尤其适用于动态生成的元素。