Red Star Red Star - 3 months ago 16
jQuery Question

Delegated event binding in jQuery

Can someone please explain why this doesn't work

$(document).ready(function(){
$(document).on('click', '.myClass', function(){
// ...
});
});


Also this doesn't work:

$(document).ready(function(){
$('body').on('click', '.myClass', function(){
// ...
});
});


Only this works:

$(document).ready(function(){
$('.myClass').click(function(){
// ...
});
});


But I need to bind every time for dynamic event handling.
So please, can you explain why the first two don't work?

(Note: it doesn't work with
$('.delegetor')
neither (i.e. a wrapper div) I tried
body
and
document
just to make sure the delegator is static)

EDIT

From what I understood from the first answer:

<div id="parent">
<div id="child">
<div id="grandchild">The one</div>
</div>
</div>


If I do

$('#child').click(function(){return false;});
$('#parent').on('click', '#grandchild', function(){ alert("hi");});


The click event won't reach grandchild because it stops in child ? (I don't think I understood this right)

Answer

Event delegation works by adding the event listener to the static container element that you bind the event to. It depends on the event bubbling up from the target element to the container element. When the container element receives the event, jQuery checks whether the target matches the selector, and then executes the callback function.

If you have a handler for an element that's between the target and the container, and it calls event.stopPropagation(), that stops the event from bubbling any further. So the event never reaches the container, and the delegation fails.

Note that if a jQuery event handler returns false, it's equivalent to calling:

event.preventDefault();
event.stopPropagation();

If you only need to do one or the other, you should do it explicitly in the handler, rather than returning false.

You need to bind the event to a static element as close in the DOM hierarchy as possible to the target element, and ensure that you don't have any intervening elements with click handlers that stop propagation.

$('.myClass').click(function() {
    ...
});

isn't affected by this because it adds the event listener directly to the .myClass elements, not a container. So it doesn't depend on the event bubbling to a container element. There would be a problem if .myClass had a child with a click handler that stops propagation -- when you click on that child, .myClass wouldn't get the event. Event delegation would also be blocked by this.

Note that if a jQuery event handler returns false, it's equivalent to calling:

event.preventDefault();
event.stopPropagation();

So in your example, since you return false in the #child handler, it prevents the event from bubbling out to #parent, so the event delegation from #parent to #grandchild fails.

If you only need to do one or the other, you should do it explicitly in the handler, rather than returning false.

You would need to change your design to something like:

<div id="parent">
  <div id="child">
    <div id="intermediary">
        <div id="grandchild">The one</div>
    </div>
  </div>
</div>

Then you could do:

$("#intermediary").on("click", ".grandchild", function() {
    ...
});

This would not be affected by the #child handler.