首页 文章

什么是事件冒泡和捕获?

提问于
浏览
865

事件冒泡和捕获之间有什么区别?在这两个中,哪个是更快更好的模型?

5 回答

  • 466

    Description:

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

    事件捕获当您使用事件捕获时
    --------------- -----------------
    element1
    ----------- -----------
    element2 \ /
    事件捕获

    element1的事件处理程序首先触发,element2的事件处理程序最后触发 . 事件冒泡当您使用事件冒泡/
    --------------- | | -----------------
    | element1 | | |
    | ----------- | | -----------
    | | element2 | | | |
    | ------------------------- |
    |活动BUBBLING |

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


    What to use?

    这取决于你想做什么 . 没有更好的 . 不同之处在于事件处理程序的执行顺序 . 大多数情况下,在冒泡阶段发射事件处理程序是很好的,但也可能需要提前解雇它们 .

  • 23

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

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

    扩展已经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>
    
  • 1206

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

    请尝试改变真假 .

  • 0

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

    通过冒泡,事件首先被最内层元素捕获和处理,然后传播到外部元素 .

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

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

    涓涓细流,泡沫起来

    在过去,Netscape主张事件捕获,而微软则推动事件冒泡 . 两者都是W3C Document Object Model Events标准(2000)的一部分 .

    IE <9使用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 中,然后在目标元素中的最后一个 li 处理 .

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

    有关更多信息,请参阅

    在QuirksMode上

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

    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());
    }
    
    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>
    <section id="log"></section>
    

    Another example at JSFiddle .

  • 65

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

    事件首先被捕获到最深的目标,然后冒泡 . 在IE <9中他们只是泡沫 . 所有处理程序都在冒泡阶段工作,除了addEventListener,最后一个参数为true,这是在捕获阶段捕获事件的唯一方法 . 可以停止冒泡/捕捉event.cancelBubble = true(IE)或其他浏览器的event.stopPropagation() .

相关问题