Why doesn't jquery: not () work, which I expect?

I am trying to set an event that fires when something WITHOUT a .four class. However, it fires when I click on objects with the .four class, although I use e.stopPropagation() .

 $("html").one("click", ":not(.four)", function(e){ e.stopPropagation(); console.log("Something without class 'four' was clicked that had class: " + $(e.srcElement).attr("class") ); }); 

( jsFiddle Demo )

This also does not work:

 $("html").not('.four').on("click", function(e){ 

Both outputs: Something without class 'four' was clicked that had class: four

I have a lot of problems with :not() , and I suspect that a lot of this may be relevant to my browser that supports CSS3 :not() , but I still can't figure out this simple problem.

+6
source share
4 answers

Your code:

 $("html").one("click", ":not(.four)", function(e){ e.stopPropagation(); // other code }); 

sets global event delegation for the click event type. This means that whenever a click event is fired on the any element on the page, jQuery checks to see if this element matches the provided selector - ":not(.four)" - and if that happens, jQuery will call a handler on that element.

This is what happens when you click on the .four element:

  • The initial element in which the click event is fired is obviously the .four element. jQuery checks to see if this element matches the ":not(.four)" selector. Since this is not the case, the handler is not called on this element.

  • Event events trigger a bubble in the DOM tree. Since the distribution of this click event has not yet been canceled, the event fires in the next element, which is the parent of the original element - the .two element in your demo. Again, jQuery checks to see if the element matches the selector. As it does, a handler is called on this element.

As you can see, your handler will be called even if you click on the .four element. To prevent code from executing when the .four element is .four , you should explicitly check inside the handler - basically what Jason's solution does.

+6
source

As Sime Vidas noted, this is the desired workaround:

 function doThisOnce(e) { e.stopPropagation(); if(!$(this).is(".four")){ console.log("Something without class 'four' was clicked that had class: " + $(e.srcElement).attr("class")); $(".one").addClass("pretty"); } else { // need to rebind the click event $(document).one("click", "*", doThisOnce); } } $(document).one("click", "*", doThisOnce); 
+2
source

Here is the solution I want to make. Place this listener with another:

 $("html").one("click", ".four", function(e){ e.stopPropagation(); }); $("html").one("click", function(e){ // other code }); 

This will prevent the spread on .four and β€œsteal” or β€œcatch” it from the bubbles to another listener. Perhaps it would be useful for the listener to "see" at a lower level than the other, depending on whether he bubbles up to one in front of the other.

See the jsFiddle demo , working finally!

+2
source

Here's another approach using the target event:

 $(document).one("click", function(e){ if( ! $(e.target).closest('.four').length ){ console.log("Something without class 'four' was clicked that had class: " + $(e.srcElement).attr("class") ); } }); 

closest() will match the children of the class, as well as the class element itself

0
source

All Articles