【问题标题】:How to ONLY trigger parent click event when a child is clicked如何仅在单击子项时触发父单击事件
【发布时间】:2016-12-16 03:22:53
【问题描述】:

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

【问题讨论】:

  • 如果它永远不会触发,你为什么要在孩子身上设置一个事件?
  • 可以将子元素的点击事件绑定到父元素。
  • 这篇文章可能会有所帮助:css-tricks.com/return-false-and-prevent-default
  • 请考虑包含您的上下文示例,以帮助展示您想要实现的目标。
  • @LinusAronsson 提出了一个很好的观点。你会想看看事件冒泡。这样子事件可以对点击做出反应,但是通过将错误气泡返回给父事件,以便它也可以根据需要处理它。我建议您还包括showdev 提到的更多信息,因为没有具体示例,很难看出这可能需要如何发挥作用。

标签: javascript jquery css html


【解决方案1】:

DOM 事件阶段

事件分为三个阶段:

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

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

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


图片版权所有 © 2016 World Wide Web Consortium,(MITERCIMKeioBeihang)。 http://www.w3.org/Consortium/Legal/2015/doc-licensethe license 允许使用)

有关捕获和冒泡的更多信息,请参阅:“What is event bubbling and capturing?”; DOM Level 3 Events draft;或W3C DOM4: Events

防止事件影响孩子

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

在捕获阶段接收事件

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

引用 MDN:

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

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

  • event.preventDefault() 用于阻止默认操作(例如,阻止浏览器在click 上导航到<a>href)。 [这在下面的示例中使用,但没有实际效果,因为文本没有默认操作。之所以在这里使用它,是因为大多数情况下,当您添加单击事件处理程序时,您希望阻止默认操作。因此,养成这样做的习惯是个好主意,而当您知道自己不想这样做时就不要这样做。]
  • 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 不支持对事件使用捕获。有关原因的更多信息,请参阅:“Why does jQuery event model does not support event Capture and just supports event bubbling

【讨论】:

  • 这是一个很好的答案。问题的 OP 可能想要阅读并接受。
  • 如何使用 IE element['onclick'] = function(e) {}; 的捕获?
【解决方案2】:

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

#parentDiv * {
    pointer-events: none
}

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

【讨论】:

  • 有趣的解决方案 (+1)。但是,它确实有一个缺点,即阻止所有其他类型的指针事件,而不仅仅是点击,到达子级。
  • 这个解决方案很简单,非常适合我的需求,感谢您解决了我遇到的问题。
  • 知道这是否适用于移动 touch 事件吗?
  • @AnandRockzz 我很确定它确实如此,我已经在移动生产中使用过它,在移动设备上触摸被认为是一个“指针”
  • 太棒了,非常适合我的情况。
【解决方案3】:
  • 阻止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 的答案。)

【讨论】:

    【解决方案4】:

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

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


    这将启用父子祖先关系,并且将为父子节点调用相同的事件。
    要使事件仅被父级调用,请在事件处理程序中使用以下代码 sn-p。第一行

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

    【讨论】:

      【解决方案5】:

      您可以在元素上使用 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>

      【讨论】:

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