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

Dan*_*Apt 9 javascript jquery

除了特定元素之外,我想对所有点击做一些事情.

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

我的代码:

$('body').on('click', ':not(a)', function () {
    // do stuff
});
Run Code Online (Sandbox Code Playgroud)

我希望所有点击都<a>被忽略,但事实并非如此.

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

Kai*_*aii 7

不,这不是一个错误,而是预期的行为.

事件一直在冒泡.通过单击a节点,您仍然会从节点触发它的div节点事件.

阅读有关W3C DOM规范中事件冒泡的更多信息.只是寻找"泡沫".

您需要停止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();
});
Run Code Online (Sandbox Code Playgroud)

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


Bla*_*ger 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');
    }
});
Run Code Online (Sandbox Code Playgroud)

http://jsfiddle.net/y3kx19z7/


gfu*_*lam 5

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

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

jQuery的API文档指出:

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

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

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

这是一个明显的例子,展示了它是如何工作的:http://jsfiddle.net/gfullam/5mug7p2m/

$('body').on('click', ':not(a)', function (e) {
    alert($(this).text());
});
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 这是所有答案中最好的,因为它在如此详细的解释中明确了为什么它实际上有效,以及如何.+1 (2认同)