委托事件不能与:not()选择器结合使用

时间:2015-03-17 18:32:08

标签: javascript jquery

除了某个元素外,我想对所有点击做一些事情。

我创建了一个非常简单的示例来演示此问题:http://jsfiddle.net/nhe6wk77/

我的代码:

$('body').on('click', ':not(a)', function () {
    // do stuff
});

我希望忽略<a>上的所有点击,但事实并非如此。

我做错了什么,或者这是jQuery方面的错误?

4 个答案:

答案 0 :(得分:7)

不,这不是错误,而是预期的行为。

事件一直在冒泡。通过单击a节点,您仍然可以从div节点触发 parent 事件。

阅读more about event bubbling in the W3C DOM Specification。只需搜索“泡沫”。

您需要停止a节点的事件传播。即:

$('body').on('click', ':not(a)', function () {
    // do something effectively
    alert('you should not see me when clicking a link');
});
$("a").click(function( event ) {
    // do nothing effectively, but stop event bubbling
    event.stopPropagation();
});

JSFiddle:http://jsfiddle.net/nhe6wk77/6/

答案 1 :(得分:6)

该代码中有很多内容并不明显。最重要的是,click事件实际上附加到body元素。由于该元素不是锚点,因此您将始终获得警报。 (事件委托之所以有效,是因为click事件从a到其所有祖先(包括body)一直冒泡,直到达到document。)

您要做的是检查event.target。这将告诉您实际点击的元素,但实际的click事件仍然绑定到body元素:

$('body').on('click', function (e) { // e = event object
    if ($(e.target).is(':not(a)')) {
        alert('got a click');
    }
});

http://jsfiddle.net/y3kx19z7/

答案 2 :(得分:5)

它按预期工作,这就是原因!

:not()选择器的使用在委托事件中受到尊重,但这是一种不常见的做法,因为事件冒泡的DOM树可能会在此过程中多次触发处理程序。

jQuery API Documentation表示:

  

jQuery将事件从事件目标起泡到附加处理程序的元素(即最里面到最外层的元素),并为该路径上与选择器匹配的任何元素运行处理程序。

注意短语“并为该路径上与选择器匹配的任何元素运行处理程序”。

在您的示例中,jQuery准确地没有在a元素上运行处理程序,但是当事件冒泡树时,它会为匹配:not(a)的任何元素运行处理程序,路径中的其他元素。

以下是一个显示其工作原理的明显示例:http://jsfiddle.net/gfullam/5mug7p2m/

$('body').on('click', ':not(a)', function (e) {
    alert($(this).text());
});
<div class="outer">
    <div class="inner">
        <a href="#">Click once, trigger twice</a>
    </div>
</div>

<div class="outer">
    <div class="inner">
        <button type="button">Click once, trigger thrice</button>
    </div>
</div>

单击嵌套div的第一个块中的链接,将启动事件冒泡,但单击的a元素 - 也就是事件target - 不会触发处理程序,因为它没有' t匹配:not(a)选择器。

但是当事件通过DOM冒泡时,它的每个父节点 - 事件currentTarget - 触发处理程序,因为它们匹配:not(a)选择器,导致处理程序运行两次。多次触发是需要注意的事项,因为它可能不是理想的结果。

同样,单击嵌套div的第二个块中的按钮将启动事件冒泡,但这次事件target:not(a)选择器匹配,因此它会立即触发处理程序。然后当事件冒泡时,每个匹配选择器的父项也会触发处理程序,导致处理程序运行三次。

正如其他人所建议的那样,您需要绑定一个替代处理程序来停止a点击事件的传播,或者检查事件target对照处理程序中的:not(a)选择器而不是委托选择器。

答案 3 :(得分:3)

$("body").click(function(e) {
    if($(e.target).is('a')){
        e.preventDefault();
        return;
    }
    alert("woohoo!");
}); 

检查点击的目标。这样你就不需要绑定另一个事件了。

updated fiddle