Writing Better jQuery Code

Writing-Better-jQuery-Header

By Mathew Carella

There are a lot of articles that discuss jQuery and JavaScript performance. However, in this article I plan to summarize a bunch of speed tips and some of my own advice to improve your jQuery and JavaScript code. Better code means faster apps and jank free websites. Fast rendering and reactivity means a better user experience.

First of all, it is important to keep in mind that jQuery is JavaScript. This means we should adopt the same coding conventions, style guides and best practices for both of them.

First of all, if you are new to JavaScript, I recommend that you to read this article on JavaScript best practices for beginners and this one on writing high quality JavaScript before starting to mess with jQuery.

When you are ready to use jQuery, I strongly recommend that you follow those guidelines:

Var Caching

DOM traversal can be expensive, so try to cache selected elements when they will be reused.

// bad

h = $('#element').height();
$('#element').css('height',h-20);

// good

$element = $('#element');
h = $element.height();
$element.css('height',h-20);

Avoid Globals

With jQuery, as with JavaScript in general, it is best to ensure that your variables are properly scoped within your functions.

// bad

$element = $('#element');
h = $element.height();
$element.css('height',h-20);

// good

var $element = $('#element');
var h = $element.height();
$element.css('height',h-20);

Use Hungarian Notation

Putting the dollar symbol at the front of variables makes it easy to recognize that this item contains a jQuery object.

// bad

var first = $('#first');
var second = $('#second');
var value = $first.val();

// better - we use to put $ symbol before jQuery-manipulated objects

var $first = $('#first');
var $second = $('#second'),
var value = $first.val();

Use Var Chaining (Single Var Pattern)

Rather than having multiple var statements, you can combine them into a single statement. I suggest placing any variables without assigned values at the end.

var 
  $first = $('#first'),
  $second = $('#second'),
  value = $first.val(),
  k = 3,
  cookiestring = 'SOMECOOKIESPLEASE',
  i,
  j,
  myArray = {};

Prefer ‘On’

Recent versions of jQuery have changed whereby functions like click() are shorthand for on('click'). In prior versions the implementation was different whereby click() what shorthand for bind(). As of jQuery 1.7, on() is the preferred method for attaching event handlers. However, for consistency you can simply use on() across the board.

// bad

$first.click(function(){
	$first.css('border','1px solid red');
	$first.css('color','blue');
});

$first.hover(function(){
	$first.css('border','1px solid red');
})

// better
$first.on('click',function(){
	$first.css('border','1px solid red');
	$first.css('color','blue');
})

$first.on('hover',function(){
	$first.css('border','1px solid red');
})

Condense JavaScript

In general, it is preferable to try to combine functions wherever possible.

// bad

$first.click(function(){
	$first.css('border','1px solid red');
	$first.css('color','blue');
});

// better

$first.on('click',function(){
	$first.css({
		'border':'1px solid red',
		'color':'blue'
	});
});

Use Chaining

Following on the above rule, jQuery makes it easy to chain mathods together. Take advantage of this.

// bad

$second.html(value);
$second.on('click',function(){
	alert('hello everybody');
});
$second.fadeIn('slow');
$second.animate({height:'120px'},500);

// better

$second.html(value);
$second.on('click',function(){
	alert('hello everybody');
}).fadeIn('slow').animate({height:'120px'},500);

Keep Your Code Readable

When trying to condense your scripts and utilize chaining, code can sometimes become unreadable. Try to utlize tabs and new lines to keep things looking pretty.

// bad

$second.html(value);
$second.on('click',function(){
	alert('hello everybody');
}).fadeIn('slow').animate({height:'120px'},500);

// better

$second.html(value);
$second
	.on('click',function(){ alert('hello everybody');})
	.fadeIn('slow')
	.animate({height:'120px'},500);

Prefer Short-circuiting

Short-curcuit evaluation are expressions evaluated from left-to-right and use the && (logical and) or || (logical or) operators.

// bad

function initVar($myVar) {
	if(!$myVar) {
		$myVar = $('#selector');
	}
}

// better

function initVar($myVar) {
	$myVar = $myVar || $('#selector');
}

Prefer Shortcuts

One of the ways to condense your code is to take advantage of coding shortcuts.

// bad

if(collection.length > 0){..}

// better

if(collection.length){..}

Detach Elements When Doing Heavy Manipulations

If you are going to do heavy manupipulation of a DOM element, it is recommended that you first detach it and then re-append it.

// bad

var 
	$container = $("#container"),
	$containerLi = $("#container li"),
	$element = null;

$element = $containerLi.first(); 
//... a lot of complicated things

// better

var 
	$container = $("#container"),
	$containerLi = $container.find("li"),
	$element = null;

$element = $containerLi.first().detach(); 
//...a lot of complicated things

$container.append($element);

Know the Tricks

When using methods within jQuery that you may have less experience with, be sure to check the documentation as there may be a preferable or faster way to use it.

// bad

$('#id').data(key,value);

// better (faster)

$.data('#id',key,value);

Use Subqueries Caching Parents

As mentioned earlier, DOM traversal is an expensive operation. It is typically better to cache parent elements and reuse these cached elements when selecting child elements.

// bad

var 
	$container = $('#container'),
	$containerLi = $('#container li'),
	$containerLiSpan = $('#container li span');

// better (faster)

var 
	$container = $('#container '),
	$containerLi = $container.find('li'),
	$containerLiSpan= $containerLi.find('span');

Avoid Universal Selectors

When combined with other selectors, the universal selector is extremely slow.

// bad

$('.container > *'); 

// better

$('.container').children();

Avoid Implied Universal Selectors

When you leave off the selector, the universal selector (*) is still implied.

// bad

$('.someclass :radio'); 

// better

$('.someclass input:radio');

Optimize Selectors

For example, using an ID should already be sufficiently specific, so there is no need to add additional selector specificity.

// bad

$('div#myid'); 
$('div#footer a.myLink');

// better
$('#myid');
$('#footer .myLink');

Don’t Descend Multiple IDs

Again, when used properly, ID’s should be sufficiently specific not to require the additional specificity of multiple descendant selectors.

// bad

$('#outer #inner'); 

// better

$('#inner');

Try to Use the Latest Version

The newest version is usually the best one: sometimes lighter and sometimes faster. Obviously, you need to account for compatibility with the code you are supporing. For example, don’t forget that from version 2.0 there’s no more support for IE 6/7/8.

Don’t Use Deprecated Methods

It is important to always keep an eye on deprecated methods for each new version and try avoid using them.

// bad - live is deprecated

$('#stuff').live('click', function() {
  console.log('hooray');
});

// better
$('#stuff').on('click', function() {
  console.log('hooray');
});

Load jQuery Code from a CDN

The Google CDN quickly delivers the script from the user’s nearest cache location. To use the Google CDN, use the following url for this http://code.jQuery.com/jQuery-latest.min.js

Combine jQuery with Native JavaScript When Needed

As I was saying before, jQuery is JavaScript, and this means that in jQuery we can do the same things we do with native JavaScript. Writing in native (or vanilla) JavaScript can somtimes mean less readable and less maintainable code and longer files. But it also can mean faster code. Keep in mind that no one framework can be smaller, lighter and faster than native JavaScript operations. (Note: click the image to run the test)

jq

Due to this performance gap between vanilla JavaScript and jQuery, I strongly recomend mixing both of them in a wise way, using (when you can) the native equivalent of jQuery functions.

Final Considerations

Finally, I recommend this article on increasing jQuery performance that includes a number of other good practices that you will find interesting if you want a deeper look about the topic.

Keep in mind that using jQuery isn’t a rquirement, but a choice. Consider why you are using it. DOM manipulations? Ajax? Templating? CSS animations? A selector engine? Sometimes, it may be worth considering a micro JavaScript framework or a jQuery’s custom buildthat is specifically tailored on your needs.

This article was originally published at http://blog.mathewdesign.com/2013/11/14/writing-performant-and-quality-jquery-code/

28 Comments

  1. Tim Severien said:

    The only thing new to me is detaching elements which makes a lot of sense.
    Like var caching, it’s good practise to cache functions too. In most cases it will have little effect on the performance, but in many cases it can make a significant difference.
    Also, like vanilla JS, break out of loops (in jQuery by returning false) if you don’t need to iterate over other elements.

    Sometimes, jQuery is overkill because there’s so much functionality you don’t need. If you still want to rely on the jQuery framework you could consider making your own build: http://projects.jga.me/jquery-builder/

  2. Alex said:

    One thing about the last deprecated “live” – your example is wrong as it implies that #stuff would be in the future in which case it could not be bound at document.ready. Instead it should be $(‘body’).on(‘click’,'#stuff’,function(……..

    Other than that, good tips for beginners

    Kudos

  3. Frank said:

    This post should be titled, “How I write jQuery” instead of the title you gave it because clearly there are few practices that I would never agree with and don’t make any sense to me, perhaps you do it that way because you like it but that doesn’t automatically put it in the “jQuery Book of Law”. Other practices do make sense but then again, coding practices and specially coding styles are a very personal thing and changes from individual to individual.

    • Guilherme said:

      Many of the “tricks” are widely used today. Look at the source of some famous jq plugins and you’ll find almost all that has been said in this article

  4. Julian said:

    A lot of these ring true with me. I have always been under the impression that you should use ‘scopes’ when writing jquery too:

    $(‘. some_class’, ‘. parent_class_scope’). hide() ;

    Is this still best practice?

    • jay said:

      for me it is the better approach when traversing the DOM especially when you already “var cached” the parent element. :)

  5. Andreas said:

    Your example of: $.data(‘#id’,key,value); does not work. $.data() doesn’t take a jQuery selector as the first argument, but a DOM element. So in this case one of the following would work:

    1:
    $.data(document.getElementById(‘id’),key,value);

    2:
    var el = $(‘#id’)[0];
    $.data(el,key,value);

    3:
    var el = $(‘#id’).get(0);
    $.data(el,key,value);

  6. Nicolas said:

    There are several things wrong with this, but just to point some things out:
    1) If you’re going to use css to set the height, you can use a function:
    $element.css(‘height’, function(index, old) { return old – 20; });

    2) “Use Var Chaining” it’s acctually not the best to write code, and it’s quite discouraged (if you forget a comma you get a lot of lovely global variables)

    3) “Prefer Shortcuts” it’s just wrong, checking if the value is false it’s a completely different think than checking if it’s greater than zero

    4) The jsperf link doesn’t work

    5) jQuery.data doesn’t take a selector: http://api.jquery.com/jQuery.data/

    6) “Use Chaining” it’s a nice way to write jquery, but it really depends on the context. I really think that your first example is way more readable than the second one.

    Thanks for the deatach method, I did not know that!

  7. Dan said:

    Every few months I see another new article that teach us how to write a good jQuery…
    And they all says the same.

  8. Bob said:

    Written by a web designer power user, not a programmer. Granted, there are some good tips, but there are also some rather poor and potentially problematic practices.

  9. lolmaus said:

    Does the «Use Chaining» recommendation provide any performance benefits? I doubt so.

    I believe it makes the code harder to grasp (and weird indentation doesn’t make it better). Thus, i find it to be a bad practice.

  10. Chris said:

    If you use jQuery inside an AngualarJS Project, the hungarian Notation should NOT be used like this, since Angular claims the $-prefixed vars.

  11. Pingback: Writing Better jQuery | All My Tech Talk

  12. Dayton Nolan said:

    $(‘#stuff’).on(‘click’, function() {
    console.log(‘hooray’);
    });

    This is not the same as using live. To get the same behavior you must do:

    $(document).on(‘click’, ‘#stuff’, function() {
    console.log(‘hooray’);
    });

    Preferably using a root element closer to the intended target.
    Also let’s toss in never using anonymous functions. How can you test an anonymous function. Which leaves another tip: TEST DRIVE YOUR JAVASCRIPT.

  13. Lee said:

    Chris, I disagree with you on that. Just because Angular uses some variables which have Hungarian notation doesn’t mean you’re no longer allowed to use your own vars which are $ prefixed. Angular certainly doesn’t “claim” your $ prefixed vars as you suggest!

  14. jay said:

    in “Load jQuery Code from a CDN”. does it need to require a fallback incase of a script loading failure?

  15. Pingback: A Couple of jQuery code considerations | Roxstyle

*

*

Top