172

I am trying to understand this particular difference between the direct and delegated event handlers using the jQuery .on() method. Specifically, the last sentence in this paragraph:

When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.

What does it mean by "runs the handler for any elements"? I made a test page to experiment with the concept. But both following constructs lead to the same behavior:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

or,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

Maybe someone could refer to a different example to clarify this point? Thanks.

3
  • 7
    For all interested: jsperf.com/jquery-fn-on-delegate-vs-direct
    – dgo
    Commented Apr 7, 2015 at 16:25
  • 1
    @KevinWheeler I commented on your fiddle below but here, essentially it isn't set up correctly (you are binding to the parent element, and delegation is intended for the children). To answer your question moey it means that the delegated handler will match newly added elements, where the one without delegation won't. Delegation has the benefit of there being less events hooked into the browser causing lower memory consumption for the app, however the tradeoff is it increases the time to process a click (minimally). If you are making a game don't delegate.
    – vipero07
    Commented Jun 19, 2015 at 21:47
  • 1
    The "test page" you reference is not working. Commented Sep 11, 2017 at 21:52

6 Answers 6

396

Case 1 (direct):

$("div#target span.green").on("click", function() {...});

== Hey! I want every span.green inside div#target to listen up: when you get clicked on, do X.

Case 2 (delegated):

$("div#target").on("click", "span.green", function() {...});

== Hey, div#target! When any of your child elements which are "span.green" get clicked, do X with them.

In other words...

In case 1, each of those spans has been individually given instructions. If new spans get created, they won't have heard the instruction and won't respond to clicks. Each span is directly responsible for its own events.

In case 2, only the container has been given the instruction; it is responsible for noticing clicks on behalf of its child elements. The work of catching events has been delegated. This also means that the instruction will be carried out for child elements that are created in future.

17
  • 51
    That is a great explanation, and has brought clarity to an issue that I have long refused to understand. Thanks!
    – dgo
    Commented Apr 13, 2013 at 13:39
  • 3
    So why does on() allow two arguments when that would pretty much be the same as using click()?
    – nipponese
    Commented Jul 31, 2013 at 20:33
  • 6
    .on() is a general purpose API that can handle any kind of event, including multiple different events (you can put multiple event names in that first string.) .click() is just a shorthand for that first form.
    – N3dst4
    Commented Aug 1, 2013 at 7:45
  • 2
    @newbie, @N3dst4 : e.target will be the initial target of the click event (can be a child node if span.green has children). From inside the handler, you should use the this reference. See this fiddle.
    – LeGEC
    Commented Feb 14, 2014 at 9:45
  • 1
    Another note to add to the topic of delegation - it's both very useful and very efficient. You'll use up fewer browser resources with delegation vs attaching an event to every matching element. Thought I'd mention it, just in case people needed more reasons to use delegation.
    – phatskat
    Commented Mar 13, 2014 at 14:03
6

The first way, $("div#target span.green").on(), binds a click handler directly to the span(s) that match the selector at the moment that code is executed. This means if other spans are added later (or have their class changed to match) they have missed out and will not have a click handler. It also means if you later remove the "green" class from one of the spans its click handler will continue to run - jQuery doesn't keep track of how the handler was assigned and check to see if the selector still matches.

The second way, $("div#target").on(), binds a click handler to the div(s) that match (again, this is against those that match at that moment), but when a click occurs somewhere in the div the handler function will only be run if the click occurred not just in the div but in a child element matching the selector in the second parameter to .on(), "span.green". Done this way it doesn't matter when those child spans were created, clicking upon them will still run the handler.

So for a page that isn't dynamically adding or changing its contents you won't notice a difference between the two methods. If you are dynamically adding extra child elements the second syntax means you don't have to worry about assigning click handlers to them because you've already done it once on the parent.

2
  • would there be any noticeable difference performance wise, say put change listener to 20 elements or just on their parent and get ti with target after, just wondering (I know 20 is crazy small number, maybe 2000 idk Commented Jan 14, 2023 at 16:05
  • 1
    @VitaliyTerziev - Using a delegated handler (option 2) will be faster at the time you call .on(), because it only selects one element and binds one handler. Performance actually running the handler at the time the event occurs is not likely to be different enough for the user to notice the difference.
    – nnnnnn
    Commented Jan 16, 2023 at 2:44
6

The explanation of N3dst4 is perfect. Based on this, we can assume that all child elements are inside body, therefore we need use only this:

$('body').on('click', '.element', function(){
    alert('It works!')
});

It works with direct or delegate event.

3
  • 3
    jquery advises against using body, as it is slower, because the script would have to search for all childs inside body, which should be a lot in most cases. It's better (faster) to use the inmediate parent container of the element.
    – mikesoft
    Commented Apr 28, 2015 at 21:19
  • 2
    Jquery removed live method which was doing the same as You showed. This is performance weak and should not be used. Commented Sep 6, 2016 at 12:58
  • In modern browsers, $('body').on() delegation from .element should behave exactly the same as native document.body.addEventHandler() with an if (Event.target.className.matches(/\belement\b/)) in the callback. It may be very slightly slower in jquery due to $.proxy overhead but don't quote me on that.
    – cowbert
    Commented Jan 12, 2018 at 20:43
3

Tangential to the OP, but the concept that helped me unravel confusion with this feature is that the bound elements must be parents of the selected elements.

  • Bound refers to what is left of the .on.
  • Selected refers to the 2nd argument of .on().

Delegation does not work like .find(), selecting a subset of the bound elements. The selector only applies to strict child elements.

$("span.green").on("click", ...

is very different from

$("span").on("click", ".green", ...

In particular, to gain the advantages @N3dst4 hints at with "elements that are created in future" the bound element must be a permanent parent. Then the selected children can come and go.

EDIT

Checklist of why delegated .on doesn't work

Tricky reasons why $('.bound').on('event', '.selected', some_function) may not work:

  1. Bound element is not permanent. It was created after calling .on()
  2. Selected element is not a proper child of a bound element. It's the same element.
  3. Selected element prevented bubbling of an event to the bound element by calling .stopPropagation().

(Omitting less tricky reasons, such as a misspelled selector.)

1

I wro te a post with a comparison of direct events and delegated. I compare pure js but it has the same meaning for jquery which only encapsulate it.

Conclusion is that delegated event handling is for dynamic DOM structure where binded elements can be created while user interact with page ( no need again bindings ), and direct event handling is for static DOM elements, when we know that structure will not change.

For more information and full comparison - http://maciejsikora.com/standard-events-vs-event-delegation/

Using always delegated handlers, which I see is current very trendy is not right way, many programmers use it because "it should be used", but truth is that direct event handlers are better for some situation and the choice which method use should be supported by knowledge of differences.

1
  • if attaching many event handlers (for example, each row of a table), it is often better performance wise to use single delegated handler to container instead of attaching many direct handlers. You can see this from the basic fact that event handler count is by itself a profiling metric.
    – cowbert
    Commented Jan 12, 2018 at 20:45
0

Case 3 (delegated):

$("div#target").delegate("span.green", "click", function() {...});

Not the answer you're looking for? Browse other questions tagged or ask your own question.