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/

Modern Web Newsletter

Subscribe to receive the Modern Web tutorials, sent out every second Wednesday.

  • http://timseverien.nl Tim Severien

    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/

  • http://www.nodex.co.uk Alex

    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

  • Jima

    :( some tricks are mad like
    if (ert.length) vs if (ert.length > 0)

    • http://none Guilherme
      • Chris Walsh

        It is true (sic!) that 0 is falsey.
        However there is some loss in readability so one must be careful when going to extremes with this kind of condensing.
        Also, (and I know that the length of an array cannot be negative) “> 0″ is not the same as “truthy” – many more novice developers could code the latter when the former is the correct solution for values that can take on negative values.
        Use with caution!

        • Mike

          I agree with this.

          There’s little point doing macro optimisations. Let the browser deal with it, better to concentrate on readable / maintainable code.

    • http://none Alex Bee

      Completely agree!

  • http://www.idonthaveawebsite.com Frank

    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.

    • http://none Guilherme

      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

  • http://derekpetey.com Derek
  • http://www.mediafrog.co.uk Julian

    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

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

  • http://deepfriedmind.com Andreas

    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);

  • http://no.com Nicolas

    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!

  • http://nowebsite Dan

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

  • Bob

    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.

  • lolmaus

    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.

  • http://campino2k.de Chris

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

  • Pingback: Writing Better jQuery | All My Tech Talk

  • Dayton Nolan

    $(‘#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.

  • http://google.com Denis

    .. $first.on(‘hover’,function(){ …
    OMG. Are you kidding or serious?

  • Lee

    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!

  • jay

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

  • http://expressjsguide.com Azat

    Check out this article Breaking Bad (Loops in JavaScript Libraries) for comparison on each/forEach methods in jQuery vs. others: http://webapplog.com/breaking-bad-loops-in-javascript-libraries/.

  • https://twitter.com/ajinair Ajith Nair

    Ha. I follow almost all of these already. :)

  • Pingback: A Couple of jQuery code considerations | Roxstyle

Top