什么是事件冒泡和捕获?

事件冒泡和捕获之间有什么区别?什么时候应该使用冒泡与捕获?

答案

<div>
    <ul>
        <li></li>
    </ul>
</div>

描述:

quirksmode.org对此有很好的描述。简而言之(从 quirksmode 复制):

事件捕捉

使用事件捕获时

| |
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

element1 的事件处理程序首先触发,element2 的事件处理程序最后触发。

事件冒泡

使用事件冒泡时

/ \
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

element2 的事件处理程序首先触发,element1 的事件处理程序最后触发。


使用什么?

这取决于您想做什么。没有比这更好的了。区别在于事件处理程序的执行顺序。在大多数情况下,在冒泡阶段触发事件处理程序是可以的,但也有必要更早地触发它们。

如果元素 1 和元素 2 有两个元素,则元素 2 在元素 1 内,并且我们将事件处理程序与这两个元素相连,可以说 onClick。现在,当我们单击元素 2 时,两个元素的 eventHandler 将被执行。现在,这里的问题是事件将按什么顺序执行。如果首先执行元素 1 附加的事件,则称为事件捕获;如果首先执行元素 2 附加的事件,则称为事件冒泡。根据 W3C,事件将在捕获阶段开始,直到到达目标并返回到元素,然后开始冒泡

通过 addEventListener 方法的 useCapture 参数可以知道捕获和冒泡状态

eventTarget.addEventListener(type,listener,[,useCapture]);

默认情况下,useCapture 为 false。这意味着它处于冒泡阶段。

var div1 = document.querySelector("#div1");
var div2 = document.querySelector("#div2");

div1.addEventListener("click", function (event) {
  alert("you clicked on div 1");
}, true);

div2.addEventListener("click", function (event) {
  alert("you clicked on div 2");
}, false);
#div1{
  background-color:red;
  padding: 24px;
}

#div2{
  background-color:green;
}
<div id="div1">
  div 1
  <div id="div2">
    div 2
  </div>
</div>

请尝试更改是非。

在 javascript.info上发现了本教程,在解释该主题时非常清楚。最后,它的三点总结确实在说明关键点。我在这里引用:

  1. 首先将事件捕获到最深的目标,然后再冒泡。在 IE <9 中,它们只会冒泡。
  2. 除具有最后一个参数true addEventListener ,所有处理程序都在冒泡阶段工作,这是在捕获阶段捕获事件的唯一方法。
  3. 对于其他浏览器,可以通过event.cancelBubble=true (IE)或event.stopPropagation()来停止冒泡 / 捕获。

还有Event.eventPhase属性,它可以告诉您事件是在目标位置还是来自其他地方。

请注意,尚未确定浏览器的兼容性。我已经在 Chrome(66.0.3359.181)和 Firefox(59.0.3)上对其进行了测试,并且该设备受其支持。

从接受的答案扩展本来就不错的代码片段 ,这是使用eventPhase属性的输出

var logElement = document.getElementById('log');

function log(msg) {
  if (logElement.innerHTML == "<p>No logs</p>")
    logElement.innerHTML = "";
  logElement.innerHTML += ('<p>' + msg + '</p>');
}

function humanizeEvent(eventPhase){
  switch(eventPhase){
    case 1: //Event.CAPTURING_PHASE
      return "Event is being propagated through the target's ancestor objects";
    case 2: //Event.AT_TARGET
      return "The event has arrived at the event's target";
    case 3: //Event.BUBBLING_PHASE
      return "The event is propagating back up through the target's ancestors in reverse order";
  }
}
function capture(e) {
  log('capture: ' + this.firstChild.nodeValue.trim() + "; " + 
  humanizeEvent(e.eventPhase));
}

function bubble(e) {
  log('bubble: ' + this.firstChild.nodeValue.trim() + "; " + 
  humanizeEvent(e.eventPhase));
}

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
  divs[i].addEventListener('click', capture, true);
  divs[i].addEventListener('click', bubble, false);
}
p {
  line-height: 0;
}

div {
  display:inline-block;
  padding: 5px;

  background: #fff;
  border: 1px solid #aaa;
  cursor: pointer;
}

div:hover {
  border: 1px solid #faa;
  background: #fdd;
}
<div>1
  <div>2
    <div>3
      <div>4
        <div>5</div>
      </div>
    </div>
  </div>
</div>
<button onclick="document.getElementById('log').innerHTML = '<p>No logs</p>';">Clear logs</button>
<section id="log"></section>

Event propagate to the Upto root element is Bubbling.

捕捉

Event propogate from body(root) element to eventTriggered Element is Capturing.
<html>
<head>
    <style type="text/css">
        .divStyle
        {
            display: table-cell;
            border: 5px solid black;
            padding: 20px;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="DIV1" class="divStyle">
        DIV 1
        <div id="DIV2" class="divStyle">
            DIV 2
            <div id="DIV3" class="divStyle">
                DIV 3
            </div>
        </div>
    </div>
    <script type="text/javascript">
        var divElements = document.getElementsByTagName('div');

        for (var i = 0; i < divElements.length; i++)
        {
            divElements[i].addEventListener("click", clickHandler, false);
            divElements[i].addEventListener("click", clickHandler, true);
        }

        function clickHandler()
        {
            alert(this.getAttribute("id") + " click event handled");
        }
    </script>
</body>
</html>