Eventual Consistency

5 jQuery tips for intermediates

jQuery is a much talked about library. The web is full of tips, guides and tutorials. Being a web developer, I've probably read dozens of them over the years, and it seems that they usually target only the far edges of the jQuery learning curve. It's either a tutorial for those using jQuery for the first time, or a deep dive into jQuery's structure for those competent (and willing) enough to really understand its internals.

Being somewhere around the middle of that curve, I'm always looking for good resources, containing practical advise I could utilize in my day to day work with the library. This post is here to cater others that find themselves in that same boat.

It's a collection of tips, tricks, and lessons I've picked up while working on Summer and prior projects. Some are slightly advanced, but don't go as far as discuss the implementation of the library. Just solid tips that helped me be more productive and happy when using the library.

I have also included a link to a jsFiddle page for each tip, showing a real world example that you can mess around with, and get a better grasp of why and how to use that particular tip in your projects. I hope you will find it useful.

1. The jQuery stack

When constructing a jQuery object we are actually building a smart array of DOM elements. This array has the power to further traverse the DOM tree, in order to refine our selection or to navigate to somewhere else on it. Take this example:

$('li').find('a');

We started by generating a array of li elements, and then another array, containing a elements that are inside the original list of li's. any subsequent operation we perform along this chain, will run based on this current list of a elements.

What is lesser known, is that jQuery actually maintains a stack of these arrays. the current array of a's knows about the prior array of li's. We don't have to select them again. To go up the stack, we can simply call the .end() function and we get back a reference to the prior selection.

This might not sound that useful at first, but in reality it lets you manipulate the document in a much easier, more expressive way. Here's an example:

$('li')
    .find('a') // li > a
        .attr('title', 'I am a link inside a list item!')
    .end()
    .find('img') // li > img
        .css('width', '50px')
    .end() // back to li
.css('background-color', 'blue');

It's not anything we couldn't have done otherwise, but this is a better, more readable alternative that makes big changes easier. Take a look at the jsFiddle example below for a real, useful, use case for this.

Other functions that help us navigate and control this stack is .pushStack(), that lets us push arbitrary elements (or other jQuery objects), and .andSelf(), which adds the prior object in the stack and adds it to the default:

$('p').find('a').andSelf().css('color', 'red');

This would change the color of both the links and their containing p elements to red.

try it yourself on jsFiddle

2. Custom Events

Pretty much one of the first things you learn when developing with jQuery, is how to bind functions to events. We're all familiar with code like this:

$('a.btn').bind('click', function(e) {
    // You could, of course, substitute .bind() with .on() or even just .click(),
    // depending on what you are trying to accomplish.
    e.preventDefault();
    alert('Yay! someone clicked on something!');
});

The lesser known feature in jQuery's event system is that it's not only restricted for use with events triggered by the browser. We can make up any event that suites our liking. Imagine this for example:

$('#music-player').on('stop-playback', function() {
    // your logic goes here.
    // ...
    // playback is stopped!
});

$('button.stop').on('click', function(e) {
    e.preventDefault();
    $('#music-player').trigger('stop-playback');
});

$('button.close').on('click', function(e) {
    e.preventDefault();
    // do all kinds of other stuff that have to do with closing the player..
    $('#music-player').trigger('stop-playback');
});

This feature lets us build quite complex routes and behaviors without repeating logic and without mixing different concerns. This pattern is taken even further with frameworks such as Backbone.js, but jQuery includes the basics out of the box.

One thing to note, is that for larger applications, this might not be the best way to define our logic, as it's all tied to DOM elements. The nice thing about jQuery though, is that it is so flexible - our jQuery object doesn't have to be a collection of DOM nodes, it can also wrap arbitrary javascript objects. We can leverage this to create almost Backbone-like (or your client-side MVC framework of choice) models using only jQuery and a few lines of code, as you'll see in the jsFiddle example.

try it yourself on jsFiddle

3. Inject document fragments

The .load() function is one of the simplest forms of AJAX available in jQuery, but it's really powerful. For those unfamiliar, it's a bit different than most other AJAX functions in that it isn't a global function, but a method on a jQuery object. when executed, it loads the requested URL and injects the resulting HTML into the elements contained in that object:

$('#news').load('/news/latest.html');

This is a really simple way of doing AJAX calls. One cool feature that is not often used, is the ability to inject only a fragment of that document, instead of the whole page, into our element:

$('#news').load('/news/latest.html div#sports');

This would preform an XHR request for the requested URL, parse the returned document, find only the requested elements, and add them to the selected elements. Not bad for one line of code.

try it yourself on jsFiddle

4. jQuery's animation queue

jQuery has a built-in queue implementation for asynchronous operations, to be executed serially, one after the other. A good example for this (and the only use of that queue inside jQuery that I know of) is the animation queue. Every animation we call on a jQuery object is pushed into a special queue inside that object (for animations, this queue is called 'fx') and are then stepped through one after the other. This makes animating complex things really easy, since we don't have to add a callback for every step in the animation to call the next one, we can simply chain them and they will just do the right thing at the right time.

$('.box')
    .animate({left: '-=25px'}, 100)
    .animate({left: '+=25px'}, 300)
    .animate({top: '-=100px'}, 1200)
    .slideUp(400)
    .fadeIn(500);

These will just happen serially, waiting for the prior to finish. If we want, we can have some of them happen in parallel by passing in a different queue name (something other than the default 'fx').

$('.box')
    .animate({left: '-=25px'}, 100)
    .animate({left: '+=25px'}, 300)
    .animate({top: '-=100px'}, {duration: 1200, queue: 'fx2'})
    .dequeue('fx2')
    .slideUp(400)
    .fadeIn(500);

In this case, the animation that uses 'fx2' would work in parallel to the others. Don't forget to call .dequeue() somewhere, to trigger the animations in that queue to start.

We use this technique quite often at Summer, to perform animations on our sidebar and widgets, and for intricate, fine-grained stuff - it really makes life easier.

try it yourself on jsFiddle

5. Create an options object with default values

This little trick is often used by plug-in developers. It comes in handy when we have some large class or function that might need to receive many possible parameters or options. Since we don't want to have to pass all these parameters every time, we can make use of jQuery's .extend() function to create a settings objects with default values:

function SomeAPIHandler(options) {
    var defaults = {
        // default parameters for SomeAPIHandler
        host: 'localhost',
        port: 8080,
        protocol: 'http'
    };
    this.settings = $.extend({}, defaults, options);
}
SomeAPIHandler.prototype = {
    getAddress:function() {
        return this.settings.protocol + '://' + this.settings.host + ':' + this.settings.port;
    }
};

// now we can create new SomeAPIHandler objects without passing all parameters.
var handler = new SomeAPIHandler({host: 'example.com'});
$('.results').html('address: ' + handler.getAddress());
‚Äč

As you can see, it's pretty straight forward. the .extend() function merges objects into the object given as it's first argument. in this case, the first argument is an empty object, which we then merge the default values into (essentially cloning them), and then override these with the options passed by the user.

try it yourself on jsFiddle


I'd love to hear your tips and tricks, as well as discuss the ones I presented here in the comment section!

comments powered by Disqus

Hi, I'm Oz Katz

I am a co-founder and CTO over at Swayy.

I usually write about software development using Python, JavaScript and other awesome, open source tools.

Feel free to reach out on Twitter, or contact me using the links at the bottom of the page.