Quick Tip – jQuery Newbs: Stop Jumping in the Pool
basix

Quick Tip – jQuery Newbs: Stop Jumping in the Pool

Tutorial Details

As editor of Nettuts+, I get to review a lot of code from tutorial submissions. Despite the fact that jQuery has been available for years now, there is still one frequent mistake that I see more than anything else.


Example #1

Consider the following bit of code:

$('.nav a').click(function() {
  $(this).hide();
  $(this).css('color', 'red');
  $(this).show();

  alert('something else');

  $(this).hide();

  return false;
});

The code above is overly complicated for a variety of reasons. Don’t worry over what the code actually does (it’s gibberish). Instead, I want you to look at all of those references to $(this).

Think of the DOM as a pool.

Think of the DOM as a pool. Remember when you were a kid, and would dive into the pool for coins, while your parents acted like they were watching? That will be our real-world comparision.

Every time you use $('.someClass'), jQuery jumps into the pool (DOM), and searches for that coin (or nodes). So, when you reference it multiple times within a function, that’s a lot of diving. Or, to cut the real-world comparison, it’s wasteful and unncessary. Why call upon jQuery if you don’t require it? You should perform what we call “caching.”

$('.nav a').click(function(e) {
   var anchor = $(this);

   anchor
      .hide()
     .css('color', 'red')
     .show();

   alert('something else');

   anchor.hide();

   e.preventDefault();

});

This is much cleaner. While modern browser engines are incredibly fast these days, and will make up for your poor coding as best as possible, you should still strive to write efficient code, and keep from wasting all that energy jumping in the pool. Now, technically, if you pass jQuery a DOM node, like this, it doesn’t re-query the DOM. It simply returns a jQuery object.

Just because the performance difference between the two will honestly be negligible, we write clean code for ourselves.

Example 2

Let’s consider a slightly more complicated example: tabs.

$('.tabs li').css('position', 'relative');

$('.tabs li').click(function() {
   $('.tabs li').removeClass('active');
   $(this).addClass('active');
   $(this).load('someHref', function() {} ); // example
   $(this).css('top', '1px');
});

This code is all over the place. It’s ugly, and inefficient. Fix number one is to get rid of all that CSS. You’d only place styling in your JavaScript if the values were created dynamically. For example, if you need to calculate the precise location an element should be on the screen, you could use .css('left', calculatedValue). In this case, it can all be exported to an external stylesheet. That leaves us with:

$('.tabs li').click(function() {
   $('.tabs li').removeClass('active');
   $(this).load('someHref', function() {} ); // example
   $(this).addClass('active');
});

Next, again, why do we keep querying the DOM for .tabs li and $(this)? Stop jumping in the pool. Let’s “cache” the location of .tabs li.

var tabs = $('.tabs li');

tabs.click(function() {
   tabs.removeClass('active');
   $(this).load('someHref', function() {} ); // example
   $(this).addClass('active');
});

Better, but we’re still calling $(this) twice, which isn’t a huge deal. But, from my experiences, if you don’t nip it in the bud early, this number quickly increases.

var tabs = $('.tabs li');

tabs.click(function() {
   var tab = $(this);
   tabs.removeClass('active');
   tab.addClass('active')
     .load('someHref', function() {} ); // example
});

Filtering

Another (slightly less optimized) option would be to use filtering.

var tabs = $('.tabs li');

tabs.click(function() {
   tabs.removeClass('active')
       .filter(this)
       .addClass('active')
       .load('someHref', function() {} ); // example
});

The difference in this case, is that, rather than referencing $(this), we’re using the filter() method to reduce the collection of list items down to only the one that was clicked.


What You Should Take Away

Yes, the world will not end if you reference $('.tabs) several times within a function. JavaScript engines are super fast these days. If you were to test the performance of doing so thousands of times, the difference in execution might be a couple hundred milliseconds. But still, the question remains: why would you?

Sometimes, when we use massive abstractions like jQuery, it’s easy forget that $('.tabs') is an actual function that runs a good bit of code. It should also be noted that these concepts apply to JavaScript in general – not just jQuery.

Use the caching techniques described above to write cleaner code…for yourself.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://donysukardi.com Don

    Thanks for the wonderful tips! I realize that I’ve been jumping in the pool all this while!

  • http://www.kumarchetan.com kumar chetan sharma

    IMHO example 1 and example 2 should mention that these are JS best practices rather than only jQuery best practices. You should “avoid” querying DOM as much as possible.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      It’s mentioned at the bottom.

      • Andrew

        Although i agree about having the $(this) as a variable after first use, I think the rational you’ve provided is wrong.

        jQuery won’t search the dom when it already has a DomNode, it just wraps it as a jQuery object, (I actually think jQuery caches those jQuery instances internally so it just does a key lookup in the cache)

        So the jQuery part isn’t really a concern (atleast not for the $(this) example.. if you actually are searching, $(“.someClass or something”) then everything you’ve said is correct.) Instead, Javascript’s scoping rules combined with the cost of invoking yet another function, are more interesting. If you make the variable local to the scope that it’s used in, then the JS engine won’t have to search up the invocation stack very far to figure out what variable you are using. Especially since jQuery/$ is usually Global, which is the most expensive to find, plus the function call (not major, but still it adds up).

        Also the whole style thing, it just looks better.

      • http://www.jeffrey-way.com Jeffrey Way
        Author

        Andrew – that’s true about passing a DOM node to jQuery. But it still has to be run through jQuery. So it makes sense to “cache” it. But yeah, I agree with what you’re saying.

  • Patrick

    You’re absolutely right.

  • http://www.dadesigners.com ali nouman

    Thanks very much for the article Sir.

  • http://www.andredublin.com Andre Dublin

    I hate to say it, but guilty.

  • http://n/a Simon

    Yep, this is a big issue. Often see it in example slices of code.

    Good tips.

    FYI – You’ve got a mistyped variable in the code snippet just above the “Filtering” header.

  • Rey

    Thanks!!

  • http://www.wdonline.com Jeremy McPeak

    I love the DOM/Pool analogy.

  • http://abderrahmane-tj.co.cc Abderrahmane TAHRI JOUTI

    a reminder is always a good thing ^^

    by the way you’re missing a ‘ in the Filtering example (line: six)

  • http://www.twitter.com/juniorjoanis Junior Joanis

    Great tips! I didn’t use “caching” in Jquery, now I will ;)

  • Sylwester

    Huh, nice to know.

    I thought that this keyword is the reference to the DOM element, so there’s no need for jQuery do dive into the pool, because we did it once and now have reference “this”.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      For the “this” keyword specifically, yes. When you pass jQuery a DOM node, it doesn’t specifically query the DOM. But it’s still a good practice to get into. You don’t need to pass “this” to jQuery multiple times.

  • http://robertbue.no Robert

    addClass(‘active), missing a ‘ some places as mentioned above

  • http://www.sleekd.com Raz

    Short and to the point. Thanks!

  • hugh

    I just ran some testing to really quantify this. Using the below javascript and firebug’s profile ability. You can see the exact file I used here at pastebin.

    var test = {};

    test.withoutCache = function(){
    $(this).data(‘test’, 1 );
    $(this).data(‘test’, 2 );
    }
    var self = jQuery(test);
    test.withCache = function(){
    self.data(‘test’, 1 );
    self.data(‘test’, 2 );
    }

    Here’s the output from Firebug’s profile, 3 runs of each function.
    > test.withoutCache() – Profile (0.337ms, 51 calls)
    > test.withoutCache() – Profile (0.52ms, 51 calls)
    > test.withoutCache() – Profile (0.415ms, 51 calls)
    > test.withCache() – Profile (0.763ms, 39 calls)
    > test.withCache() – Profile (0.256ms, 39 calls)
    > test.withCache() – Profile (0.26ms, 39 calls)

    Then I just did a simple for loop and ran each one 1000 times in order to smooth out minor execution times…
    Profile (250.272ms, 51000 calls)
    >>> for( var i = 0; i < 1000; i++ ){ test.withoutCache(); }
    Profile (202.166ms, 39000 calls)
    >>> for( var i = 0; i < 1000; i++ ){ test.withCache(); }

    It appears that without cache is about 25% more expensive in terms of time in this particular test (250ms vs 200ms), and causes 30% more function calls (51 vs 39)

    • hugh

      Oops, forgot the pastebin link: http://pastebin.com/HCkXtW28

      • http://benmartinstudios.com.au Ben

        Interesting test Hugh. I’d be very interested to see similar tests in a browser like IE7/8, just not sure how to test it ;)

    • hugh

      That is a valid point. I modified the test slightly. http://pastebin.com/ppE6seaj
      function nestDiv( parent, total ){

      var element = $(“”).appendTo( parent );
      var repeat = total – 1;
      if( repeat > 0 ){
      var i = 0;
      for( i = 0; i < 10; i++ ){
      nestDiv( element, repeat );
      }
      }
      }

      var test = {};
      test.withoutCache = function(){
      var i = 0;
      for( i = 0; i < 1000; i++ ){
      jQuery(".test").data('test', i );
      }
      }

      test.withCache = function(){
      var self = jQuery(".test");
      var i = 0;
      for( i = 0; i < 1000; i++ ){
      self.data('test', i );
      }
      }

      jQuery(document).ready( function(){
      nestDiv( $('body'), 5 );
      jQuery('div').eq(500).addClass('test');
      });
      Ok, this version creates 11,111 divs (arbitrarily), nesting them up to 5 deep in the hierarchy.
      It then picks the 500th div (arbitrarily) and applies the class "test" to it.
      test.withCache and test.withoutCache are then run.
      Results using straight DOM querying via selector:
      test.withCache();
      Profile (109.735ms, 15011 calls)
      test.withoutCache();
      Profile (189.083ms, 25001 calls)
      Ok so after that I decided to make it use the "this" context as the original article points out. http://pastebin.com/TPxxdPSV
      Once again, 11,111 divs, but this time the javascript is as follows:
      jQuery(document).ready( function(){
      nestDiv( $('body'), 5 );
      jQuery('div').eq(500).addClass('test');

      jQuery('.test').bind('without_cache', function(){
      var i = 0;
      for( i = 0; i < 1000; i++ ){
      jQuery(this).data('test', i );
      }
      });

      jQuery('.test').bind('with_cache', function(){
      var self = jQuery(this);
      var i = 0;
      for( i = 0; i < 1000; i++ ){
      self.data('test', i );
      }
      });
      });
      Now I find these results interesting, due to a virtually negligible difference.
      Results using "this" context variable:
      var test = jQuery('.test');

      test.trigger('with_cache')
      Profile (100.184ms, 15067 calls)
      test.trigger('without_cache')
      Profile (108.723ms, 17065 calls)
      Conclusion would be that the "this" context is not nearly as expensive as actually querying the DOM.

  • http://blog.lastrose.com Gabriel

    I disagree a bit with the statement “You’d only place styling in your JavaScript if the values were created dynamically.”

    In some instances when dealing with fallbacks you should use javascript to alter css.
    I refer for example to a tab group. by default you would want all tabs to be visible for users without js, and then hide/alter them them for people with js. While this can be accomplished by other methods (body class=”nojs” and remove that class with jquery), assuming that you have little control over it, then the best bet would be to use jquery to set the css. (not the best example but the first I could think of)

    Another reason to use jquery to set the css is to override the stylesheet. This can sometimes be the case when you are developing a plugin.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      Oh sure there are some instances where it makes sense. But, from experience, people tend to add far too much CSS in their JS. CSS that could easily be applied as a class.

      • http://nathanrjones.com Nathan Jones

        My take on it is if it is impossible for the user to interact with the application properly without this line of css, keep it in your JavaScript. If it is merely for look and feel, get it out of there!

        That said, I only do this because I typically use MVC frameworks and keep the CSS outside of the controller.

    • http://askia-it.fr Mattew

      Gabriel, hiding or showing elements if Javascript is present should always be done whith css (explaination just after). I know what i’m talking about because if ever you will had a lot of js files in a project you will appreciate to get rid of all this $(selector).hide(); and $(selector).show() all over your javascript code. Trust on me, the best pratice for nearly all case of hiding element when Javascript is active is just to add one time a “js” (or whatever you whant for the name) on the body tag using Javascript and write your exceptions in the css.

      For example, if you have a hidden alert box which is opening with javascript but is directly visible without :

      <div class=”my-alert-box”>
      <a href=”#”>Show the message</a>
      <div>You should upgrade your account!</div>
      </div>

      .my-alert-box {
      background: red;
      color: red;
      }
      .my-alert-box a {
      color: red;
      font-weight: bold;
      }

      .js .my-alert-box a,
      .my-alert-box.show div {
      display: block;
      }

      .js .my-alert-box div,
      .my-alert-box a {
      display: none;
      }

      Here, you could show your box with js using .show(). Or show it using your CSS by adding the “show” class on it. It’s often usefull to have a class on the element regarding to his state (shown or hidden).
      You can note that i have hidden the link when there is no Javascript, because the box is already shown !

      I concentrate my js code on the fonctionnal part, not just dealing with graphic aspect all the time with many hide() and show() everywhere.

      Trust me, i had to write big JS and i’m CSS expert and almost never use .css(). It’s usefull for dynamic coordinates often, but most of the time, changing colors, backgrounds, or so, should be done with CSS because it’s just graphic design.

    • http://askia-it.fr Mattew

      Sorry, according to the article “Do You Suffer From the Dunning-Kruger Effect? ” I don’t want to proclaim myself as a CSS expert. I would say that i’m nearly good in CSS ;)

  • Dieter

    Very good article, Jeffrey.
    it sounds so obvious, but I must admit that I was guilty…
    Thank you.

  • http://eichefam.net Paul

    It was nice to see an article on this – and I like the term “diving into the pool.”

    Not too long ago, perhaps a few months, I got into the habit of caching right away inside functions, like so:

    $(‘a’).click(function() {
    var $this = $(this);
    // do something
    }

    Even if it doesn’t have any discernible impact on execution time, it certainly makes for cleaner, easier-to-read code.

    Thanks again for the tip!

    • http://kylehayes.info Kyle

      Thank you for posting this article. While I’m glad so many folks are happy to write JavaScript with jQuery now because it makes it so easy, it also makes me cringe when I see these kinds of issues. With the risk of sounding cliché, I think it makes it _too_ easy and does not encourage people to learn what is actually going on under the hood.

      • //nerdswithguitars.ca Sean

        Absolutely.
        Sadly, striving to build a more idiot-proof product only succeeds in building better idiots.

        That’s not meant as a slight against anyone, but when a system goes out of its way to cover for all of the mistakes a user can make, without the user ever having to so much as learn they’re making mistakes – let alone how not to make them, and why they’re mistakes in the first place – it leads to mountains of people who “learn” by copy and pasting weak/compromised/terribly-slow code, and expanding on it, with even more slow code, filled with a new breed of misunderstanding.

        Then, all of a sudden, you have broken apps and frameworks with all kinds of compatibility problems, and you have end-users who deal with slower or selectively-compatible experiences all for the sake of having a fade-in or two.

        You have apps which depend on the “painter’s algorithm”, everywhere, built on top of, or extending frameworks which depend on the “painter’s algorithm”, everywhere, and all of a sudden, you’re doing thousands or millions of checks in the lifetime of your program, which you wouldn’t have had to do, if cached or if you filtered your results first, and then cached that filtered list.

  • http://dangelzm.ck.ua DangelZM

    Good article. Thanks!

  • http://www.thoughtresults.com Saeed Neamati

    Great notes. I think the most important thing any beginner developer of jQuery should bear in mind while coding is chaining, and caching. These are enough at elementary steps.

  • Jon

    Great examples but in a number of them you are missing a few single quotes to close out the quoted text.

  • http://www.ashmenon.com Ash Menon

    Out of curiosity, you mentioned that filtering is a slightly less optimized method of doing things, and to me (or at least to my way of coding) it looks somewhat lengthier than just assigning it to variable names like in the examples before it. Any advantages to filtering?

  • http://www.imstillreallybored.com Josh Bedo

    Nice tip sometimes i catch myself doing this even after years of writing jQuery but i usually go back and simplify it.

  • http://simonwjackson.com Simon W. Jackson

    I know I was guilty of Example #1 when i was first starting with jQuery :/

  • http://www.atmedia.co atmedia.co

    I’m guilty as charged! haha

  • http://www.dev-hq.net/ Joe

    I’ve been looking at jQuery and this article has helped me a lot, thanks!

  • Ravindran

    Nice article.

    Another quick suggestion. If we have to avoid using tabs (global variable) inside the click handler / it’s not available,

    var tabs = $(‘.tabs li’);

    tabs.click(function() {
    $(this).addClass(‘active’)
    .load(‘someHref’, function() {})
    .siblings().removeClass(‘active’);
    });

    Most likely, we might want to remove ‘active’ class from other elements and add it to the current one.

    • http://eddiemonge.com Eddie Monge

      Its not always siblings though. Sometimes you have to do .parent().find(‘li’) or some other tree climbing. IF you wrap all your code in a closure then you dont have to worry so much about global vars.

    • http://askia-it.fr Mattew

      I agree with the siblings() method exposed by Ravindran, and was thinking about it when reading the example code (to test if i found the error ;) )

      I think that the algorithm of your second final example code is less readable because the filter() method makes us lost the flow of the code. It’s more complicated to read than siblings() because siblings() = “return all brothers” it’s simple and direct, filter() = “find all the element matching the selector”, it’s longer to think and to notice visualy (i know, the code don’t have to be visual, but sometimes it helps and it is not more expensive). Personnaly i was lost for seconds when reading this code.

      It’s a ridiculous detail, but i think it’s less fluid using filter()…

      Thanks for this article !

  • andres

    In Example # 1, why is “e.preventDefault()” any better than “return false” ?

    • http://www.wdonline.com Jeremy McPeak

      If I remember correctly, return false in a jQuery event handler is the same as calling preventDefault() and stopPropagation(). Usually, you don’t want to stop propagation (at least in my opinion).

  • http://net.tutsplus.com Jeffrey Way
    Author

    Correct. If you simply want to disable the default action of, say, clicking on an anchor tag, it’s smarter to use e.preventDefault();

  • http://none Alex

    Thanks a lot mr.Way! nice tuts!

  • Henson

    there is one inconsistency in your example. In Example 1, you cache inside the click event, while in Example 2, you cache outside the click event. Which one is better?

  • Chris

    I agree that you shouldn’t query the DOM unnecessary often. But honestly, you all think that

    var anchor = $(this);
    anchor
    .hide()
    .css(‘color’, ‘red’)
    .show();

    is CLEANER than this:

    var anchor = $(this);
    anchor.hide();
    anchor.css(‘color’, ‘red’);
    anchor.show();

    ???
    In my opinion, the first one is pretty dirty and not speaking code. It slaps object orientation and jumps around on it ;-)
    And, for example, just think of situations, where you change the order of the three calls. You’ll mess it up when you forget to update the final semi-colon.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      It all comes down to preference, but why would you want to type more than you need to? It’s redundant.

    • http://askia-it.fr Mattew

      Changing the order is not very usefull except in some case because this three actions will be executed in a millisecond… That’s why this example code is just an example code, it’s stupid to show a block, add a border, and hide it just after without delay…
      Sorry but the first is definitely the best ;) and it’s more readable with indentation afterwards.

  • Linish

    Nice tutorial :D

  • Denny Dzul

    Geewiz … I have been a pool guy … and it’s tiring. Great tutorial.

  • http://www.2021.com.au David

    Thanks for the quick tip, quite useful.

  • Ryan

    Great article, thank you! I’m sure much of this article was written out of frustration seeing poor coding techniques, however, keep in mind that there are many people out there who simply don’t know any better. Definitely keep writing these types of tips (and fantastic analogy using a “pool”, might I add), it’s even a nice reminder for experienced coders too! Thanks!

  • http://www.nightocoder.com Rawaf

    Great post, I found some tips will useful me with new project … I think you worked hard about this post thank you … really quite useful.

  • Jose Miguel

    Great post, very useful!! Thanks for that.

  • http://www.startutorial.com XuDing

    Thanks for these useful tips and we will definitely use it in our applications.

  • Richard

    In example 1, you swap “return false;” for “e.preventDefault()”.

    This is not always ‘correct’. What if you intend to stopPropogation too? returning false will do both, and calling e.stopPropogation() as well is an extra jquery function call which only leads to slower functions and more resources needed.

    Only in situations where you DO NOT want to stopPropogation() should you use e.preventDefault(). Otherwise you should use return false.

    I would like to understand your reasoning behind what you suggest is right about this. Especially as you are saying ‘newbs’ are doing it wrong this way.

  • Trip

    Like many front end devs these days, I’m quite good with jQuery, but because I’ve never done a lot of pure JS stuff, I’ve developed bad habits as I’ve learned and this article covers one of them – effeciency.

    It was a good read and I’m definitely going to start being more careful with my selectors.

  • Jon Hockley

    I think I’ve emptied my pool I jumped in it so much :/

    Thanks for the tip, I really had no idea.

  • http://www.icetrack.it/ daniel J

    Looks like i’ve been spending a lot of time in the ‘pool’ Although reading this article, makes me think, If i was to really think about things, I would write it this way, just sloppy coding i suppose!

    thanks for the memory jog!

  • http://www.kayseriemlaksayfasi.com KayseriEmlak

    nice shared thanks for everything..

  • miqureshi

    nice shared i liked it..