JavaScript 中的事件冒泡和捕获

17 Mar 2025 | 6 分钟阅读

在 JavaScript 中,事件的传播被称为“事件流”。事件流是特定网页接收事件的顺序。因此,在 JS 中,事件流的过程取决于三个方面:

事件捕获

事件目标

事件冒泡

在本节中,我们将学习和讨论两个方面,即事件冒泡事件捕获。我们将尝试逐一实践性地学习这些概念。

事件冒泡

在使用 JavaScript 开发网页或网站时,会使用事件冒泡的概念,即当一个元素嵌套在另一个元素中,并且它们都属于同一个事件时,事件处理程序会被调用。这种技术或方法被称为事件冒泡。因此,在执行网页的事件流时,会使用事件冒泡。我们可以将事件冒泡理解为当一个元素嵌套在另一个元素中,并且两个元素都为同一事件注册了监听器时,调用事件处理程序的顺序。所以,调用从最深层的元素开始,向其父元素及所有祖先元素,自下而上地进行。

事件冒泡示例

让我们看下面的例子来理解事件冒泡的工作原理。

输出

Event Bubbling and Capturing in JavaScript

代码说明

  • 以上是基于 HTML 和 JavaScript 的代码。
  • 我们使用了一个 div 标签,其 div id 为 p1,并在该 div 内嵌套了一个按钮,其 button id 为 c1。
  • 现在,在 JavaScript 部分,我们使用 querySelector() 函数将 html 元素(p1 和 c1)分配给变量 parent 和 child。
  • 之后,我们创建并为 div 元素和子按钮添加了一个 click 事件。还创建了两个函数,帮助我们了解父元素和子元素执行的顺序。这意味着如果子元素的事件首先被调用,将打印“child is invoked”,否则将打印“parent is invoked”。
  • 因此,当点击按钮时,它将首先打印“child is invoked”,这意味着子事件处理程序中的函数首先执行。然后它会转到 div 父函数的调用。

这个顺序的发生是由于事件冒泡的概念。事件冒泡就是这样发生的。

我们也可以借助下面的流程图来理解事件流。

Event Bubbling and Capturing in JavaScript

这意味着当用户点击按钮时,点击事件按从下到上的顺序流动。

停止冒泡

从目标开始向上移动就是冒泡,即从子元素到其父元素,它会一直向上移动。但处理程序也可以在事件被完全处理后决定停止冒泡。在 JavaScript 中,我们使用 event.stopPropagation() 方法。

例如

在上面的代码中,当我们点击按钮时,它将不起作用,因为这里调用了 event.stopPropagation() 方法,导致父函数不会被调用。

Event Bubbling and Capturing in JavaScript

注意:event.stopPropagation() 方法阻止了向上的冒泡(仅针对一个事件),但当前元素上的所有其他处理程序仍然会运行。

为了停止冒泡并阻止当前元素上的处理程序运行,我们可以使用 event.stopImmediatePropagation() 方法。这是另一个方法,可以停止冒泡和所有其他处理程序的执行。这意味着如果一个元素在单个事件上有多个事件处理程序,使用此 event.stopImmediatePropagation() 方法将停止所有事件处理程序的冒泡。

不要不必要地使用事件冒泡。

尽管事件冒泡是一种方便的方法,但建议不要不必要地使用它。这是因为 event.stopPropagation() 方法会产生隐藏的陷阱,这些陷阱可能会在以后引起一些问题。

让我们通过一个例子来理解它。

  • 创建一个嵌套菜单,其中每个子菜单处理其元素的点击事件,并调用 event.stopPropagation() 方法来阻止触发外部菜单。
  • 现在,为了跟踪用户在点击上的行为,我们决定捕获整个窗口的点击事件,为此使用了 document.addEventListener('click')。
  • 但是,由于我们调用了 event.stopPropagation() 方法,我们的分析工具在因 stopPropagation() 而停止点击的地方将不会做任何事情,因此我们得到了一个死区。

尽管不了解事件捕获,事件冒泡的概念是不完整的,所以让我们从事件捕获开始,并尝试结合这两个概念来完全理解其概念和工作原理。

事件捕获

Netscape 浏览器是第一个引入事件捕获概念的。事件捕获与事件冒泡相反,在事件捕获中,事件从最外层元素移动到目标。而在事件冒泡的情况下,事件从目标移动到文件中的最外层元素。事件捕获在事件冒泡之前执行,但捕获很少使用,因为事件冒泡足以处理事件流。

事件捕获示例

让我们看一个示例代码来理解事件捕获的工作原理。

输出

Event Bubbling and Capturing in JavaScript

代码说明

  1. 上述代码是基于 HTML 和 JavaScript 的。
  2. 在 HTML 部分,我们创建了一个 div,其 id 为 p1。在该 div 内部,我们嵌套并创建了一个 id 为 c1 的按钮。
  3. 转到 JS 代码,最初,我们使用 querySelector() 方法将 html 元素,即 p1 id,分配给一个名为 parent 的变量,同样地,我们将 c1 id 分配给一个名为 child 的变量。
  4. 然后,我们使用了一个 click 事件,并将其附加到 p1 div 和 c1 按钮上。同时包含一个函数,用于在控制台上打印相应的消息。这意味着如果子事件首先被调用,它将首先在控制台上打印“Child is invoked”消息,如果父事件处理程序首先被调用,它将首先在控制台上打印“Parent is invoked”消息。
  5. 接下来,我们将 addEventListener() 的第三个参数设置为 true,以便在父 div 中启用事件捕获。
  6. 当我们点击按钮时,它首先执行附加在父 div 中的函数。
  7. 之后,按钮的 onclick() 函数运行,这是因为事件捕获。由于事件捕获,父元素的事件首先执行,然后目标元素的事件被执行。

所以,当我们点击按钮时,点击事件按以下顺序执行,您可以在下面的流程图中看到。

Event Bubbling and Capturing in JavaScript

事件流的完整概念

下图显示了事件流的执行过程。

Event Bubbling and Capturing in JavaScript

因此,事件处理和事件捕获都是事件委托的基础。它们是事件流的强大力量。