Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event delegation vs direct binding when adding complex elements to a page

I have some markup like this (classes are just for explication):

<ol id="root" class="sortable">
  <li>
    <header class="show-after-collapse">Top-Line Info</header>
    <section class="hide-after-collapse">
      <ol class="sortable-connected">
        <li>
          <header class="show-after-collapse">Top-Line Info</header>
          <section class="hide-after-collapse">
            <div>Content A</div>
          </section>
        </li>
      </ol>
    </section>
  </li>
  <li>
    <header/>
    <section class="hide-after-collapse">
      <ol class="sortable-connected">
        <li>
          <header/>
          <section class="hide-after-collapse">
            <div>Content B</div>
          </section>
        </li>
      </ol>
    </section>
  </li>
</ol>

That is, nested sortable lists. The sortable plugin suffices, however, since each li (hereafter "item") maintains its level, though the inner lists are connected. The items have an always-visible header and a section visible when in expanded state, toggled by clicking the header. The user can add and remove items from either level at will; adding a top-level item will include an empty nest list inside it. My question is with respect to JS initialization of the newly created item: While they will share some common functionality, which I can cover via

$("#root").on("click", "li > header", function() {
  $(this).parent().toggleClass("collapsed");
});

and

li.collapsed section {
  display: none;
}

(Side question: would this be an appropriate place to use the details/summary HTML5 tags? It seems sort of iffy about whether those will even make it into the final spec, and I want a sliding transition, so it seems like I'd need JS for that anyway. But I throw the question to the masses. Hello, masses.)

If the root list is the only (relevant) element guaranteed to be in existence at page load, for .on() to work effectively, I have to bind all the events to that element and spell out the precise selector for each, as I understand it. So, for example, to tie separate functions to two buttons right next to each other, I'd have to spell out the selector in full each time, à la

$("#root").on("change", "li > section button.b1", function() {
  b1Function();
}).on("change", "li > section button.b2", function() {
  b2Function();
});

Is that accurate? That being the case, does it make more sense to forgo .on() and bind my events at the time the new item is added to the page? The total number of items will probably number in the dozens at most, if that makes a difference in the response.

like image 391
slk Avatar asked Jan 11 '12 22:01

slk


People also ask

What is event delegation used for?

Event delegation refers to the process of using event propagation (bubbling) to handle events at a higher level in the DOM than the element on which the event originated. It allows us to attach a single event listener for elements that exist now or in the future.

What is event delegation How is it different from event bubbling?

tl;dr: event delegation is the technique, bubbling is what the event itself does, and capturing is a way of using event delgation on events that don't bubble.

How do you attach a event to element which should be executed only once?

Approach 1: In this approach, we will be using the jQuery one() method. This method attaches an event to the element with the specified selector. It is named one because the event handler corresponding to the event is only executed once.


2 Answers

You will create less CPU overhead in binding the events using $(<root-element>).on(<event>, <selector>) since you will be binding to a single "root" element instead of potentially many more single descendant elements (each bind takes time...).

That being said, you will incur more CPU overhead when the actual events occur as they have to bubble up the DOM to the "root" element.

Short-story: delegate saves CPU when binding event handlers; bind saves CPU when events trigger (e.g. a user clicks something).

So it's up to you to decide what point is more important for performance. Do you have available CPU when you add the new elements? If so then binding directly to the new elements would be the best for overall performance however if adding the elements is a CPU intensive operation you will probably want to delegate the event binding and let the event triggering create some extra CPU overhead from all the bubbling.

Note that:

$(<root-element>).on(<event>, <selector>, <event-handler>)

is the same as:

$(<root-element>).delegate(<selector>, <event>, <event-handler>)

and that:

$(<selector>).on(<event>, <event-handler>)

is the same as:

$(<selector>).bind(<event>, <event-handler>)

.on() is new in jQuery 1.7 and if you are using 1.7+ then .delegate(<selector>, <event>, <event-handler>) is just a short-cut for .on(<event>, <selector>, <event-handler>).

UPDATE

Here is a performance test showing that it is faster to delegate event binding than to bind to each element individually: http://jsperf.com/bind-vs-click/29. Sadly this performance test has been removed.

UPDATE

Here is a performance test showing that event triggering is faster when you bind directly to elements rather than delegate the binding: http://jsperf.com/jquery-delegate-vs-bind-triggering (Note that this isn't a perfect performance test since the binding methods are included in the test, but since delegate runs faster on binding it just means that bind is even faster relatively when talking about triggering)

like image 178
Jasper Avatar answered Oct 18 '22 17:10

Jasper


Since the accepted answer has inaccurate tests (BTW: test your code, measure performance, don't just blindly follow some "rules" - this is not how optimization is done!) and is simply wrong I post fixed tests: https://jsperf.com/jquery-delegate-vs-bind-triggering/49

which proves on such simple example there is NO difference between delegation or direct binding

The only cases when delegation is always bad are events like mouse move and scroll - which are triggered x times per second. THIS is where you will notice any performance difference.

If you have even 1ms difference (won't happen, but this is just an example) on single event - like click - you won't notice that. If you have 1ms difference on event that happens 100 times in a second - you will notice CPU consumption.

Just having thousand of elements won't negatively impact your performance with delegation - actually - this is the case when they should be used - to avoid hogging CPU when attaching thousand of event handlers.

So if you really need a rule to follow (don't do that) - use delegation on everything except mouse move, scroll and other events that you can expect to fire continuously.

like image 44
dzek Avatar answered Oct 18 '22 17:10

dzek