Reputation: 1265
HTML:
<ul>
<li>
<a href="http://wp.pl">wp.pl</a>
</li>
</ul>
Javascript:
$(document).ready(function() {
$body = $('body');
$body.on('click', 'a', function(event) {
event.preventDefault();
event.stopPropagation();
/**
* Causes the "This also..." never shows, but "Why is..." still appears
*/
//event.stopImmediatePropagation();
alert('Should be seen always');
});
$body.on('click', 'a', function(event) {
alert('This also should be seen - same level as "a"');
});
$body.on('click', 'li', function() {
alert('This never shows - and that is ok - because is under "a"');
});
$body.find('ul').on('click', 'li', function() {
alert('Why is this shown?');
});
});
Fiddled here: http://jsfiddle.net/g3PEr/1/
The question is: why click
event set on element found by find()
fires even when its child element has stopPropagation()
or stopImmediatePropagation()
?
Please read accepted answer and its comments to know why is this happening.
Upvotes: 3
Views: 2070
Reputation: 140228
It doesn't really matter if you specify "a"
or "li"
in the second argument - the event handler is attached to "body"
. The only thing the selector does is acting as a filter. The event handler won't be fired when none of the elements on the propagation path matched the filter "a"
. And that's it.
So consider your code as:
$body.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
$body.on('click', function(event) {
});
$body.on('click', function() {
});
$("ul").on('click', function() {
});
Since bubbling starts at the bottom, the "ul"
handler is fired first. Then comes the body handler, and they fire in the order the events were attached since they are all on body.
.stopImmediatePropagation()
can be used to stop propagation to handlers that are on the same level as well as any upper levels.
The upper levels of "body"
are still document
and window
(window being at the top of everything), but because you have no handlers there you will not observe that stopPropagation()
has any effect.
Another way to explain it that calling $("body").on( "click", "li", fn );
is the same as doing:
$("body").on( "click", function(e) {
var currentTarget = $(e.target).closest("li");
if( currentTarget.size() > 0 ) {
e.currentTarget = currentTarget;
fn.call(currentTarget, e);
}
});
Upvotes: 2
Reputation: 10658
You are using delegated event listeners so the event handlers are attached to the delegated target, not the descendant.
The first three events are bound to the body
element and the last event is bound to the ul
element, which is a child of the body
. So the handler on the ul
element would fire first because the event hasn't bubbled to the body
yet. Once the event reaches the body
the propagation will be stopped (if an a
tag was clicked).
Upvotes: 0
Reputation: 388336
you are using delegated event registration to register the event handler, it makes use of event bubbling to register the event handlers means when an event happens in an element it will get bubbled upto all the ancestor elements till it reaches the document object.
in your case when you click on a
, it triggers click event for the element a
, then it gets propagated to li
then to ul
where you have a click handler registerd it gets fired, then it reaches the body
, html
and last to document
object where you have the other click handlers registered so those also gets executed where you are stopping the propagation but already the events attached to ul
has got fired.
the li
handler associated with body
is not fired because the propagation is prevented from the a
handler in body
Upvotes: 0