Reputation: 146490
I'm pretty confused by the :not()
selector. In plain CSS is seems to be rather straightforward:
section { /* Overriden as expected */
color: red;
}
input {
color: green; /* In effect as expected */
}
:not(input) {
color: blue; /* In effect as expected */
}
<section>
<p>Lorem ipsum</p>
<input value="Lorem ipsum">
</section>
However, when applied to filter the descendants of the selected elements that trigger an event I'm unable to grasp the logic:
jQuery(function($){
$(document).on("keydown", "input", function(event){
// This fires only for <input> as expected
console.log("Event handler #1 on", event.target);
});
$(document).on("keydown", ":not(input)", function(event){
// This fires for *all* elements :-?
console.log("Event handler #2 on", event.target);
// ... even though these checks return the results that intuition suggests
console.log('Is "input"? %s; Is ":not(input)"? %s',
$(event.target).is("input"),
$(event.target).is(":not(input)")
);
});
$(document).on("keydown", "section :not(input)", function(event){
// This *never* fires :-?
console.log("Event handler #3 on", event.target);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<p>Click and type here</p>
<input value="Click and type here">
</section>
What's the rationale behind the way :not()
works here?
I'm really looking for an explanation as opposed to a fix.
Upvotes: 4
Views: 56
Reputation: 370989
The issue is that the keydown
event bubbles up from the input
. If you use :not(input)
, the handler will not fire when the event was just initialized at the input
element, but it will fire when the event bubbles up to the section
element. You can check this by checking this
inside the function, which will refer to the element to which the event has bubbled when the handler fires. (The event.target
will always be the input
when you're typing in the input field)
jQuery(function($){
$(document).on("keydown", ":not(input)", function(event){
// This fires for *all* elements :-?
console.log("Event handler #2 on", this);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<p>Click and type here</p>
<input value="Click and type here">
</section>
If you continue adding :not
s, you'll see it bubble up all the way up to the HTML tag:
jQuery(function($){
$(document).on("keydown", ":not(input):not(section):not(body)", function(event){
// This fires for *all* elements :-?
console.log("Event handler #2 on", this);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<p>Click and type here</p>
<input value="Click and type here">
</section>
I suppose you could use :not(input):not(section):not(body):not(html)
, but that's a bit silly and hard to manage.
Your third handler is properly excluding the input
from firing the event, but only input
(and similar) elements fire keydown
events - it can't be fired from a <section>
, for example. It might be clearer if there's a textarea child of the section
as well as the input - you'll see that the textarea triggers the handler, but the input doesn't:
$(document).on("keydown", "section :not(input)", function(event) {
console.log("Event handler #3 on", event.target);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<p>Click and type here</p>
<input value="Click and type here">
<textarea></textarea>
</section>
Upvotes: 5