如何使点击事件仅在父级DIV上触发,而不是子元素?

283

我有一个带有类名为foobar的DIV,这个DIV里面有几个没有类名的DIV,但我认为它们会继承foobar类:

$('.foobar').on('click', function() { /*...do stuff...*/ });

我希望只有在DIV区域内但不是其子DIV内点击时才触发该事件。

12个回答

520
如果e.targetthis相同,则表示您没有单击后代元素。

$('.foobar').on('click', function(e) {
  if (e.target !== this)
    return;
  
  alert( 'clicked the foobar' );
});
.foobar {
  padding: 20px; background: yellow;
}
span {
  background: blue; color: white; padding: 8px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class='foobar'> .foobar (alert) 
  <span>child (no alert)</span>
</div>


3
嗨,@vicky。顺便提一下,JavaScript 有两种不同类型的值相等比较方式。"===" 或 "!==" 使用 "严格相等比较算法",而 "==" 和 "!=" 则使用 "抽象相等比较算法", 这是强制类型转换的。通常建议总是使用严格比较,除非有特定需要使用强制类型比较 (因为其规则稍微复杂)。在这种情况下,由于对象被比较,真的没有区别,但你会发现大多数人仍然坚持使用严格比较。 - user1106925
1
@vicky:没关系,还是正确的。只是提醒你JS有不同的运算符。;-) - user1106925
1
@GauravAggarwal:首先使用if语句的目的是使得在点击.foobar元素的子元素时,下面的代码不会运行。如果我们将警告放在顶部,它将始终运行。因此,基本上,当单击外部黄色区域时,您应该看到警报,但不应在蓝色子元素上看到。 - user1106925
3
这个答案很好,因为它也适用于原生JavaScript的addEventListener,并不仅限于jQuery。 - Michael Giovanni Pumo
1
如何在原生JS中实现这个? - Irvan Hilmi
显示剩余8条评论

124

我没有成功地得到被接受的答案,但是这似乎可以解决问题,至少在纯JS中。

if(e.target !== e.currentTarget) return;

8
e.currentTarget比引用this更准确,因为指向this的对象可能会根据调用它的作用域和上下文而发生变化。 - eballeste
5
这里似乎最干净的方法是使用 event.currentTarget - Mark Carpenter Jr
4
请注意,您还可以使用以下代码: if (e.target === e.currentTarget) { ...your code }为什么这个代码行得通: e.target是触发事件的元素(例如,用户点击的元素) e.currentTarget是事件监听器所附加到的元素。因此,当您比较这两个元素时,事件附加的元素与被单击的元素相同。 - Guilherme Samuel
谢谢,这个解决方案完美地解决了我的问题。我观察到的是,当你记录“e”并打开记录的对象时,currentTarget为空,但当你记录“e.currentTarget”时,它正确地记录了元素。我很困惑为什么会这样? - Akash Kumar Seth
我得到了答案:“因为事件对象的e.currentTarget属性在传播过程中会发生变化”。 - Akash Kumar Seth
1
肯定是最佳答案 - Cybernetic

111

如果你不介意只针对新版浏览器,还有另一种方法可行。只需添加CSS即可。

pointer-events: none;

要捕获点击事件,可以向想要捕获的 div 的任何子元素添加。以下是支持表格

http://caniuse.com/#feat=pointer-events


33
我知道我应该避免写“谢谢”评论,但是我可以亲吻你的脚来表达感激之情 :-) - Simona Adriani
@SimonaAdriani 嗯? - n3wb
谢谢,我也是用同样的方法,但我在编写单元测试时遇到了困难。我该如何确保 click 的编程点击事件触发被阻止? - Pankaj Parkar
这不是正确的。您仍然可能希望在子元素上允许事件,而不是在父元素上。例如,父级可能具有mouseover事件,您不希望在作为输入的子元素上使用该事件,但显然您想要使用该输入。 - Cybernetic
1
@Cybernetic - 在这种情况下,您可以在父元素上处理点击(或聚焦)事件,然后使用input.focus()聚焦输入框,但无论如何,对于这个问题有多种解决方案,某人使用哪一个将基于他们的用例。 - hobberwickey

37

您可以利用冒泡事件:

$('.foobar').on('click', function(e) {
    // do your thing.
}).on('click', 'div', function(e) {
    // clicked on descendant div
    e.stopPropagation();
});

这看起来是我特定场景的一个不错解决方案,因为我只想排除对后代<a>元素的点击。只有一个问题:当我在它们上面中键单击时,事件仍然会触发(在Google Chrome中测试)。是否有一种变体也可以防止这种情况的发生? - cgogolin
@cgogolin,请看一下这个链接:https://stackoverflow.com/questions/12197122/how-can-i-prevent-a-user-from-middle-clicking-a-link-with-javascript-or-jquery - Jessica

23
//bind `click` event handler to the `.foobar` element(s) to do work,
//then find the children of all the `.foobar` element(s)
//and bind a `click` event handler to them that stops the propagation of the event
$('.foobar').on('click', function () { ... }).children().on('click', function (event) {
    event.stopPropagation();
    //you can also use `return false;` which is the same as `event.preventDefault()` and `event.stopPropagation()` all in one (in a jQuery event handler)
});

这将停止在.foobar元素的任何子元素上冒泡click事件,因此事件不会到达.foobar元素来触发其事件处理程序。

这里是演示:http://jsfiddle.net/bQQJP/


6

我曾经遇到同样的问题,根据其他答案,我想出了这个解决方案。

 $( ".newsletter_background" ).click(function(e) {
    if (e.target == this) {
        $(".newsletter_background").hide();
    } 
});

基本上,它表示如果目标是 div,则运行代码,否则不执行任何操作(不隐藏它)。

你可以在这里使用箭头函数来代替(function(e) {})吗? - Zaffer

5
$(".advanced ul li").live('click',function(e){
    if(e.target != this) return;
    //code
    // this code will execute only when you click to li and not to a child
})

3
您可以使用 event.currentTarget。它仅对接收到事件的元素进行点击事件。

target = e => {
    console.log(e.currentTarget);
  };
<ul onClick={target} className="folder">
      <li>
        <p>
          <i className="fas fa-folder" />
        </p>
      </li>
    </ul>


currentTarget并不决定事件是否被执行,它只是保存元素的引用。 - shaedrich

2
如果您无法使用pointer-events: none;并且目标是现代浏览器,您可以使用composedPath来检测对象上的直接点击,如下所示:

如果您无法使用pointer-events: none;并且目标是现代浏览器,您可以使用composedPath来检测对象上的直接点击,如下所示:
element.addEventListener("click", function (ev) {
    if (ev.composedPath()[0] === this) {
        // your code here ...
    }
})

你可以在这里阅读更多关于composedPath的信息:https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath。最初的回答。

1

我的情况类似,但是这种情况是当你有几个foobar时,并且你只想通过一次点击关闭一个:

找到父级案例

$(".foobar-close-button-class").on("click", function () {
    $(this).parents('.foobar').fadeOut( 100 );
    // 'this' - means that you finding some parent class from '.foobar-close-button-class'
    // '.parents' -means that you finding parent class with name '.foobar'
});

找到子案例
$(".foobar-close-button-class").on("click", function () {
    $(this).child('.foobar-close-button-child-class').fadeOut( 100 );
    // 'this' - means that you finding some child class from '.foobar-close-button-class'
    // '.child' -means that you finding child class with name '.foobar-close-button-child-class'
});

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