什么是事件冒泡和捕获?

时间:2011-01-06 15:44:43

标签: javascript javascript-events

9 个答案:

答案 0 :(得分:1308)

答案 1 :(得分:488)

答案 2 :(得分:67)

如果有两个元素元素1和元素2.元素2在元素1内部,我们附加一个事件处理程序,两个元素都让我们说onClick。现在当我们点击元素2时,将执行两个元素的eventHandler。现在问题在于事件将以何种顺序执行。如果首先执行附加了元素1的事件,则称为事件捕获,如果首先执行附加元素2的事件,则称为事件冒泡。 根据W3C,事件将在捕获阶段开始,直到它到达目标回到元素然后它开始冒泡

addEventListener方法的useCapture参数已知捕获和冒泡状态

  

eventTarget.addEventListener(类型,听众,[方法,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>
&#13;
&#13;
&#13;

请尝试改变真假。

答案 3 :(得分:23)

我发现这个tutorial at javascript.info在解释这个主题时非常清楚。最后的三点总结实际上是在谈论关键点。我在这里引用它:

  
      
  1. 事件首先被捕获到最深的目标,然后冒出来。在   IE&lt; 9他们只是泡沫。
  2.   
  3. 所有处理程序都适用于冒泡阶段   addEventListener,最后一个参数为true,这是唯一的方法   捕获阶段捕获事件。
  4.   
  5. 鼓泡/捕捉可以   由event.cancelBubble = true(IE)或event.stopPropagation()停止   对于其他浏览器。
  6.   

答案 4 :(得分:4)

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

请注意,尚未确定浏览器兼容性。我在Chrome(66.0.3359.181)和Firefox(59.0.3)上进行了测试,并在那里支持。

扩展已经great snippet from the accepted answer,这是使用eventPhase属性的输出

&#13;
&#13;
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);
}
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 5 :(得分:2)

DOM 事件描述了事件传播的 3 个阶段: 捕获阶段 - 事件深入到元素。目标阶段——事件到达目标元素。冒泡阶段——事件从元素中冒泡出来。

enter image description here

答案 6 :(得分:1)

冒泡

  Event propagate to the Upto root element is Bubbling.

捕获

  Event propogate from body(root) element to eventTriggered Element is Capturing.

答案 7 :(得分:1)

在这里扩展其他人的答案

事件冒泡:从最内层控制到最外层控制。

事件捕获::从最外层控件到最内层控件发生事件。

addEventListener()方法用于启用/禁用事件冒泡和事件捕获。该方法具有三个参数。如果将第三个参数(阶段)设置为true,则启用事件捕获,如果将其设置为false,则启用事件冒泡(这是默认行为)。

如果要同时启用冒泡和捕获功能,则分配处理程序2次,如下所示,将相位参数设置为false,一次将相位参数设置为true,如下所示。

<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>

请注意::IE8和更早版本不支持addEventListener()方法。这意味着IE8和更早版本不支持事件捕获,因此上述代码在IE 8和更早版本中将不起作用。

答案 8 :(得分:0)

正如其他人所说,冒泡和捕获描述了一些嵌套元素接收给定事件的顺序。

我想指出,对于最内层 元素可能会出现一些奇怪的东西。实际上,在这种情况下,添加事件侦听器的顺序确实很重要

在下面的例子中,div2 的捕获将先执行,而不是冒泡;而 div4 的冒泡会比捕获先执行。

function addClickListener (msg, num, type) {
  document.querySelector("#div" + num)
    .addEventListener("click", () => alert(msg + num), type);
}
bubble  = (num) => addClickListener("bubble ", num, false);
capture = (num) => addClickListener("capture ", num, true);

// first capture then bubble
capture(1);
capture(2);
bubble(2);
bubble(1);

// try reverse order
bubble(3);
bubble(4);
capture(4);
capture(3);
#div1, #div2, #div3, #div4 {
  border: solid 1px;
  padding: 3px;
  margin: 3px;
}
<div id="div1">
  div 1
  <div id="div2">
    div 2
  </div>
</div>
<div id="div3">
  div 3
  <div id="div4">
    div 4
  </div>
</div>