【问题标题】:What is event bubbling and capturing?什么是事件冒泡和捕获?
【发布时间】:2011-06-04 17:45:47
【问题描述】:

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

【问题讨论】:

标签: javascript dom-events event-bubbling


【解决方案1】:

事件冒泡和捕获是 HTML DOM API 中事件传播的两种方式,当事件发生在另一个元素内的元素中,并且两个元素都为该事件注册了句柄时。事件传播方式在which order the elements receive the event中确定。

通过冒泡,事件首先被最里面的元素捕获和处理,然后传播到外部元素。

通过捕获,事件首先被最外层元素捕获并传播到内部元素。

捕获也称为“trickling”,有助于记住传播顺序:

涓涓细流,冒泡

过去,Netscape 提倡事件捕获,而 Microsoft 提倡事件冒泡。两者都是 W3C Document Object Model Events 标准 (2000) 的一部分。

IE only event bubbling,而 IE9+ 和所有主流浏览器都支持两者。另一方面,performance of event bubbling may be slightly lower 用于复杂的 DOM。

我们可以使用addEventListener(type, listener, useCapture) 在冒泡(默认)或捕获模式下注册事件处理程序。要使用捕获模型,请将第三个参数传递为 true

示例

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

在上面的结构中,假设在li 元素中发生了点击事件。

在捕获模型中,事件将首先由div 处理(div 中的单击事件处理程序将首先触发),然后是ul,最后是目标元素@987654339 @。

在冒泡模型中,会发生相反的情况:事件将首先由li 处理,然后由ul 处理,最后由div 元素处理。

有关详细信息,请参阅

在下面的示例中,如果单击任何突出显示的元素,您可以看到事件传播流的捕获阶段首先发生,然后是冒泡阶段。

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

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

function capture() {
    log('capture: ' + this.firstChild.nodeValue.trim());
}

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

function clearOutput() {
    logElement.innerHTML = "";
}

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);
}
var clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);
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 id="clear">clear output</button>
<section id="log"></section>

Another example at JSFiddle.

【讨论】:

  • useCapture 现在支持 IE >= 9。source
  • 我知道发表评论为时已晚,但我在这里找到了不错的文章catcode.com/domcontent/events/capture.html
  • triclklingcapturing 一样吗? Crockford 在这个视频谈话中谈到了 Trickling v. Bubbling - youtube.com/watch?v=Fv9qT9joc0M&list=PL7664379246A246CB 围绕 1 hr 5 minutes
  • 关于详细解释中的顺序,上面的答案是正确的,但让您认为涓涓细流发生在“起泡,涓涓细流”之后。事件总是在冒泡阶段之前经历捕获阶段。正确的顺序是trickle down => onElement => bubble up
  • "通过冒泡,事件首先被最内层元素捕获和处理,然后传播到外层元素。" -- 您应该指出,并非所有事件都会冒泡(例如,focus)。
【解决方案2】:

说明:

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

事件捕捉

当你使用事件捕获时

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

element1 的事件处理器最先触发,element2 的事件处理器最后触发。

事件冒泡

当你使用事件冒泡时

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

element2 的事件处理器最先触发,element1 的事件处理器最后触发。


使用什么?

这取决于你想做什么。没有更好的了。不同之处在于事件处理程序的执行顺序。大多数情况下,在 冒泡 阶段触发事件处理程序会很好,但也可能需要更早地触发它们。

【讨论】:

  • 不会同时发生,先捕获然后冒泡,还有什么是dispatch事件?
  • 这里有一个图形示例:javascript.info/bubbling-and-capturing
  • 捕获阶段处理程序在某些元素的代码(您无法控制的代码,如插件)停止传播并且您真的想知道该事件何时发生时特别有用。您可以在去他们的路上而不是在回来的路上拿起它。如果他们停止传播,您可能无法在冒泡阶段获得它。
【解决方案3】:

如果有两个元素元素 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>

请尝试更改真假。

【讨论】:

【解决方案4】:

我发现tutorial at javascript.info 非常清楚地解释了这个主题。而它最后的3点总结,真的是说到了重点。我在这里引用它:

  1. 事件首先被捕获到最深的目标,然后冒泡。在 IE
  2. 所有处理程序都在冒泡阶段工作,但除外 addEventListener 与最后一个参数 true,这是唯一的方法 在捕获阶段捕捉事件。
  3. 冒泡/捕获可以 由 event.cancelBubble=true (IE) 或 event.stopPropagation() 停止 对于其他浏览器。

【讨论】:

    【解决方案5】:

    还有Event.eventPhase 属性可以告诉您事件是在目标还是来自其他地方,并且浏览器完全支持它。

    在已有的great snippet from the accepted answer 上展开,这是使用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>

    【讨论】:

    【解决方案6】:

    冒泡

      Event propagate to the upto root element is **BUBBLING**.
    

    捕获

      Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.
    

    【讨论】:

      【解决方案7】:

      DOM 事件描述了事件传播的 3 个阶段: 捕获阶段——事件传递到元素。目标阶段——事件到达目标元素。冒泡阶段——事件从元素冒泡。

      【讨论】:

        【解决方案8】:

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

        我想指出,对于 innermost 元素可能看起来有些奇怪。实际上,在这种情况下,添加事件侦听器的顺序很重要

        在下面的例子中,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>

        【讨论】:

        • 添加事件监听器的顺序无关紧要如果仍然不确定,请尝试自己的示例。
        • @sasidhar 我的例子是不言自明的。现在,如果您单击 div #4,您将获得“捕获 3,气泡 4,捕获 4,气泡 3”。如果您颠倒断言capture(3); capture(4); bubble(4); bubble(3); 的顺序,然后再次单击 div #4,您将得到“capture 3, capture 4, bubble 4, bubble 3”。这是事实,尽管我无法解释。
        • 在 Edge 和 chrome 中尝试了您的示例,无论顺序如何,结果始终为 capture(3); capture(4); bubble(4); bubble(3)'
        猜你喜欢
        • 2011-03-18
        • 2014-03-07
        • 1970-01-01
        • 1970-01-01
        • 2011-02-09
        • 2017-01-13
        • 1970-01-01
        相关资源
        最近更新 更多