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

时间:2016-08-04 17:18:50

标签: javascript jquery events event-delegation

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

基于https://learn.jquery.com/events/event-delegation/并参考下面的HTML和JS,点击div-4会触发所有祖先(div-one,div-two,div-three,body)上的点击事件。我将事件处理程序放在body上,选择:not(#div-three)

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

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

  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
  9. 如果在事件处理程序中调用了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
      });
    });
    

1 个答案:

答案 0 :(得分:1)

这发生在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。如果单击第二个,则两个处理程序将按正确的顺序为每个box div调用一次。

很遗憾,我无法找到任何支持此文档的文档。