让我们从一个示例开始。
处理程序(handler)被分配给了

,但是如果你点击任何嵌套的标签(例如 ),该处理程序也会运行:

  1. <div onclick="alert('The handler!')">
  2. <em>If you click on <code>EM</code>, the handler on <code>DIV</code> runs.</em>
  3. </div>

这是不是有点奇怪?如果实际上点击的是 ,为什么在

上的处理程序会运行?

冒泡

冒泡(bubbling)原理很简单。
当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
假设我们有 3 层嵌套 FORM > DIV > P,它们各自拥有一个处理程序:

  1. <style>
  2. body * {
  3. margin: 10px;
  4. border: 1px solid blue;
  5. }
  6. </style>
  7. <form onclick="alert('form')">FORM
  8. <div onclick="alert('div')">DIV
  9. <p onclick="alert('p')">P</p>
  10. </div>
  11. </form>

image.png
点击内部的

会首先运行 onclick:

  1. 在该

    上的。

  2. 然后是外部
    上的。
  3. 然后是外部
    上的。
  4. 以此类推,直到最后的 document 对象。

image.png
因此,如果我们点击

,那么我们将看到 3 个 alert:p → div → form。
这个过程被称为“冒泡(bubbling)”,因为事件从内部元素“冒泡”到所有父级,就像在水里的气泡一样。
几乎 所有事件都会冒泡。
这句话中的关键词是“几乎”。
例如,focus 事件不会冒泡。同样,我们以后还会遇到其他例子。但这仍然是例外,而不是规则,大多数事件的确都是冒泡的。

event.target

父元素上的处理程序始终可以获取事件实际发生位置的详细信息。
引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过 event.target 访问。
注意与 this(=event.currentTarget)之间的区别:

  • event.target —— 是引发事件的“目标”元素,它在冒泡过程中不会发生变化。
  • this —— 是“当前”元素,其中有一个当前正在运行的处理程序。

例如,如果我们有一个处理程序 form.onclick,那么它可以“捕获”表单内的所有点击。无论点击发生在哪里,它都会冒泡到 并运行处理程序。
在 form.onclick 处理程序中:

  • this(=event.currentTarget)是 元素,因为处理程序在它上面运行。
  • event.target 是表单中实际被点击的元素。

一探究竟:

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <link rel="stylesheet" href="example.css">
  6. </head>
  7. <body>
  8. A click shows both <code>event.target</code> and <code>this</code> to compare:
  9. <form id="form">FORM
  10. <div>DIV
  11. <p>P</p>
  12. </div>
  13. </form>
  14. <script src="script.js"></script>
  15. </body>
  16. </html>
  1. form {
  2. background-color: green;
  3. position: relative;
  4. width: 150px;
  5. height: 150px;
  6. text-align: center;
  7. cursor: pointer;
  8. }
  9. div {
  10. background-color: blue;
  11. position: absolute;
  12. top: 25px;
  13. left: 25px;
  14. width: 100px;
  15. height: 100px;
  16. }
  17. p {
  18. background-color: red;
  19. position: absolute;
  20. top: 25px;
  21. left: 25px;
  22. width: 50px;
  23. height: 50px;
  24. line-height: 50px;
  25. margin: 0;
  26. }
  27. body {
  28. line-height: 25px;
  29. font-size: 16px;
  30. }
  1. form.onclick = function(event) {
  2. event.target.style.backgroundColor = 'yellow';
  3. // chrome needs some time to paint yellow
  4. setTimeout(() => {
  5. alert("target = " + event.target.tagName + ", this=" + this.tagName);
  6. event.target.style.backgroundColor = ''
  7. }, 0);
  8. };

image.png
点击之后
image.png
event.target 可能会等于 this —— 当点击事件发生在 元素上时,就会发生这种情况。

停止冒泡

冒泡事件从目标元素开始向上冒泡。通常,它会一直上升到 ,然后再到 document 对象,有些事件甚至会到达 window,它们会调用路径上所有的处理程序。
但是任意处理程序都可以决定事件已经被完全处理,并停止冒泡。
用于停止冒泡的方法是 event.stopPropagation()。
例如,如果你点击