为什么 event.stopPropagation() 会停止委托事件中的多个匹配元素?

Why does event.stopPropagation() stop multiple matched elements in delegated events?

使用委托事件时,所有匹配元素都会在事件从目标冒泡时触发该事件。

基于https://learn.jquery.com/events/event-delegation/参考HTML和下文JS,点击div-四个触发其所有祖先的点击事件(div-一个,div-二,div-三,正文)随着它冒泡。我将事件处理程序放在 body 上,选择 :not(#div-three)

这可以在 https://jsfiddle.net/bLw7ch50/1/ 中看到,例如,点击 div 4 会触发 3 个警报。对于 div-四个,div-两个和 div-一个。

我认为发生的事情是这样的:

  1. 点击 div-four,触发 div-four 的点击事件。
  2. div-four 事件冒泡到 div-three,触发了 div-three 的点击事件。
  3. div-four 事件冒泡到 div-two,触发了 div-two 的点击事件。
  4. div-four 事件冒泡到 div-one,触发了 div-one 的点击事件。
  5. div-four 事件冒泡到 body,触发了 body 的点击事件。事件处理程序运行。报告 target:div-four|current:div-four.
  6. div-three(从第 2 步开始)事件冒泡到 body。不触发事件处理程序。
  7. div-two(来自第 3 步)事件冒泡到 body。事件处理程序运行。报告 target:div-four|current:div-two.
  8. div-one(从第 4 步开始)事件冒泡到 body。事件处理程序运行。报告 target:div-four|current:div-one.

如果在事件处理程序中调用了 event.stopPropagation()div-four 事件将在第 5 步停止冒泡。(尽管在 body 上面没有任何冒泡。)但是,步骤 2、3、4 中触发的事件应该仍然存在并冒泡,因此步骤 6、7、8 仍将继续。

然而,情况似乎并非如此。在下面的 https://jsfiddle.net/bLw7ch50/ 中,取消注释 event.stopPropagation() 后,仅出现 1 个警报 target:div-four|current:div-four。所以我可能在某个地方错了。我的理解有什么问题吗?

HTML:

<body>
  <div id="div-one">
    1st div
    <div id="div-two">
      2nd div
      <div id="div-three">
        3rd div
        <div id="div-four">
          4th div
        </div>
      </div>
    </div>
  </div>
  <br/>
  <br/>
  <div id="output">
  </div>
</body>

CSS:

div {
  border: 1px solid;
}

Javascript(使用jquery1.9.1)

$(document).ready(function() {
  $("body").on("click", ":not(#div-three)", function(event) {
    var event_target = $(event.target).attr("id");
    var event_current = $(event.currentTarget).attr("id");
    $("#output").append("target:" + event_target +
                        "|current:" + event_current + "<br/>");
    alert("target:" + event_target +
          "|current:" + event_current);
    // Commented out in https://jsfiddle.net/bLw7ch50/1/, exists in https://jsfiddle.net/bLw7ch50
    event.stopPropagation(); // When this line is here, all other events stop
  });
});

这一切都发生在 jQuery 的事件委托中,它与事件冒泡的方式无关,只是 jQuery 试图模仿正常事件发生的情况。

使用普通事件处理程序,如果您 stopPropagation 该事件将不再在树中冒泡,并且具有相同事件类型的处理程序的祖先将不会收到它。这与使用事件委托时用于决定是否应为元素调用事件处理程序的逻辑相同。

当您使用委托绑定一个事件时,您只是将一个事件绑定到委托目标。当事件处理程序(不是您的事件处理程序,jQuery 的内部处理程序)被触发时,jQuery 查看原始事件目标和委托目标之间的元素,然后根据选择器并按从原始事件目标到委托目标的顺序调用它们的处理程序。如果这些处理程序中的任何一个 stopPropagation,符合条件的事件目标列表中的任何其他元素都不会调用该处理程序。此过程中没有触发其他事件。

该系统还考虑了其​​他委托事件。

$(document).on('click', '.box1', function () {
    console.log('first box1 handler');
});

$(document).on('click', '.box1', function (e) {
    console.log('second box1 handler');
    e.stopPropagation();
});
$(document).on('click', '.box2', function () {
    console.log('first box2 handler');
});

$(document).on('click', '.box2', function (e) {
    console.log('second box2 handler');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box1">
<div class="box1">
<div class="box1">
<div class="box1">
click here to begin
</div>
</div>
</div>
</div>
<div class="box2">
<div class="box2">
<div class="box2">
<div class="box2">
click here to begin without propagation
</div>
</div>
</div>
</div>

如果您单击第一个框,您将只会调用一次第一个处理程序和第二个处理程序,因为我们停止了在最里面的传播 div。两者仍然被调用,因为我们使用了 stopPropgation 而不是 stopImmediatePropagation。如果您单击第二个,则两个处理程序都将以正确的顺序为每个框 div 调用一次。


很遗憾,我找不到任何文档来支持这一点。