Decoupling with Custom jQuery Events

|

0

By Simon Smith

While the concept of custom events in jQuery may not be a new one, I tend to only see them used within plugins, mainly to keep their events namespaced for fear of collusion with other events.

The truth is they can be leveraged in everyday jQuery code to help make life a little easier.

The problem

It’s not uncommon to see a single event handler finding itself responsible for many things:

$('.add-items').on('click', function(event) {
    $('.loading-message').show();

    $.ajax({
        // etc
    });

    $('.something-else').addClass('foo')

    event.preventDefault();
});

Having an event handler responsible for a lot of different tasks will typically result in large, incoherent event callbacks. The situation often gets worse when other developers have to add in additional functionality later.

Decoupling all the things

One route I like to take is to separate the logic relating to the DOM event. This means just handling anything relating to the event (preventDefault or stopPropagation calls, for example) and then firing a custom event and allowing another part of the application handle the ‘business’ side of things:

var doc = $(document);
$('.add-items').on('click', function(event) {
    doc.trigger('addItem', [$(this)]);
    event.preventDefault();
});

The first thing to note is that I’m grabbing a reference to the document wrapped as a jQuery object:

var doc = $(document);

By triggering the events on this global object, it can become a sort of mediator between other parts of code. Storing a reference to it outside of the event handler callback also means that it won’t be created each time the button is clicked. This is a good practice to get in the habit of.

Then all that is required is to trigger an event of our choice and pass any useful data along with it.

doc.trigger('addItem', [$(this)]);

An object or array can be passed along with the event. In this case I’m passing along a jQuery object that refers to the clicked button.

Responding to the event

Listening for the addItem event is as simple as creating an event listener on the document object:

$(document).on('addItem', function(event, btnElement) {
    $('.something-else').addClass('foo')
});

$(document).on('addItem', function(event, btnElement) {
    $.ajax({
        // etc
    });
});

The event object in the above callback handlers are related to the addItem custom event and have nothing to do with the earlier click event.

Now the logic is nicely separated, and additional chunks of functionality can be added or removed without any need to worry about the other parts of the application.

Seeing as the global document object was chosen to be the mediator, these events can be attached in other places (perhaps a different file completely) without any extra effort.

It seems like a bit more work initially, but as the code expands, this way of decoupling events will start to pay back in droves.

Taking it a step further with Flight

If this technique takes your fancy then I strongly advise you to give Twitter’s Flight library a look. It builds upon the concept of using jQuery events to decouple modules of code, but adds on a plethora of other nice features. Plus it’s developed by some of the smartest JS developers around.

It’s also very easy to integrate into existing code bases, compared to switching to something more fully-fledged like Ember or Backbone.

I’ve also set up a crude little demo if you’d like to tinker.

This article was originally published at http://simonsmith.io/decoupling-with-custom-jquery-events/

The background image is courtesy of http://commons.wikimedia.org/wiki/File:Life_is_Beautiful.jpg

Previous

Best of JavaScript, HTML & CSS – Week of July 1, 2013

Soma.js – Your Way Out of Chaotic JavaScript

Next

9 thoughts on “Decoupling with Custom jQuery Events”

  1. 0

    Hi Simon, I’m really liking this technique. Thanks for the write up. Do you know if this can cause any degrades to performance by having multiple listeners on the document?

    Reply
    • 0

      Once the event bubbles up the DOM, it will fire any event handlers attached to the event. It only traverses upwards the one time, so there aren’t any performance degradations with this method as it pertains to DOM traversal.

      Reply
  2. 0

    Instead of writing $(‘.add-items’).on(‘click’, function(event) {});, I’ve found that writing $(‘.add-items’).on(‘click’, add-itemsClicked);function add-ItemsClicked(event) {} has elevated my understanding of JavaScript to the next level. People will say that writing using this new style requires coming up for a name for the function, but I think of it as self-documentation. After all, naming something encapsulates the essence of the purpose of the function.

    Reply

Leave a Comment