javascript 什么是事件冒泡和捕获?

1szpjjfi  于 2022-12-21  发布在  Java
关注(0)|答案(9)|浏览(131)

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

lmvvr0a8

lmvvr0a81#

事件冒泡和捕获是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元素处理。
有关详细信息,请参见

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

k97glaaz

k97glaaz2#

说明:

quirksmode.org对此有一个很好的描述。简单地说(复制自quirksmode):

事件捕获

使用事件捕获时

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

元素1的事件处理程序首先触发,元素2的事件处理程序最后触发。

事件冒泡

使用事件冒泡时

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

元素2的事件处理程序首先触发,元素1的事件处理程序最后触发。

使用什么?

这取决于你想做什么。没有更好的方法。不同的是事件处理程序的执行顺序。大多数情况下,在 * 冒泡 * 阶段触发事件处理程序是可以的,但也有必要提前触发它们。

2eafrhcq

2eafrhcq3#

如果有两个元素元素1和元素2,元素2在元素1的内部,我们为这两个元素附加一个事件处理程序,比如onClick,现在当我们单击元素2时,这两个元素的eventHandler都将被执行,现在的问题是事件将以什么顺序执行。如果与元素1关联的事件首先执行,则称为事件捕获;如果与元素2关联的事件首先执行,则称为事件冒泡。根据W3C,事件将在捕获阶段开始,直到它到达目标并返回到元素,然后它开始冒泡
捕获和冒泡状态可通过addEventListener方法的useCapture参数获知
添加事件监听器(类型,监听器,[,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>

请尝试更改正确和错误。

u0sqgete

u0sqgete4#

我发现这个tutorial at javascript.info在解释这个主题时非常清晰。它在最后的3点总结确实是在谈论关键点。我在这里引用它:
1.事件首先被捕获到最深的目标,然后冒泡。在IE〈9中,它们只冒泡。
1.所有处理程序都在冒泡阶段工作,但最后一个参数为trueaddEventListener除外,这是在捕获阶段捕获事件的唯一方法。
1.气泡/捕获可以通过event.cancelBubble=true(IE)或event.stopPropagation()(对于其他浏览器)停止。

piah890a

piah890a5#

还有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>
dldeef67

dldeef676#

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

whhtz7ly

whhtz7ly7#

"冒泡"

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

捕捉

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

vptzau2j8#

正如其他人所说,冒泡和捕获描述了一些嵌套元素接收给定事件的顺序。
我想指出的是,对于最里面的元素,可能会出现一些奇怪的情况。实际上,在这种情况下,事件侦听器被添加的顺序确实很重要。
在下面的示例中,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>
    • EDIT**:这种行为可能因浏览器而异(例如,目前在Firefox上出现,但在Chrome和Edge上没有)。不过,我认为应该注意到这一点。
abithluo

abithluo9#

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

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

1-捕获阶段

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

document.addEventListener('click',handleClick)

如果你想参与这个阶段,你必须写这个

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

当我们需要检测目标之外的点击时,我们需要这个阶段。也许我们有一个下拉菜单或模态打开,如果用户点击模态或下拉菜单之外的任何地方,我们希望关闭它

2-目标阶段

浏览器将查看被点击的元素,该元素是Button,并且如果按钮具有事件处理程序,则浏览器将调用该事件处理程序。

3-气泡阶段

Capture Phase相反,在这个阶段中,浏览器将从直接父元素(本例中为div元素)启动流程,然后访问body元素。

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

相关问题