Efficient Event Targeting with jQuery

Today I would like to talk a bit about setting up events on DOM using jQuery, and how one should try to avoid setting up huge numbers of unnecessary events on a single document. When setting up events, you are allocating little bits of memory to store procedures and logic into that will be ran upon what ever event type you apply, is indeed performed.

If when say faced with a huge list of records (lets say 3 or 4 hundred records), and each record has “additional details” that requires a user toggle open and closed, then one can see that the number of events can add up fast.

Let’s take a look at a snip of markup from this list of records I mentioned above:

    ...
  • Basic Details See More

    More Details

    Here is more details about this record.

  • ...

So assuming we have about 3 or 4 hundred list items (or rather records on our DOM), we would need to target each records span tag that are classed with “toggle-details”. Once we select each element, we then must assign a click event to each element that will decide to hide, or display a sibling section element.

One way to write that logic with jQuery is as follows:

$('.records .toggle-details').bind('click',function(){
    $(this).parent('li').find('.additional-details').toggle();
});

This will target each .toggle-details span tag, and bind a click event to each element. Once any of the elements are clicked, the logic asks for that clicked element to look to its parent wrapper, then search for a child called .additional-details to decide if it should be hidden or displayed.

The problem with our above code, is that we are asking our document to reserve 3 or 4 hundred events in memory for each span tag that has a class of “.toggle-details”. YIKES!

A much better way to do this, would be to look use a single parent element on the page, to use as a listening helper that will judge how to perform event logic, like so:

$('.records').bind('click',function(e){
    var target = $(e.target);
    if(target.is('.toggle-details')){
        target.parent('li').find('.additional-details').toggle();
    }
});

Here we not applying hundreds of events, but rather just one event to a single parent wrapper, and then utilizing the properties that come with our event in the form of a variable called “e”.

One very useful property of most event types is called .target; it allows you to ask for the element, at the start of a bubble up event capture. This means that although you have set the click event up on the parent wrapper, the element actually being clicked in this parent wrapper can still be recognized and targeted.

So, we first setup our variable called “target” and equal it to our event’s captured target (e.target). From here, it is a simple condition in our logic to accomplish what we need on a particular element; so we say, if “target” is classed with “.toggle-details”, then proceed with our original logic from above.

Kinda awesome right? But wait, it gets better!

In theory, if a person is to setup say a single click event on the body of the document, like so:

$('body').bind('click',function(e){
    var target = $(e.target);
    if(....){ .... }
});

Then any logic you need to have applied to elements that may come and go off the DOM, will always continue to work because the logic is never actually applied to any moving elements. Basically we have no more need to ever use .live, .delegate or .on for these circumstances of moving elements.

If you do decide to start wrapping up your event logic into the body under a single event, it is helpful to know that you want to reserve such logic as “e.preventDefault()” in side the meeting conditions, like so:

$('body').bind('click',function(e){
    var target = $(e.target);
    if(target.is('.some-anchor-tag')){
        ...
        e.preventDefault();
    }
});

Notice the e.preventDefault(); lives in the met condition? This is important, cause if you have it out side the condition, all links on the page will stop working. So, when using single element event binding and event targets, you need to think of your conditions as actual selectors, and keep your logic in them as you would any anonymous function.

Some common helpful variables one might want to setup if you are using a body to wrap your event logic into, are:

  • var target = $(e.target) = Used to find the element with in our event that has been interacted with. This is required.
  • var parent = target.parent(); = Points the the immediate parent wrapper of our found target element.
  • var body = $(this); = I much rather type 4 chars than 7 chars, but for semantics sakes, body is much more descriptive than $(this), SO USE IT!

So to conclude; with the single binding of an event type that uses the event targeting property, we no longer need to setup hundreds of events to elements anymore. We no longer need to worry about events being lost because elements get moved, or altered in our document. Also, a handy side effect from this approach is you end up with very clean and easy to read logic, that for the most part is easily found in one location.

Thanks, and enjoy!

Devin R. Olsen

Located in Portland Oregon. I like to teach, share and dabble deep into the digital dark arts of web and game development.

More Posts

Follow Me:
TwitterFacebookGoogle Plus