什么是事件冒泡和捕获?

发布于 2024-10-10 20:15:13 字数 34 浏览 10 评论 0原文

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

What is the difference between event bubbling and capturing? When should one use bubbling vs capturing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

何其悲哀 2024-10-17 20:15:13

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

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

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

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

滴下来,冒泡

,Netscape 提倡事件捕获,而 Microsoft 则提倡事件冒泡。两者都是 W3C 文档对象模型事件 标准的一部分(2000)。

IE < 9 使用仅事件冒泡,而 IE9+ 和所有主要版本浏览器都支持。另一方面,对于复杂的 DOM,事件冒泡的性能可能会稍低

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

示例

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

在上面的结构中,假设在 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);
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>

JSFiddle 的另一个示例

Event bubbling and capturing are two ways of event propagation in the HTML DOM API, when an event occurs in an element inside another element, and both elements have registered a handle for that event. The event propagation mode determines in which order the elements receive the event.

With bubbling, the event is first captured and handled by the innermost element and then propagated to outer elements.

With capturing, the event is first captured by the outermost element and propagated to the inner elements.

Capturing is also called "trickling", which helps remember the propagation order:

trickle down, bubble up

Back in the old days, Netscape advocated event capturing, while Microsoft promoted event bubbling. Both are part of the W3C Document Object Model Events standard (2000).

IE < 9 uses only event bubbling, whereas IE9+ and all major browsers support both. On the other hand, the performance of event bubbling may be slightly lower for complex DOMs.

We can use the addEventListener(type, listener, useCapture) to register event handlers for in either bubbling (default) or capturing mode. To use the capturing model pass the third argument as true.

Example

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

In the structure above, assume that a click event occurred in the li element.

In capturing model, the event will be handled by the div first (click event handlers in the div will fire first), then in the ul, then at the last in the target element, li.

In the bubbling model, the opposite will happen: the event will be first handled by the li, then by the ul, and at last by the div element.

For more information, see

In the example below, if you click on any of the highlighted elements, you can see that the capturing phase of the event propagation flow occurs first, followed by the bubbling phase.

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.

素年丶 2024-10-17 20:15:13

描述:

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

事件捕获

当您使用事件捕获时

<前> <代码> | |
---------------| |-----------------
|元素1 | | |
| ------------| |------------ |
| |元素2 \ / | |
| ---------------------------------- |
|事件捕捉 |
-----------------------------------

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

事件冒泡

当您使用事件冒泡时

<前><代码> / \
---------------| |-----------------
|元素1 | | |
| ------------| |------------ |
| |元素2 | | | |
| ---------------------------------- |
|活动冒泡 |
-----------------------------------

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


使用什么?

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

Description:

quirksmode.org has a nice description of this. In a nutshell (copied from quirksmode):

Event capturing

When you use event capturing

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

the event handler of element1 fires first, the event handler of element2 fires last.

Event bubbling

When you use event bubbling

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

the event handler of element2 fires first, the event handler of element1 fires last.


What to use?

It depends on what you want to do. There is no better. The difference is the order of the execution of the event handlers. Most of the time it will be fine to fire event handlers in the bubbling phase but it can also be necessary to fire them earlier.

万水千山粽是情ミ 2024-10-17 20:15:13

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

请尝试更改 true 和 false。

If there are two elements element 1 and element 2. Element 2 is inside element 1 and we attach an event handler with both the elements lets say onClick. Now when we click on element 2 then eventHandler for both the elements will be executed. Now here the question is in which order the event will execute. If the event attached with element 1 executes first it is called event capturing and if the event attached with element 2 executes first this is called event bubbling.
As per W3C the event will start in the capturing phase until it reaches the target comes back to the element and then it starts bubbling

The capturing and bubbling states are known by the useCapture parameter of addEventListener method

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

By Default useCapture is false. It means it is in the bubbling phase.

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>

Please try with changing true and false.

谁许谁一生繁华 2024-10-17 20:15:13

我发现这个位于 javascript.info 的教程在解释这个主题时非常清楚。而最后的三点总结,真是说到了要害。我在这里引用一下:

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

I have found this tutorial at javascript.info to be very clear in explaining this topic. And its 3-points summary at the end is really talking to the crucial points. I quote it here:

  1. Events first are captured down to deepest target, then bubble up. In
    IE<9 they only bubble.
  2. All handlers work on bubbling stage excepts
    addEventListener with last argument true, which is the only way to
    catch the event on capturing stage.
  3. Bubbling/capturing can be
    stopped by event.cancelBubble=true (IE) or event.stopPropagation()
    for other browsers.
对你再特殊 2024-10-17 20:15:13

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

输入图像描述这里

DOM Events describes 3 phases of event propagation: Capturing phase – the event goes down to the element. Target phase – the event reached the target element. Bubbling phase – the event bubbles up from the element.

enter image description here

亚希 2024-10-17 20:15:13

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

扩展已经来自已接受答案的精彩片段,这是使用 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>

There's also the Event.eventPhase property which can tell you if the event is at target or comes from somewhere else, and it is fully supported by browsers.

Expanding on the already great snippet from the accepted answer, this is the output using the eventPhase property

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>

何以畏孤独 2024-10-17 20:15:13

冒泡

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

捕获

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

Bubbling

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

Capturing

  Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.
苹果你个爱泡泡 2024-10-17 20:15:13

当浏览器检测到事件时,它会尝试查找事件处理程序。此过程分为 3 个阶段。假设我们有这些元素

   <body>
      <div>
        <button>click</button>
      </div>
    </body>

1-Capture Phase

浏览器将查看刚刚单击的元素。然后它将转到顶部父元素,即 body。如果body中有任何点击句柄,浏览器就会调用它。检查 body 元素后,它将查看第二个顶部父元素,即 div 元素。这个过程将重复,直到浏览器到达最底部的按钮元素。一旦它看到按钮元素,第一阶段-捕获就会结束。大多数时候我们都会忽略这个阶段。这段代码忽略了这个阶段,

document.addEventListener('click',handleClick)

如果想涉及到这个阶段,就得这样写,

document.addEventListener('click',handleClick,true)

当我们需要检测目标之外的点击时,就需要这个阶段。也许我们打开了一个下拉菜单或模式,并且我们想要关闭它,如果用户单击模式或下拉菜单

2-Target Phase

之外的任何位置,浏览器将查看单击的元素,即 Button 和如果按钮有事件处理程序,它将调用它。

3- 冒泡阶段

Capture Phase相反,在此阶段,浏览器将从直接父元素(在本例中为div元素)开始该过程,然后它将访问body 元素。此代码将为冒泡阶段设置事件处理程序

document.addEventListener('click',handleClick,false)

When browser detects an event, it tries to find an event handler. This process has 3 phases. Let's say we have these elements

   <body>
      <div>
        <button>click</button>
      </div>
    </body>

1-Capture Phase

browser is going to take a look at the element that was just clicked. Then it will go to the top parent element which is body. if there is any click handle in body, the browser will call it. after checking the body element, it will look at the second top parent element which is div element. This process will repeat until the browser gets down to the button element at the very bottom. Once it sees the button element, Phase One- Capturing will be over. Most of the time we ignore this phase. This code ignores this phase

document.addEventListener('click',handleClick)

If you want to involve this phase, you have to write this

document.addEventListener('click',handleClick,true)

We need this phase when we need to detect a click outside the target. maybe we have a dropdown or a modal open and we want to close it if the user clicks on anywhere outside the modal or dropdown

2-Target Phase

the browser will look at the clicked element which is the Button and if the button has an event handler, it will call it.

3- Bubble Phase

Opposite to Capture Phase, in this phase, browser will start the process from the immediate parent element which is div element in this case and then it will visit the body element. This code will set up the event handler for the bubble phase

document.addEventListener('click',handleClick,false)
我只土不豪 2024-10-17 20:15:13

我做了一个小例子,您可以在其中体验“事件冒泡”: https:// /codepen.io/abernier/pen/yKGJXK?editors=1010

在此处输入图像描述

单击任意 div,您将看到“click”事件冒泡!

$divs.forEach(($div) => $div.addEventListener("click", handleClick2));

I made a small example, where you can experience "event-bubbling" live: https://codepen.io/abernier/pen/yKGJXK?editors=1010

enter image description here

Click any div, and you'll see the "click" event bubbling up!

$divs.forEach(($div) => $div.addEventListener("click", handleClick2));
小镇女孩 2024-10-17 20:15:13

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

我想指出的是,对于最里面元素可能会出现一些奇怪的情况。事实上,对于某些浏览器(例如 Mozilla Firefox),事件侦听器的添加顺序确实很重要。

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

备注:尝试使用 Mozilla Firefox 运行上面的代码片段。

编辑:到 2024 年,这个问题似乎已经得到解决。

As other said, bubbling and capturing describe in which order some nested elements receive a given event.

I wanted to point out that for the innermost element may appear something strange. Indeed, for some browser (e.g. on Mozilla Firefox), the order in which the event listeners are added does matter.

In the following example, capturing for div2 will be executed first than bubbling; while bubbling for div4 will be executed first than capturing.

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>

Remark: try running the snippet above with Mozilla Firefox.

EDIT: By 2024 this issue seems to have been fixed.

命硬 2024-10-17 20:15:13

这些是javascript中的事件传播过程。

事件冒泡:
在这种情况下,事件首先被捕获,并由事件发生的最里面的元素处理,然后它通过 DOM 层次结构中的父元素传播。

事件捕获:
在这种情况下,事件首先由最外面的元素捕获并处理,然后向下传播。

These are event propagation process in javascript.

Event Bubbling:
In this, the event is first captured and it is handled by the innermost element, where the event occurred, then it propagates through parent elements in the DOM hierarchy.

Event Capturing:
In this case, the event is first captured and handled by the outermost element, then it propagates downward.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文