如何仅在单击子项时触发父项单击事件

新手上路,请多包涵

子项和父项都是可点击的(子项可以是带有 jQuery 单击事件的链接或 div)。当我点击子项时,如何只触发父级点击事件而不触发子级事件?

原文由 Mike Li 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 559
1 个回答

DOM 事件阶段

事件分为三个阶段:

  1. 捕获: 第一阶段是“捕获”,其中事件处理程序从 <window> 开始被调用,并通过后代向下移动到事件的目标。
  2. 目标: 第二个阶段是“目标”阶段,此时目标上的事件侦听器被调用。
  3. 冒泡: 第三个阶段是“冒泡”,首先处理程序监听被调用目标的父元素,然后逐渐监听该元素的祖先元素。

事件也有一个“默认动作”,它发生在冒泡阶段之后。默认操作是浏览器定义的操作,通常针对作为事件目标的元素类型的指定类型的事件发生(例如,浏览器导航到 href<a>click 上,而在另一种类型的元素上 click 将具有不同的默认操作)。

DOM Level 3 Events 草案 有一个图表,以图形方式显示事件如何通过 DOM 传播:

使用 DOM 事件流在 DOM 树中分派的事件的图形表示

图片版权 © 2016 万维网联盟,( 麻省理工学院ERCIM庆应义塾北航)。 http://www.w3.org/Consortium/Legal/2015/doc-license根据许可证 允许使用)

有关捕获和冒泡的更多信息,请参阅:“ 什么是事件冒泡和捕获? ”; DOM Level 3 事件草案;或 W3C DOM4:事件

防止事件传给孩子

对于您想要的,要在父级事件之前获取事件并防止事件发生在子级上,您必须在捕获阶段接收事件。一旦您在捕获阶段收到它,您必须阻止事件传播到 DOM 树中较低元素上的任何事件处理程序,或者已注册为在冒泡阶段侦听的事件处理程序(即元素/阶段上的所有侦听器将在你的听众之后被事件访问)。您可以通过调用 event.stopPropagation() 来执行此操作。

在捕获阶段接收事件

使用 addEventListener(type, listener[, useCapture]) 添加侦听器时,您可以将 useCapture 参数设为 true

引用 MDN:

[ useCapture is] 一个布尔值,表示 这种类型的事件在被分派到 DOM 树中它下面的任何 EventTarget 之前将被分派到注册的侦听器。通过树向上冒泡的事件不会触发指定使用捕获的侦听器。事件冒泡和捕获是传播发生在嵌套在另一个元素中的元素中的事件的两种方式,当两个元素都为该事件注册了句柄时。事件传播模式决定了元素接收事件的顺序。有关详细说明,请参阅 DOM Level 3 Events 和 JavaScript Event order。如果未指定, useCapture 默认为 false。

防止其他处理程序获取事件

  • event.preventDefault() is used to prevent the default action (eg prevent the browser from navigating to the href of an <a> upon a click ). [这在下面的示例中使用,但没有实际效果,因为文本没有默认操作。此处使用它是因为大多数情况下,当您添加单击事件处理程序时,您希望阻止默认操作。因此,养成这样做的习惯是个好主意,当你知道自己不想这样做时就不要这样做。]
  • event.stopPropagation() 用于防止在任何事件阶段之后的元素上的任何处理程序接收事件。它不会阻止调用 当前元素和阶段上的 任何其他处理程序。它不会阻止默认操作的发生。
  • event.stopImmediatePropagation() : 相同元素和阶段的处理程序按添加顺序调用。除了具有与 event.stopPropagation() 相同的效果外, event.stopImmediatePropagation() 还可以防止 同一元素和事件阶段上的 任何其他处理程序接收事件。它不会阻止默认操作的发生。鉴于此问题的要求是防止事件传播给孩子,我们不需要使用它,但可以这样做而不是使用 event.stopPropagation() 。但是请注意,同一元素上的侦听器是按添加顺序调用的。因此, event.stopImmediatePropagation() 不会阻止事件被那些在与您的侦听器相同的元素和阶段上的侦听器接收,这些侦听器是在您的侦听器之前添加的。

例子

在下面的示例中,事件侦听器被放置在父元素和子元素 <div> 元素上。只有放置在父级上的侦听器才能接收事件,因为它在子级之前的捕获阶段接收到事件并执行 event.stopPropagation()

 var parent=document.getElementById('parent');
var child=document.getElementById('child');
var preventChild=document.getElementById('preventChild');

parent.addEventListener('click',function(event){
    if(preventChild.checked) {
        event.stopPropagation();
    }
    event.preventDefault();
    var targetText;
    if(event.target === parent) {
        targetText='parent';
    }
    if(event.target === child) {
        targetText='child';
    }
    console.log('Click Detected in parent on ' + targetText);
},true);

child.addEventListener('click',function(event){
    console.log('Click Detected in child (bubbling phase)');
});

child.addEventListener('click',function(event){
    console.log('Click Detected in child (capture phase)');
},true);
 <input id="preventChild" type="checkbox" checked>Prevent child from getting event</input>
<div id="parent">Parent Text<br/>
  <div id="child" style="margin-left:10px;">Child Text<br/>
  </div>
</div>

查询

jQuery 不支持对事件使用捕获。有关原因的更多信息,请参阅:“ 为什么 jQuery 事件模型不支持事件捕获而只支持事件冒泡

原文由 Makyen 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题