How can you write better in jQuery?

Some useful little tips that makes using jQuery count

Terry Mun
Coding & Design

--

Author’s note: This article has been updated as of 2nd of July, 2017 to be compliant with changes in jQuery 3.0+.

Since its sleepy origins dating back to 2006 and resting quietly somewhere on John Resig’s (@jeresig) computer, jQuery has grown tremendously, by quality, user base and popularity, over the years. The multi-browser compatibility (note the avoidance of the term cross-browser) of jQuery — yes, even down to the burning hell of IE6 — coupled with its ability to transverse through the DOM, perform AJAX calls, create animations and handle events with relative ease makes it the most popular JavaScript framework to date.

How popular is jQuery? At the time of writing, the framework is used by over 68% of the top 10,000 sites, and up to 45% adoption rate in the top 1 million sites.

As an avid and a somewhat seasoned user of the framework itself, I participate actively in helping out other users in various forums, although I concentrate most of my efforts on Stackoverflow. This article serves as a non-exhaustive compilation of some useful tips and tricks I have come across or dished out, as well as some common pitfalls that developers unknowingly make.

Tips on stylistics

Code readability is more important than you may think.

This section covers some useful pointers in dealing with indention and presentation of code. It has nothing to do with the performance of jQuery itself, but improves readability of your code.

Readability of your code is crucial when someone else is expected to review your code, or take over your project in the future. It makes it easier for a third party to troubleshoot problems and understand your workflow/strategy. Hell, you might even benefit from it too, if you’re reviewing codes that you written too long ago.

Self-closing elements

When inserting empty elements into the DOM, you can use self-closing tags:

$('#el1').append('<table class="holly" />');
$('#el2').before('<p class="user-message note" />');

Single quotes, or double up?

When you look at various jQuery scripts written all over the web, you will realize that some people prefer double quotes while some prefer their simpler, single counterparts. Does it make any difference? No, not really — but you do have to be careful to escape whichever one you are using within the quotes.

// Both examples are identical
$('p').append('<span class="message">Hello</span>');
$("p").append("<span class='class'>Hello</span>");

Remember to escape if you are using the same type of quotes in a string:

// Both examples are identical
$('p').append('<span class=\'message\'>Hello</span>');
$("p").append("<span class=\"message\">Hello</span>");

Indenting code

When performing method/function chaining, it is very easy to lose context along the way:

$('#ele1').find('img').css({ opacity: 1 }).after('<div />').next().append($object1).addClass('holy-moly');

Whew, that’s quite a handful! What we can do is to separate each method into a new line, and indent them according to their context/scope:

$('#ele1')
.find('img')
.css({ opacity: 1 }) // Applies to <img>
.after('<div />') // Applies to <img>
.next() // Moves on to appended <div>
.append($obj1) // Applies to <div>
.addClass('holy-moly'); // Applies to <div>

To use $, and when to not?

Sometimes, it would be nice to be able to tell apart regular JS variables and jQuery objects at a single glance. We can use $var to denote jQuery objects:

var $w        = $(window)    // The window object
threshold = $w.height(), // Numerical value
waitTime = 100, // Numerical value
message = 'Hello!' // String
$note = $('<p />'); // <p> object

Functionality

Optimizing codes, and correcting common mistakes

This segment deals with common mistakes made by web developers, as well as useful tips on how your code can be optimized to deliver a better user experience.

Load a CDN-hosted copy of jQuery and other plugins

CDN-hosted resources are delivered faster because content is delivered from the server that the least number of node separated away from the end user. Moreover, by loading popular resources through CDN, there is a high chance that the user already as a cached copy on his/her machine, thereby reducing loading time, latency and saving bandwidth.

For jQuery, I highly recommend either one of the following sources:

Remember the rule: jQuery first, plugins second. Always load jQuery first, and then other plugins.

But wait — watch out for legacy code!

You might want to load version-specific jQuery library, if you happen to have legacy code that is yet to be ported over to be compatible with newer releases of jQuery (especially codes written prior to v1.7. release). The jQuery site hosts a list of deprecated functions that you might find useful. Notable functions include: .size(), .toggle() and .live().

Many popular jQuery plugins are available via CDN, too —even though the list of such are carefully curated by a selected few individuals, I think they have done quite a good job at including most, if not all, the commonly used ones.

Remember, beauty takes time

Event handlers are bound to elements if they already exist within the DOM tree when your script is executed (i.e. at runtime). Therefore, it is always advisable to wrap all your jQuery code between the DOMready handler:

$(document).ready(function () {
// Everything else goes there
});

Even better, by shaving 16 characters off:

$(function () {
// Everything else goes here
});

Multiple selectors

The selectors in jQuery works just like the ones in CSS — instead of specifying multiple statements to perform a single action, you can combine the selectors by concatenating them with commas. Let’s say we want to add the class “foo” to two elements, #section1 and .box:

$('#section1').addClass('foo');
$('.box').addClass('foo');

There is a faster way to do it:

$('#section1, .box').addClass('foo');
  1. Give me #section1 and .box.
  2. Add the same class, “foo”, to both of them.

Chaining is your best friend

…and also one of the most powerful feature of jQuery. Every method in jQuery returns a set of objects, allowing further manipulation —thus making chaining possible. Moreover, jQuery keeps tab on what DOM elements you have transversed during your chaining, allow you to return to the ones you have access previously.

Let’s say we want to perform some actions on a paragraph element: we want to add a class to it, remove another class, find images inside it and set its opacity to 0.5. The long winded way to do this is:

$('p').addClass('success');
$('p').removeClass('warning');
$('p img').css({ opacity: 0.5 });

However, since we are performing all the said actions on the same paragraph elements anyway, we can chain them instead—note the indentation style I have advocated earlier on:

$('p')
.addClass('success')
.removeClass('warning')
.find('img')
.css({ opacity: 0.5 });

Context is useful, and important

jQuery allows us to take advantage of the context of an element of interest. It is hard to describe without using an arbitrary example, so let us say we have the following markup:

<section>
<nav id="#el1"><!-- --></nav>
<div id="#el2"><!-- --></div>
<p id="#el3"><!-- --></p>
<ul id="#el4"><!-- --></ul>
<img id="#el5" />
</section>

… and what you want to add the class “hidden” to all elements except for say, #ele2. Of course you can write something like the following:

$('#el1').addClass('hidden');
$('#el3').addClass('hidden');
$('#el4').addClass('hidden');
$('#el5').addClass('hidden');

The snippet definitely get works done. The problem is that the code looks, and is, bloated (aka too verbpse). No good. The trick is to take advantage of the context. We can select for #el2, and then proceed to select all its siblings and add the same class to all them — remember that we can use chaining!

$('#el2').siblings().addClass('hidden');
  1. select #el2.
  2. Select all its siblings, construct new jQuery object from them.
  3. Add the class “hidden” to all of them.

The focus is on .on()

Dealing with dynamically added DOM elements

When new elements are added to the DOM tree after the DOM has loaded, event handlers are not bound to them even when they satisfy the $(selector) criteria. Along the same vein as the previous tip, the new elements are not in the DOM tree when your script was executed, therefore event handlers are not bound to them when they were added dynamically (i.e. after DOM ready or pageload).

Note: As of jQuery v3.0 and beyond, all convenience methods such as .click() are removed. Instead, always use .on(‘click’) instead.

The easy way to do this is to use the .on() method. What it does is simple — it listens to the selector which the method is chained to, and listen for events bubbling up from a specified selector.

<section class="post">
<div class="box">
<!-- The <a> element below is dynamically added -->
<a class="button">This is a button.</a>
</div>
</section>

In the markup above, the anchor element is added dynamically. Therefore, the following code will not work:

$('.button').click(function(){...});

There are two ways you can attach event handlers to the newly added element — by listening to the click event bubbling up to (1) a parent of the element itself (but the parent has to exist when DOM is ready), or (2) on the document body (the highest level):

// #1: Listen to event bubbling on parent. Any would work!
// Note: Parent has to exist when DOM is ready
$('.box').on('click', '.button', function(){...});
$('.post').on('click', '.button', function(){...});
// #2: Listen to event bubbling up to the document itself.
// Note: The fail safe method
$(document).on('click', '.button', function(){...});

Even better: .on() allows you to bind multiple event handlers to the same element(s). Let’s say we want to trigger the function doSomething() when an input element is either clicked on, or focused on (can be triggered by clicking on its corresponding <label>, or by tabbing). Instead of doing this:

$('input').click(doSomething);
$('input').focus(doSomething);

…we can go for this instead:

$('input').on('click focus', doSomething);

Sorry for the pun, but .live() is dead.

Moreover, one of the most common problems pertaining jQuery use is with .live() failing to work in recent versions of jQuery. The function has been deprecated as of version 1.7. The fix is easy — simply port the line involving .live() to .on(). Also, the .delegate() method has been superseded by .on() — but it remains the most effective way of binding handlers in older jQuery versions (which you should really avoid using).

// Instead of these:
$('.button').live('click', function(){...});
$(document).delegate('.button', 'click', function(){...});
// … use this:
$(document).on('click', '.button', function(){...});

Use .prop() to deal with boolean attributes, not .attr()

Yet another very popular and often repeated question on Stackoverflow. How do you check, or uncheck, checkboxes? We know that checkboxes can be marked as checked by adding a checked property in HTML:

<input type="checkbox" checked />

Now the question is, how do we track the checked status of a checkbox, or set that status? Some may recommend the .attr() method, which sounds intuitive but actually does more harm than good. Instead, use the .prop() method instead:

// To check the status
$('input[type="checkbox"]').prop('checked');
// To set the checked/unchecked status
$('input[type="checkbox"]').prop('checked', true);
$('input[type="checkbox"]').prop('checked', false);

You should avoid using .removeProp() because once the property is removed, it cannot be added back anymore.

The .prop() method is best suited to manipulating or reading HTML boolean attributes (whose values do not matter — it’s their presence that does). In fact, they are known as implied boolean attribute for this very reason. Examples of such attributes are:

  • checked (for checkboxes and radio buttons)
  • disabled
  • multiple (for email or file inputs)
  • readonly (for select, text and textarea)
  • selected (for select)

Inserting content —Before, after, prepend or append?

When inserting elements before or after an element, use .before() and .after() respectively. For example, if we want to insert an image before, and a division after a paragraph, we can do this:

$('p').before('<img />').after('<div />');

This will yield the following structure:

<img />
<p><!-- Pre-existing --></p>
<div></div>

When inserting elements into an element, use .prepend() or .append(). The difference is that prepend inserts the new element into the start (i.e. insert element as the first child), while append does the exact opposite, inserting the element as the last child:

$('div').prepend('<p>Hello!</p>').append('<p>Goodbye!</p>');

This will yield the following structure:

<div>
<p>Hello!</p>
<!-- Pre-existing content -->
<p>Goodbye!</p>
</div>

e.preventDefault() or return false?

When I started writing jQuery, I was just as confused as many others when it comes to deciding between using e.preventDefault() and return false.

Pretext: The hypertext reference attribute, href, of an anchor element <a> will point the browser to a portion of the page (if a hash is used, i.e. ‘#top’ or simply ‘#’) or a different URL. However, the anchor element also doubles as a semantically-valid clicking target or a call-to-action element, sacrificing its initial purpose of updating the page scroll position or redirecting to another URL. In this case, the ‘#’ is often the de facto value used by web developers:

<a href="#" title="Update shopping cart">Update cart</a>

In order to prevent the link above from sending the user rocket speed towards the top of the page, which is the default behaviour, we have to prevent the default action from occurring. This is commonly done in two ways:

// Method #1
$('a').on('click', function (e) {
e.preventDefault();
});
// Method #2
$('a').on('click', function () {
return false;
}

I personally prefer the first method, because it does what it exactly says — prevent the default action of the element from taking place. While return false is an entirely valid command on its own, it is important to note that it also prevents event from bubbling up the DOM tree. In other words, writing return false is an equivalent of the following:

$('a').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
});

This is a little dangerous when you have dynamically-added elements within the element itself (in this case, the <a> element), methods that rely on event bubbling, such as .on(), will not work — when elements within the anchor element is clicked on, the event bubbles up but is stopped at <a>.

Moreover, return false must be placed on the last line in the code block or else it will block the execution of downstream scripts within the same block.

Loopy doopy do!

Very often people wonder how to loop through each element individually, and then target the current element. For this purpose, we can use .each() and $(this). Let’s say we want to add a class to all paragraphs, based on their index. If you are working on a static page, you might be tempted to hardcode it:

<p class="para-1">...</p>
<p class="para-2">...</p>
<p class="para-3">...</p>

What if your page content changes dynamically? That sounds like a lot of trouble. Or, what if you update your page so often, inserting new elements here and there, and lose track of the index? jQuery is here to help:

$('p').each(function (i) {
$(this).addClass('para-' + (i+1));
}

Remember that i is a zero-based index, i.e. it starts from 0 for the first encountered element.

It opens the door towards manipulation based on index and the likes. Let’s say we want to assign the top heading of each section of the page into a dynamically-updated navigation menu:

$('section').each(function (i) {
// Check if ID exists — if not, assign one
if($(this).attr('id') == undefined) {
$(this).attr('id', 's'+i);
}
// Variables
var sID = $(this).attr('id'),
sHead = $(this).find('h2').first().html();
// Insert list item and anchor
$('nav ul').append('<li><a href="#'+sID+'">'+sHead+'</a></li>');
});

The above code can be simply explained as:

  1. Loop through all the <section> elements.
  2. Check if it has an ID. If not, assign it an ID based on index (easy to ensure that ID is unique, because index is unique itself).
  3. For each <section> encountered: store ID (generated or pre-existing) and HTML content of the first encountered <h2> element; append list item and anchor, complete with a href attribute that points to the section’s own ID, and a HTML content derived from that of the current section’s header.

Adding a new element and selecting it

It is very common for the need to append a new element after another, and then selecting it to manipulate it. For example, we want to add a little span after a table element. A common method is to give the span an ID, and then select for it thereafter:

$('table').after('<span id="#hello">Hello, Molly!</span>');
$('#hello').doSomething();

Not too bad, but it might be problematic down the road — what if, say, there are multiple instances of table involved? You will be repeating the same ID over and over again, but the W3C directive dictates that IDs are supposed to be unique. jQuery will simply select the first instance if the IDs are non-unique, therefore breaking the effect.

What you would do is to take advantage of .next(), and perform DOM transversing:

$('table')
.after('<span>Hello, Molly!</span>')
.next().doSomething();
  1. Select all <table> in the document
  2. After each <table>, add the <span> element.
  3. Now move on to the next available sibling (which is the newly added <span>)
  4. Perform .doSomething() on the newly constructed jQuery object (which is the <span> now)

Fall back, fall back!

Another common question is to return to the old element, after a new jQuery object has been constructed.

<div>
<img />
<p><!-- --></p>
</div>

Let’s say we want to select <div>, do something, find its children <img>, do something, and then move on to <p>. We can take advantage of .end(), which allows us to pop off the last jQuery object constructed:

$('div')
.find('img') // Find <img>
.doSomething() // Applies to <img>
.end() // Pops <img> off, return to <div>
.find('p') // Find <p>
.doSomethingElse();

.end() showcases one of jQuery’s crowning jewel — that it keeps track of what elements you have accessed previously, allowing you to return to them at a later point.

Automatic vendor prefixes

jQuery automatically adds vendor prefixes when it sees fit — therefore saving you the headache of adding vendor prefixes yourself:

Instead of writing:

$('.box').css({
'-webkit-transform': 'translate(100,100) scale(2)',
'-moz-transform': 'translate(100,100) scale(2)',
'-ms-transform': 'translate(100,100) scale(2)',
'-o-transform': 'translate(100,100) scale(2)'
});

You can simply use this:

$('.box').css({ 'transform': 'translate(100,100) scale(2)' });

Debouncing & throttling

If you have wondered why your browser performance is sluggish whenever you attach the event handlers .scroll() or .resize() to, say, the $(window) object, that is because some browsers (like Chrome) tend to fire the event way too often than one might expect.

That is when debouncing or throttling come into play — although they are of decidedly different nature. Debouncing involves coalescing the amount of calls made in close succession (threshold to be user-defined) into a single event, while throttling involves limiting the firing rate, i.e. the timing between each firing of the event (also to be user-defined).

I recommend the use of Ben Alman’s jQuery throttle/debounce plugin. The CDN-hosted version is also available.

For example, the $(window).resize() event can be debounced — you don’t have to trigger the event on-the-fly as the user resizes his/her viewport. What you need is simply the final dimension of the viewport after the user decides he/she is done resizing.

Meanwhile, the $(window).scroll() event can be throttled — such that it is fired after every fixed interval, say, 200ms, instead of almost constantly as the user scrolls through the page.

Here is a non-exhaustive list of event handlers that can benefit from throttling or debouncing:

  • .resize() — debounce or throttle, depends on your needs
  • .scroll() — throttle
  • .keyup() — throttle, especially when making AJAX calls (e.g. live search)

Houston, we’ve got a problem!

“What have you tried?”

This is the comment very often seen on community-powered forums — so commonly seen and yet so little value it adds to the discussion that it has been banned from Stackoverflow. Meanwhile, software developer Matt Gemmell gives a thorough analysis of this eternally popular phrase.

There are many ways your script can break — a missing semicolon, an extra period, a missing closing bracket or brace, undeclared variables, attempting to access variables beyond the function’s scope and etc.

Multiple IDs

Remember that it is not semantically valid to have non-unique IDs on the page. Therefore, caution should be taken when cloning elements that has its ID attribute specified. A way to circumvent this is to avoid using ID on elements that should be duplicated, or empty the attribute itself before re-inserting the cloned object into the DOM:

$('#toClone').clone().attr('id','').appendTo('#newLocation');

In the event that multiple IDs are detected, jQuery will usually pick out the first instance — and this depends on the behavior of the browser’s JS engine. We cannot depend on this behavior as it may be different from one browser to another.

Accidentally using curly quotes

Curly quotes are not interpreted as dumb/typewriter quotes. The following code will give you an error:

// Problematic: using curly quotes!
$(‘p’).on('click', function(){ alert(‘Hey!’); });
// All clear: using dumb/typewriter quotes.
$('p').on('click', function(){ alert('Hey!'); });

Accessing variables beyond scope

A common mistake is to attempt to access variables that are beyond a funciton’s scope, as seen in the following example:

$('p')
.mouseover(function() {
var msg = $(this).data('msg');
})
.find('a')
.click(function() {
alert(msg);
});

You will be thrown the following error:

Uncaught ReferenceError: msg is not defined.

Simply because the msg variable is only accesible within the anonymous function triggered by the mouseover event on <p>. You should, instead, declare msg outside the anonymous function instead:

var msg;
$('p')
.mouseover(function() {
msg = $(this).data('msg');
})
.find('a')
.click(function() {
alert(msg);
});

Check your browser console

The browser’s inspector does more than simply help you to check how CSS is applied to your pages, or how the final markup of a page looks like. It also doubles as a convenient tool that helps you track for error messages.

When the browser’s JavaScript engine encounters an error, it will log a message in the console. The message usually also includes the line in which the error originated. Check in the near vicinity of that line. What do you find?

Debugging your script

JSLint and JSHint are easy-to-use, idiot-proof tools that help to break down your JavaScript code and catch a myriad of possible errors in the file — they also offer help in optimizing your script, such as suggesting variables to be removed when they are declared, but remain unused, throughout your script.

Don’t use alert().
Use console.log() instead.

You can also debug your code manually but logging a message in your browser console using the command console.log() — which is extremely convenient and helpful when you need to figure out if you are getting values that you intend.

console.log($(this).index());

The problem with using alert() is that it pauses the execution of the script until it is dismissed, and can be massively annoying if you have inserted it in a loop. console.log(), on the other hand, is not intrusive and can be easily access through the browser’s console.

Note: the console object does not exist in Internet Explorer until developer tool is opened — which, not surprisingly, follows the anti-pattern that Microsoft is very well known of. Paul Irish has provided a very convenient fix:

window.log = function(){
log.history = log.history || [];
// store logs to an array for reference
log.history.push(arguments);
if(this.console){
console.log(Array.prototype.slice.call(arguments));
}
};

Concluding remarks

jQuery is a powerful library — and also a very useful one, which anoints its position as the most popular JavaScript library as we speak. There are plenty of ways to make jQuery do something you want, and there is often more than correct solution. The trick is to ensure that your code runs efficiently, and that you exploit the best of what jQuery can offer.

If you find that I have left out some tricks that you find absolutely useful, do drop me a note on Twitter — I’ll be more than happy to do a follow-up. If you have found any mistakes, do let me know, too.

For now, all I can bade you goodbye with is, have fun writing awesome jQuery!

--

--

Terry Mun
Coding & Design

Amateur photographer, enthusiastic web developer, whimsical writer, recreational cyclist, and PhD student in molecular biology. Sometimes clumsy. Aarhus, DK.