Monday, January 13, 2014

Event Handler Optimization

I've been coding with jQuery for a long time.  I'm pretty sure 1.3 was new when I started.

One of the things that had always been "good enough" for me was to attach event handlers en masse directly to the elements involved: $('a.history').click(popupHistory);

It so happens that if the page is a table of 50 customers in the search result, each with a history link, then 50 handlers get bound.  jQuery takes care of all the magic, but it can't really optimize your intent.  Newer versions of jQuery—that is, 1.7 and up—support a more intelligent event-binding syntax which is a beautiful combination of two things:
  1. A single handler on an element containing all of the interesting ones, invoked as the event bubbles.
  2. A selector for the handler to filter interesting children.  The handler is only called if the event occurs within a child matching that selector.
These features are part of .on().  For an idea how this works:

<div id="box"></div>
<script type="text/javascript">
$('#box').on('click', 'a.history', function (e) {
popupHistory($(e.target).closest('[data-customer]').attr('data-customer'));
});
// populate the #box with HTML somehow, e.g.:
$('#box').html('<div data-customer="3">Customer! <a class="history" href="#">View Order History</a></div>');
</script>

Not only does this attach one event handler under the hood, but that event handler will be run for any present or future children.  The parent must be there for .on() to bind the handler to (otherwise, you'd have an empty jQuery set, and nothing would happen), but children will be detected by that handler when the event fires.

In the presence of dynamically added/removed elements, then, it's a double optimization: not only does only one handler get bound in the first place, but the handlers don't have to be changed on a per-element basis when children are added or removed.

No comments: