Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't the jquery :not() selector working as I expect it to?

I'm trying to set an event that fires when anything WITHOUT the .four class is clicked. However, it fires when things with the .four class are clicked, even though I'm using 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 does not work either:

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

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

I'm having tons of trouble with :not() and I suspect a lot of it might have to do with my browser supporting CSS3 :not() now, but I still can't understand this simple issue.

like image 832
brentonstrine Avatar asked Jan 01 '13 01:01

brentonstrine


2 Answers

Your code:

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

sets up global event delegation for the click event type. This means that whenever a click event is triggered at any element on the page, jQuery will check if that element matches the provided selector - ":not(.four)" - and if it does, jQuery will invoke the handler on that element.

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

  1. The original element at which the click event is triggered is obviously the .four element. jQuery checks if that element matches the ":not(.four)" selector. Since it doesn't, the handler is not invoked on that element.

  2. Click events bubble up the DOM tree. As propagation for this click event has not been canceled yet, the event triggers at the next element, which is the parent of the original element - the .two element in your demo. Again, jQuery checks if the element matches the selector. Since it does, the handler is invoked on that element.

As you can see, your handler will be invoked even if you click on a .four element. In order to prevent the code from being executed when a .four element is clicked, you have to explicitly check within the handler - basically what Jason's solution does.

like image 112
Šime Vidas Avatar answered Sep 30 '22 19:09

Šime Vidas


As Šime Vidas pointed out, 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);
like image 29
Jason Whitted Avatar answered Sep 30 '22 21:09

Jason Whitted