如何防止在单击子链接时触发父元素的onclick事件?

510
我正在使用jQuery使一个div可点击,在这个div中还有锚点。我遇到的问题是,当我点击一个锚点时,两个点击事件都会触发(对于div和锚点)。如何防止在单击锚点时触发div的onclick事件?
以下是错误的代码:
JavaScript
var url = $("#clickable a").attr("href");

$("#clickable").click(function() {
    window.location = url;
    return true;
})

HTML

<div id="clickable">
    <!-- Other content. -->
    <a href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>
26个回答

629
事件会冒泡到DOM中已附加点击事件的最高点。因此,在您的示例中,即使div中没有其他明确可点击的元素,div的每个子元素都会将其点击事件冒泡到DOM,直到DIV的点击事件处理程序捕获它。
有两种解决方法是检查实际上是谁发起了事件。jQuery会传递一个eventargs对象以及事件:
$("#clickable").click(function(e) {
    var senderElement = e.target;
    // Check if sender is the <div> element e.g.
    // if($(e.target).is("div")) {
    window.location = url;
    return true;
});

您还可以将点击事件处理程序附加到链接,告诉它们在其自己的处理程序执行后停止事件冒泡

$("#clickable a").click(function(e) {
   // Do something
   e.stopPropagation();
});

5
好的回答。很高兴知道还有其他选择。 - Jonathon Watney
9
+1!使用stopPropagation附加点击处理程序是一个非常好的技巧,谢谢! - Thomas
3
如果您有一些需要防止传播的元素,您可以找到它们的父元素,并通过防止其冒泡来防止传播。因此,不必拦截div中的所有a链接,只需拦截div本身的点击事件并防止它向上传递,就可以完成操作。 - sucitivel
38
在父元素中使用if(e.target !== this) return;比在子元素中使用e.stopPropagation()更好,因为你永远不知道其他人是否会附加一些处理程序到子元素上,或者某个库是否必须将处理程序附加到子元素上(你不想干扰库的代码)。这是更好的分离责任的方法。 - UTF_or_Death
10
if( e.target !== this) return;这一检查放在父元素中意味着,如果该父元素中的任何其他子元素被点击且它们本身没有onClick处理程序,则不会发生任何操作。因此,在具有可点击父 div 的情况下,它是无用的。但e.stopPropagation()则可以正常工作。 - derf26
显示剩余8条评论

148

使用stopPropagation方法,以下是示例:

$("#clickable a").click(function(e) {
   e.stopPropagation();
});

如jQuery文档所述:

stopPropagation方法阻止事件冒泡到DOM树上,防止任何父级处理程序被通知该事件。

请注意,它不会阻止其他侦听器处理此事件(例如为按钮设置多个点击处理程序),如果这不是期望的效果,则必须使用stopImmediatePropagation代替。


小心不要意外地将事件监听器设置为被动。 - Gabriel Petersson
1
尽一切可能避免使用Event.stopPropagation()。一个应用程序(或第三方代码)绝对不应该停止或阻止事件在层级、组件之间传播。相反,应该检查Event.target.closest("selector")是否匹配,并相应地采取行动。示例 - undefined
尽量避免使用 Event.stopPropagation()。应用程序(或第三方代码)永远不应该停止或阻止事件在层和组件之间传播。相反,应检查 Event.target.closest("selector") 是否匹配并采取相应措施。示例 - Roko C. Buljan

84

这是我为所有需要纯 JavaScript 代码(非 jQuery)的人提供的解决方案

document.getElementById("clickable").addEventListener("click", function(e) {
    e = window.event || e; 
    if(this === e.target) {
      // put your code here
    }
});

如果单击父元素的子元素,您的代码将不会被执行。


1
这对我有用,我需要一个非JS / jQuery解决方案。 十分感谢! - L A
5
好的答案。我直接传递了事件,因此没有使用MDN不建议使用的window.event:https://developer.mozilla.org/en-US/docs/Web/API/Window/event。如果您有时间,我希望您可以评论一下为什么您使用了`window.event`(也许这是2015年存在的问题或者我没有理解原因)。 - Rob Monhemius
它不起作用,我已经测试过了。 - showtime
1
如果(this !== e.target)返回;比正面形式更好,因为它避免了嵌套,并且使得当当前情况与我们无关时更清晰地停止。 - zakmck

36
如果您不打算与内部元素进行任何交互,那么使用 CSS 方案可能对您有用。只需将内部元素设置为 pointer-events: none 即可。在您的情况下:
.clickable > a {
    pointer-events: none;
}

或者一般地针对所有内部元素:

.clickable * {
    pointer-events: none;
}

使用这个简单的技巧,在开发ReactJS时为我节省了很多时间。

浏览器支持可以在这里找到:http://caniuse.com/#feat=pointer-events


如果您想在子元素中拥有其他事件监听器怎么办?您的方法将阻止您这样做。 - showtime
如答案所述,这是“如果您在任何情况下都不打算与内部元素进行交互”的情况。否则,您必须寻找其他解决方案。 - Hooman Askari

28

内联替代方案:

<div>
  <!-- Other content. -->
  <a onclick='event.stopPropagation();' href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>

18

你也可以尝试这个

$("#clickable").click(function(event) {
    var senderElementName = event.target.tagName.toLowerCase();
    if(senderElementName === 'div') {
        // Do something here 
    } else {
        // Do something with <a> tag
    }
});

11

如果有人需要写作(对我有用):

event.stopImmediatePropagation()

来自这个解决方案。


10

this不可用时(React等),我会将其与ev.currentTarget进行比较。

$("#clickable").click(function(e) {
    if (e.target === e.currentTarget) {
        window.location = url;
        return true;
    }
})

9

使用return false;e.stopPropogation();将不允许进一步的代码执行。它会在此处停止流程。


是的,这很重要 - 我自己也遇到过这个问题。但上面 Rex 的答案很有帮助 - 可以获取被点击的元素,并在某些情况下将其用于您试图停止的逻辑中。.target.nodeName 也很有帮助,可以清楚地了解到被点击的是什么。 - Watercayman

8

如果可点击的div中有多个元素,则应该这样做:

$('#clickable *').click(function(e){ e.stopPropagation(); });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接