什么是事件冒泡和捕获?

Aru*_*hny 982 javascript event-bubbling dom-events

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

Aru*_*hny 1376

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

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

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

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

涓涓细流,泡沫起来

在过去,Netscape主张事件捕获,而微软则推动事件冒泡.两者都是W3C 文档对象模型事件标准(2000)的一部分.

IE <9 使用事件冒泡,而IE9 +和所有主要浏览器都支持这两种情况.另一方面,对于复杂的DOM ,事件冒泡性能可能略低.

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

<div>
    <ul>
        <li></li>
    </ul>
</div>
Run Code Online (Sandbox Code Playgroud)

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

在捕获模型中,事件将由div第一个事件处理(首先点击事件处理程序div),然后ul是目标元素中的最后一个事件处理器li.

在冒泡模型中,会发生相反的情况:事件首先由元素处理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);
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

JSFiddle的另一个例子.

  • IE> = 9现在支持`useCapture`.[来源](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener#Browser_compatibility) (40认同)
  • 我知道发表评论为时已晚,但我在http://catcode.com/domcontent/events/capture.html找到了很好的文章 (7认同)
  • 上面的答案在详细解释中的顺序是正确的,但让你认为涓涓细流发生在第二次"冒泡,涓滴".事件总是在泡沫阶段之前经历捕获阶段.正确的顺序是`trickle down` =>`onElement` =>`bubble up` (6认同)
  • `triclkling`和`capture`一样吗?Crockford在这个视频讲座中讨论了"Trickling v.Bubbling" - https://www.youtube.com/watch?v=Fv9qT9joc0M&list=PL7664379246A246CB在1小时5分钟左右. (3认同)
  • “通过冒泡,事件首先被最里面的元素捕获和处理,然后传播到外部元素。” -- 您应该指出并非所有事件都会冒泡(例如,`focus`)。 (2认同)

Fel*_*ing 499

描述:

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

事件捕获

使用事件捕获时

               | |
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------
Run Code Online (Sandbox Code Playgroud)

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

事件冒泡

当您使用事件冒泡时

               / \
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------
Run Code Online (Sandbox Code Playgroud)

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


用什么?

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


小智 69

如果有两个元素元素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);
Run Code Online (Sandbox Code Playgroud)
#div1{
  background-color:red;
  padding: 24px;
}

#div2{
  background-color:green;
}
Run Code Online (Sandbox Code Playgroud)
<div id="div1">
  div 1
  <div id="div2">
    div 2
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

请尝试改变真假.

  • @masterxilo:不需要Fiddle,StackOverflow现在支持[内联代码(stack snippets)](http://blog.stackoverflow.com/2014/09/introducing-runnable-javascript-css-and-html-code-snippets/ ). (2认同)

gm2*_*008 25

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

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


小智 14

DOM 事件描述了事件传播的 3 个阶段: 捕获阶段 \xe2\x80\x93 事件深入到元素。目标阶段 \xe2\x80\x93 事件到达目标元素。冒泡阶段 \xe2\x80\x93 事件从元素中冒泡。

\n

在此输入图像描述

\n


Kon*_*dal 8

冒泡

  Event propagate to the upto root element is **BUBBLING**.
Run Code Online (Sandbox Code Playgroud)

捕捉

  Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.
Run Code Online (Sandbox Code Playgroud)


Ade*_*lin 7

还有一个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);
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)