Technology

“Pub/Sub” with jQuery

Posted by Pixafy Team

“Pub/Sub” is short for publishing and subscribing. It is derived from the observer pattern (click here for more on the observer pattern). The overall idea is that when a certain event fires (or action takes place), listeners that are subscribed to it execute. This allows for extensive asynchronous development as you can easily hook on to your jQuery AJAX callbacks and keep clean and concise code.

With the great advancements that have come along with AJAX (if you are unfamiliar with AJAX, read this first), there have also been some problems.

A major benefit of AJAX is the ability to gather data from the server in the background while the page loads (and even after). An example would be a custom Twitter feed that displays the latest ten tweets with the hash-tag “pub/sub.”

You make a call like this:

http://search.twitter.com/search.json?q=pubsub&rpp=10&include_entities=true&lang=en&result_type=mixed&callback=?

Now that you have made the call, you plan to use the data to build your awesome tweets feed! But how do you access the data when it is returned? You could just build the feed in the callback of the $.ajax() request, but that is bad for two reasons.

  1. Your logic will be too dependent, which is un-scalable.
  2. Your code becomes too messy and hard to manage.

This is where jQuery comes to the rescue. With just a little bit of code we can set up an observer system for our tweets feed and anything else that may require event observation, such as populating a table with data. First, I will show you the code for setting up pub/sub, and then I will break it down for you.

Aliasing jQuery’s $.trigger(), $.on(), $.off(), and $.one()

;(function( $, window, document ) {
var o = $( {} );
$.each({
trigger: ‘publish',
on:         'subscribe',
off:         'unsubscribe',
one:       ‘subscribeOnce’
}, function( key, val ){
$[val] = function() {
o[key].apply( o, arguments );
}
});
})( jQuery, window, document, undefined );

The first step is to start our function with a semi-colon. This is done in case our code will later be concatenated. We can’t depend on what comes before being properly closed so we add the semi-colon to ensure our function is stand-alone.

Next we wrap our code in a self-invoking function and alias jQuery as “$,” the window object as “window,” the document object as “document,” and undefined as nothing in order to preserve a true “undefined.” The reason we pass in the parameters is because we will be using the new strict method for processing our Javascript. (Click here for more information on “use strict.”) Without passing them in, “window,” and “document” would technically be undefined and throw an error.

Note: Older browsers that do not support strict mode read the declaration as a string and continue to process the Javascript.

The last thing to mention is aliasing our jQuery object with “$.” This ensures safe usage with the $ symbol without any conflicts with other JS libraries, such as Prototype or MooTools.

Note: It is a best practice to keep your jQuery separate from other JS libraries, so create a separate file for all jQuery and wrap the entire file in the self-invoking function.

;(function( $, window, document )  {
“use strict”;
// code here….
})( jQuery, window, document, undefined );

Next we want to pass an empty object to jQuery. This will return an instance of jQuery that we can use to set up our API.

var o = $( {} );

Once we have our instance of jQuery, we are now ready to loop over our functions and alias them.

$.each({
trigger: 'publish',
on:         'subscribe',
off:         'unsubscribe',
one:       ‘subscribeOnce’
}, function( key, val ){
$[val] = function() {
o[key].apply( o, arguments );
}
});

To do this we simply add new methods to the jQuery object (‘publish’, ‘subscribe’, unsubscribe’, and ‘subscribeOnce’) that will apply our original functions (‘trigger’,  ‘on’, ‘off’,‘one’).

The first part of the $.each statement is simply our object we want to iterate over.

{
trigger:                 ‘publish’,
on:         ‘subscribe’,
off:         ‘unsubscribe’,
one:       ‘subcribeOnce’
}

The next step is to assign our functions to the jQuery instance.

function( key, val ){
$[val] = function() {
o[key].apply( o, arguments );
}
}

Normally when adding a method to an object you would do something like this:

obj.someMethod = function() {
// code goes here…
};

This won’t work for us in our situation because we are using a loop and variables. If we were to do:

$.val = function() {

};

Then we would actually make a method on the jQuery object called “val” and overwrite it until our loop completed; this is obviously not the behavior we want.

The solution is to use the array notation for jQuery. in fact, just as you can call:

$( document ).on( ‘click’, function() {

});

You can also call:

$(document)[‘on’]( ‘click’, function() {

});

Using the array notation will allow us to pass in our “val” variable and add a method to jQuery with our desired name stored in the “val” variable.

$[val] = function {

};

Next we call our instance of jQuery (our “o” variable), and call the actual function on it like so:

o[key].apply(o, arguments);

If this were the first iteration in our loop it would look like:

$[‘publish’] = function() {
o[‘trigger’].apply( o, arguments );
}

This calls the trigger method, sets our instance of jQuery as “this” in the scope of the function and passes in any arguments that may have been sent with the method call. Example:

$.publish( ‘twitter/feed/ready’,  data );

The “data” object would be passed through to the trigger method call and passed as a parameter of that function.

Now that we have our new jQuery methods set up, let’s put them to good use. However, before we can do that, we must make a quick detour and discuss jQuery custom events and namespacing.

jQuery Custom Events

A little known fact about jQuery is that it is extremely simple to set up custom events. For example:

$( ‘.someElement’ ).trigger( ‘click’ );

$( ‘.someElement’ ).on( ‘click’, function() {
// your code here
});

What you may not know is that it does not matter what the event is named. Instead of click you could just as easily use:

$(‘.someElement’).trigger(‘someGibberish’);

$(‘.someElement).on(‘someGibberish’, function() {
// your code here
});

This code will behave the same as the code utilizing the click event name.

It is very, very easy to abuse this and just start throwing out custom events like nobody’s business. Which brings us to our next topic.

Namespacing

Because it is so easy to create and use custom events in jQuery, it is extremely important that you utilize a naming convention and stick to it. Even if you are the only person working on your code now, you may not always be.

Just as your site should be separated into sections (business logic, models, controllers, utilities, etc), your custom events should be too. There are two common separators in use for name-spacing custom events, the “.” and the “/”.  It is a matter of preference only and has no effect on the jQuery.

Now to build a name-space convention for our project, let’s use Twitter for our example. There are a number of ways to integrate Twitter so our first namespace should be just that, “twitter.” Now it will be apparently obvious to anyone that this publishing or subscription will be about twitter. So now we would have something like:

$(document).publish(‘twitter’, data);

Now let’s say that you allow people to log in with Twitter on your site/application. Now you could have a namespace dealing with Twitter login like so:

$(document).publish(‘twitter/login’, data);

You see where we are going with this, now we get to the actual usage, it would make sense to have events to fire for login success and login failure like so:

$(document).publish(‘twitter/login/success’, data);
$(document).publish(‘twitter/login/failure’, data);

OK, now that we have completed our detour, let’s get back on track and complete our Tweets feed.

Pulling It All Together

First we want to make our call to twitter to get our feed, but this time instead of using the $.ajax() call which is a little more powerful than we need we are going to use $.getJSON().

;(function( $, window, document ) {
var o = $( {} );
$.each({
trigger: 'publish',
on:         'subscribe',
off:         'unsubscribe',
one:       ‘subscribeOnce’
}, function( key, val ){
$[val] = function() {
o[key].apply( o, arguments );
}
});

$( document ).ready(function() {
var url = ‘http://search.twitter.com/search.json?q=pubsub&rpp=10&include_entities=true&lang=en&result_type=mixed&callback=?’;
$.getJSON( url, function ( results ) {
$.publish( ‘twitter/tweets/results’, results );
});
$.subscribe(‘twitter/tweets/results’, function ( results ) {

//make your tweet feed magic happen!
}); 

//if you did not need to listen for this action/event anymore
$.unsubcribe( ‘twitter/tweets/results’ );

//if you only need to listen one time for this action/event
$.subscribeOnce( ‘twitter/tweets/results’, function() {
//make your tweet feed magic happen!
});
});
})( jQuery, window, document, undefined );

If we wanted to take this a step further we could easily let people on our site/application update the feed by simply adding one more custom event and a search box on our site/application. You could then fire a custom Twitter search event and have the feed div update when the new results are brought back from Twitter. To implement this change would require an additional ten minutes of coding, but adds a great deal of interactivity to your site/application. Remember, scalability is key when developing your code.

See the demo at: http://jsbin.com/uxakut/2/edit

Hopefully you can see the power of setting up a pub/sub API for your projects. Setting it up takes about ten lines of code, and the power and usefulness of pub/sub in many cases far outweighs the ten lines.

If you found this useful or have a different method you prefer to accomplish this task, please share below in the comments or tweet us @Pixafy!