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

Mik*_* Li 21 javascript css jquery html5

子和父都可以点击(子可以是jQuery点击事件的链接或div).当我点击child时,我如何只触发父点击事件而不触发子事件?

Mak*_*yen 77

DOM事件阶段

活动分为三个阶段:

  1. 捕获:第一阶段是"捕获",其中事件处理程序从后面开始调用<window>并向下移动到事件的目标.
  2. 目标:当调用目标上的事件侦听器时,第二阶段是"目标"阶段.
  3. 冒泡:第三阶段是"冒泡",首先是处理者首先调用目标的父节点,然后逐步调用该元素的祖先.

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

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

使用DOM事件流在DOM树中调度的事件的图形表示
图像版权所有©2016 World Wide Web Consortium,(麻省理工学院,ERCIM,Keio,北京).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是]一个布尔值,指示在将此类型的事件分派到DOM树中位于其下的任何EventTarget之前,将将其分派给已注册的侦听器.向上冒泡到树中的事件不会触发指定使用捕获的侦听器.事件冒泡和捕获是传播发生在被嵌套在另一个元件之内,当两个元件已经注册了该事件的手柄元件的事件的方法有两种.事件传播模式确定元素接收事件的顺序.有关详细说明,请参阅DOM Level 3事件和JavaScript事件顺序.如果未指定,则useCapture默认为false.

阻止其他处理程序获得该事件

  • event.preventDefault()用于防止默认动作(例如防止浏览器导航到href<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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

jQuery的

jQuery不支持在事件上使用捕获.有关为什么看到的更多信息:" 为什么jQuery事件模型不支持事件Capture并且只支持事件冒泡 "

  • 这是一个很好的答案。问题的 OP 可能想要阅读并接受。 (3认同)

Abr*_*kes 17

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

#parentDiv * {
    pointer-events: none
}
Run Code Online (Sandbox Code Playgroud)

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

  • 有趣的解决方案(+1)。然而,它确实有一个缺点,即阻止所有其他类型的指针事件(而不仅仅是单击)到达子级。 (2认同)

yay*_*aya 6

  • 阻止children接收父级的点击事件:
parent.addEventListener('click',function(e){
  e.stopPropagation();
  console.log('event on parent!')
},true);
Run Code Online (Sandbox Code Playgroud)

(注意第二个参数是true

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

(有关详细说明,请参阅@Makyen 的答案。)