传播

对于大多数事件类型,在具有子节点的节点上注册的处理器,也将接收发生在子节点中的事件。若点击一个段落中的按钮,段落的事件处理器也会收到点击事件。

但若段落和按钮都有事件处理器,则先执行最特殊的事件处理器(按钮的事件处理器)。也就是说事件向外传播,从触发事件的节点到其父节点,最后直到文档根节点。最后,当某个特定节点上注册的所有事件处理器按其顺序全部执行完毕后,窗口对象的事件处理器才有机会响应事件。

事件处理器任何时候都可以调用事件对象的stopPropagation方法,阻止事件进一步传播。该方法有时很实用,例如,你将一个按钮放在另一个可点击元素中,但你不希望点击该按钮会激活外部元素的点击行为。

下面的示例代码将mousedown处理器注册到按钮和其外部的段落节点上。在按钮上点击鼠标右键,按钮的处理器会调用stopPropagation,调度段落上的事件处理器执行。当点击鼠标其他键时,两个处理器都会执行。

  1. <p>A paragraph with a <button>button</button>.</p>
  2. <script>
  3. let para = document.querySelector("p");
  4. let button = document.querySelector("button");
  5. para.addEventListener("mousedown", () => {
  6. console.log("Handler for paragraph.");
  7. });
  8. button.addEventListener("mousedown", event => {
  9. console.log("Handler for button.");
  10. if (event.button == 2) event.stopPropagation();
  11. });
  12. </script>

大多数事件对象都有target属性,指的是事件来源节点。你可以根据该属性防止无意中处理了传播自其他节点的事件。

我们也可以使用target属性来创建出特定类型事件的处理网络。例如,如果一个节点中包含了很长的按钮列表,比较方便的处理方式是在外部节点上注册一个点击事件处理器,并根据事件的target属性来区分用户按下了哪个按钮,而不是为每个按钮都注册独立的事件处理器。

  1. <button>A</button>
  2. <button>B</button>
  3. <button>C</button>
  4. <script>
  5. document.body.addEventListener("click", event => {
  6. if (event.target.nodeName == "BUTTON") {
  7. console.log("Clicked", event.target.textContent);
  8. }
  9. });
  10. </script>