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

IT技术 javascript jquery css html
2021-03-02 00:05:42

child 和 parent 都是可点击的(child 可以是带有 jQ​​uery 单击事件的链接或 div)。当我点击孩子时,如何只触发父点击事件而不触发子事件?

5个回答

DOM 事件阶段

活动分为三个阶段:

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

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

DOM级别3事件草案有一个图,以图形方式显示事件通过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:

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

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

  • event.preventDefault()用于防止默认动作(例如防止浏览器导航到href<a>在一个click)。[这在下面的示例中使用,但没有实际效果,因为文本没有默认操作。在这里使用它是因为在大多数情况下,当您添加单击事件处理程序时,您希望阻止默认操作。因此,养成这样做的习惯是个好主意,只是在您知道不想这样做时才这样做。]
  • event.stopPropagation()用于防止稍后在任何事件阶段中元素上的任何处理程序接收事件。它不会阻止调用当前元素和阶段上的任何其他处理程序它不会阻止默认操作的发生。
  • event.stopImmediatePropagation(): 相同元素和阶段上的处理程序按添加顺序调用。除了与 具有相同的效果之外event.stopPropagation(),还event.stopImmediatePropagation()可以防止同一元素和事件阶段上的任何其他处理程序接收事件。它不会阻止默认操作的发生。鉴于此问题的要求是防止事件传播给孩子,我们不需要使用 this,但可以这样做而不是使用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 不支持对事件使用捕获。有关原因的更多信息,请参阅:“为什么 jQuery 事件模型不支持事件捕获而只支持事件冒泡

这是一个很好的答案。问题的 OP 可能想要阅读并接受。
2021-05-06 00:05:42
你如何使用 IE 的捕获element['onclick'] = function(e) {};
2021-05-08 00:05:42

当您知道没有任何子元素是可交互的时,在某些情况下可能有用的另一个选项是pointer-events: none在您的 css ( link ) 中设置。我通常将它应用于我想要捕获交互的元素的所有子元素。像这样:

#parentDiv * {
    pointer-events: none
}

请注意*,声明该规则适用于 的所有子项parentDiv

有趣的解决方案(+1)。但是,它确实有一个缺点,即阻止所有其他类型的指针事件(不仅仅是点击)到达子级。
2021-04-18 00:05:42
@AnandRockzz 我很确定它确实如此,我已经在移动生产中使用过它,并且在移动设备上,触摸被认为是“指针”
2021-04-18 00:05:42
知道这是否适用于移动触摸事件吗?
2021-05-07 00:05:42
这个解决方案很简单,非常适合我的需求,感谢您解决了我遇到的问题。
2021-05-14 00:05:42
太棒了,非常适合我的情况。
2021-05-16 00:05:42
  • 防止children接收父级的点击事件:
parent.addEventListener('click',function(e){
  e.stopPropagation();
  console.log('event on parent!')
},true);

(注意第二个参数是true

  • 防止parent接收自身或其子级的点击事件:
parent.addEventListener('click',function(e){
  e.stopPropagation();
  console.log('event on parent or childs!', e.target.closest('.parent_selector'))
});
  • e.stopPropagation 意味着停止层次结构中的下一个来接收事件。
  • 第二个参数 ( useCapture) 是一个标志,表示颠倒接收事件的顺序。(使用capture阶段而不是bubble阶段。)。这意味着如果您将其设置为 true,则父级将收到单击事件,然后是子级。(通常孩子会先得到事件。)

(有关详细解释,请参阅@Makyen 的回答。)

为了让这里的生活变得非常简单和轻松,我在
与此类似的父节点上使用

target_image.addEventListener('drop',dropimage,true);


这将启用父子祖先关系,并且将为父子关系调用相同的事件。
要使事件只为父级调用,请在事件处理程序中使用以下代码片段。第一行

event.stopPropagation();
event.preventDefault();

您可以在元素上使用 CustomEvents 属性。

  • 创建一个事件对象,让子元素将事件分派给它的父元素

在这里看演示

document.getElementById('parent').onclick = function() {
  alert("you are clicking on the parent stop it");
}
document.getElementById('child').onclick = function(e) {
  alert('I am sending this event to my parent');
  event = new CustomEvent('click');
  document.getElementById('parent').dispatchEvent(event);

}
#parent {
  display: inline-block;
  width: 100px;
  height: 100px;
  border: solid black;
}

#child {
  border: solid red;
}
<div id=parent>
  <div id=child>I am a child</div>
</div>

示例中的 event.target 引导我更多地研究“事件”,非常足智多谋的答案!
2021-04-23 00:05:42
同意你的第二条评论,但不是你的第一条...... OP 问题的标题指出“当点击孩子时如何只触发父点击事件”..用户点击触发父的孩子......我是知道您发布的答案……您的方法很受欢迎(即使我使用),但我只是在说明另一种方法
2021-04-27 00:05:42
我同意标题没有那么具体。但是,我引用的内容直接来自问题。请参阅问题中的第二个也是最后一个句子。您确实提出了一种替代方法,这在两个元素没有祖先<->后代关系的情况下可能很有用。
2021-05-05 00:05:42
该问题专门询问“仅触发父点击事件而不是子事件”。您这样做的方式,仍然会触发子级上的事件(并且还会调用任何其他侦听器)。
2021-05-11 00:05:42
此外,除非您完全控制页面的 HTML 和所有JavaScript ,否则使用该element.onclick属性是一个坏主意。分配给该属性会取代任何已经以这种方式定义的侦听器(该属性/属性中只能存在一个)。这可能会导致您无法完全控制的页面中断。在 JavaScript 中,使用addEventListener().
2021-05-13 00:05:42