Your jQuery: Now With 67% Less Suck

54 Comments

Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.

Got something to add? You can leave a comment below.

  1. Alex Michael

    This is some good advice – I’d like to add my stone on the wall. On the last example, tmp += “…” creates an intermediary string on every iteration, resulting in poor performance when dealing with large arrays. You should instead make tmp an array, push the li’s in, and then join(’‘) it.

  2. Jason Hummel

    If you’d rather not play around with all that messy string concatenation when performing costly DOM manipulations, give createDocumentFragment a try.

    var div = document.createElement(“div”);
    div.appendChild( document.createTextNode(“Test div to be inserted 100 times”) );

    var fragment = document.createDocumentFragment();

    for(i = 0; i < 100; i++) { fragment.appendChild( div.cloneNode(true) );
    }

    document.getElementById(“container”).appendChild( fragment.cloneNode(true)
    );

    Amazingly the browser support for documentFragments goes back to IE6.The benefit comes from the ability to add the entire fragment DOM tree into the page DOM with one insertion – just like the method outlined in the article.

  3. Rob L.

    Useful tips all. One mini-pattern that I’ve picked up from co-workers is, when caching selectors, prefix your variable with $ to note that it’s a jQuery object. This can be really helpful in methods where you’re dealing with lots of variables, especially when you come back later to modify them or fix bugs.

    var $blocks = $(”#blocks”).find(“li”);

  4. Joe Mottershaw

    This has been a very good read, it’s made me more aware of how sluggish my jQuery code actually is compared to how it could be!

    I have a question though, you mentioned caching, how does this fair with using $(this). For example, if you only called upon an element once in your script for a click detection, would you need to assign it to a variable first if that element was the same element also be altered? Some examples…

    1st Example

    $(”#div_id”).click(function() { $(this).hide(); });

    2nd Example

    var div_id = $(”#div_id”);
    div_id.click(function() { div_id.hide(); });

    Apologies for the bad example…
    But is there much of a difference in loading time between the two?

    Is $(this) a sort of variation of caching as it’s already selected the element and you’re just calling it back by saying “this one, the one you just found, yeah, use that”, or is it having to wade through all the code again to find it?

  5. Nice article.

    Last example can perform even better if you use array joining instead of string concatenation.

    var arr = [reallyLongArrayOfImageURLs], tmp = [];
    $.each(arr, function(count, item) { tmp.push(’<li><img src=”‘item’”></li>’);
    });
    $(’#imgList’).append(tmp.join(”“));

  6. Fantastic, specially liked the .find() optimization.
    For someone coming from ActionScript jQuery shares lots of optimizations.
    But DOM manipulation is the heavyweight here.

    I was expecting a mention to the second parameter of jQuery(), the context parameter. I was under the impression it narrowed the scope of the DOM search. Is it really faster? or is .find() better.

    I also have the doubt as to how does jQuery reads selectors. Is it like CSS (from right to left) or always goes for the ID first?

  7. Scott Kosman

    Thanks for the feedback and questions, everyone. I’ll try to get to them all in time, today’s going to be a little crazy around the office, though, but I’ll do what I can.

    One quick response in regards to selector performance, right-to-left, context vs. .find(), etc.: this jsperf example runs through various methods and in almost all browsers (Opera being the lone exception), .find() outperforms other selectors.

    Caveat: all methods get obliterated by native JavaScript (see revision 69 of the same test for proof – I created rev 70 and removed the native version just to make the graph readable), but still.

    @Andi: yes, those two methods will generally yield pretty similar results. Using .find() for me is more force of habit than anything else.

  8. JulienW

    As seen on this jsperf testcase : http://jsperf.com/various-jquery-testcases

    This is also way faster to use :

    $(document.getElementById(“foo”));

    than

    $(”#foo”);

    But really, is it really so important ? Most of the time, you just cache the result of the selector in a var once, and use it after this.

  9. Mathias Bynens

    Nice write-up, Scott!

    Just a few clarifications:

    * `document.querySelectorAll(’:hidden’)` doesn’t actually work; in other words, supporting QSA doesn’t affect the performance of jQuery/Sizzle’s custom selectors. (The text makes it seem like it does.)
    * The reason Opera scores so well in QS/QSA tests is because it caches the results. It would be tricky but interesting to create a performance test case that works around this by avoiding repeated selectors.

  10. Scott Kosman

    Good question, Bertram. I’d (almost, see note below) always recommend using the latest version of the library. The addition of .on() is only one change of many since 1.4, there have been an absolute ton of additions and improvements over the last few releases.

    Besides, once the libraries are minified and gzip’d the file size difference will only be (by my best estimate) about 6 or 7 kb.

    The only time I’d be wary of going with the latest version is when updating the version of jQuery used in an existing project. Though updating is almost always painless, the chance of something breaking in your existing code when moving to a new version of jQuery is always present. When I’m using the Google Libraries API to load jQuery (which is almost every project) I will always specify the exact point version I’m loading rather than relying on it to just feed me the latest version. When a new version of the library gets rolled out I’ll switch to the updated version in a dev environment and make sure nothing’s broken before updating on live.

  11. Scott Kosman

    Bam! Here it is!

    http://www.learningjquery.com./2009/03/43439-reasons-to-use-append-correctly

    It’s a couple of years old but the idea is still solid. Pushing values into an array on each iteration and then using $(“whatever”).append(myreallylongarray.join(’‘)); to do your heavy lifting shows HUGE performance boosts. In retrospect this is the “right way” example I should have used for the DOM Manipulation section of my article, but hindsight is 20/20, etc.

  12. Alex Rohde

    Interesting article, objective, useful, and well-supported. I really liked the part on event delegation.

    One thing I’m noticing is a schism in the developer community around conflicting sets of standards (performance, aesthetics, reusability).

    For example, though inserting inline HTML for DOM manipulaiton may be the fastest method, I recently was turned down an interview because a code sample did just this (which was a violation of separation of concerns or some such philosophical thing).

  13. Thanks for this great article !

    Here’s the test for the last part, ‘DOM Manipulation’, quickly thrown together, with a notSoLongImageArrayFromFlickr:

    http://jsperf.com/jquery-dom-manipulation

    Now, off to test it with a lot of different browsers :)

  14. JulienW

    I also strongly disagree with the last part. Not about its speed but about its security. Really, doing string concatenation should light a warning in your head ! And I keep seeing these advice on every website talking about jQuery !

    Really, what is faster here is that you don’t access the node at each step of the loop. You can do this by adding elements to an “off-DOM” node. Yes, it will probably be slower than string concatenation, but still way faster than using an attached DOM node.

    So instead of using string concatenation, use jQuery’s “attr” function on a unattached DOM, and it should be nearly as fast.

    jsperf testcase to demonstrate this

    (feel free to fiddle with the testcase as it doesn’t seem so reliable on my firefox right now)

  15. Andreas Lagerkvist

    Some nice tips here. I’m curious, what’s the difference between .on() and .live()? To me it seems they should both do the same thing so I don’t really get why .on() was added and what the potential differences between them are?

  16. Andi Farr

    Julien: unless I’m reading it wrong, your test seems to show that appending to an off-DOM node is considerably slower than simply adding to the DOM directly in all browsers tested.

    You can make it slightly faster by adding the src attribute when you create the img element ($(’<img>’, { ‘src’ : item });) rather than using a seperate attr() call, but it’s still slower than just appending straight to the DOM – pretty surprising, not what I would have expected.

    Cheers!

  17. JulienW

    @Andi: I’m quite surprised too. Especially on Chrome.

    It means that adding once as innerHTML is always faster than creating individual nodes.

    But I’m really not satisfied by this as it makes the code exposed to bad characters in strings (read: code injection).

    I’d like to see what happens with DOM document fragments…

    Anyway, I dislike to micro-optimize because the measured performance can change in the next browser version… I do prefer to code secure and reasonably fast, than the contrary. If jQuery is your bottleneck, you’re doing something wrong (like putting your model in the DOM).

  18. Matthew

    Would it be possible for jQuery to detect when an ID is the first bit of a selector string and silently-internally do a getElementById call followed by a .find with the remaining string? I can’t think of any problems that would rise from this.

  19. dave mellett

    Excellent article with some good tips. Be careful with caching when using ajax. New nodes added to the page will not exist in the cached selector, you’ll have to re-cache. Or even better, use .live() – or .on() in v1.7 I believe.

  20. Scott Kosman

    Thanks for the clarifications, Mathias. :hidden was a bad example for me to use as the “title” for the pseudo-selectors section because as you correctly state, it doesn’t use qSA. Good call-out on that.

    I was totally unaware of Opera’s caching, too – I actually can’t run Opera on my work computer because it can be used to torrent so I can’t do a lot of testing with it!

  21. Alan Moore

    This article was a source of one excellent piece of information for me: that .live() had been replaced by on().

    I’m surprised that .live() hasn’t had more exposure with the rise of media queries and responsive design: if your page has an element hidden at its initial size (or added to the DOM when the size changes), then jQuery can’t attach, say, a ‘click’ to that element, since it’s not in the tree. Change your page width, add the element back in and… no jQuery action on click.

    Rather than $(‘a’).click(function() { … }); we use $(‘a’).live(‘click’, function(event) { … }); instead. It’s very powerful, but I was not aware that live() has been deprecated. Now I know better, and I will use
    $(‘a’).on(‘click’, function(event) { … }); instead!

    As for the performance issues, I must just not be doing enough with my scripts to tax the browsers. I develop in Safari, then Chrome, then Firefox. I test IE inside virtual machines with only 512Mb apiece and none of them have any performance problems. If I start to spot any, I’ll be back here to see what I can do…

  22. Amber Weinberg

    Great article. I’m guilty of knowing jQuery and not Javascript myself (although I can’t stand either). I didn’t realize the find function was faster than a regular selector. :)

  23. Bart Lewis

    In regards to $(’.class .other’) versus $(’.class’).find(’.other’), the docs have this to say:

    Internally, selector context is implemented with the .find() method, so $(‘span’, this) is equivalent to $(this).find(‘span’).

  24. pete weissbrod

    I ran some of the js perf tests using internet explorer 9.0.8 and in quite a few instances I was surprised to see it outperform chrome. The new JS engine is extremely strict as I discovered when trying to use processing.js

  25. Twest

    Nice article man, we may be reworking some of our app and the future way we do things because of this read and the new 1.7 on() support

  26. daGrevis

    One of the best articles I have read about jQuery so far. Pure awesomeness.

    I really would love to see next part of it. More tips – better internet! :D

  27. Aaron Peterson

    A great way to keep track of your cached jquery selectors (I think I picked this up from Alex Sexton) is to prefix the var names with a $. This makes it much easier to remember:

    var $elem = $(”#foo”).find(”.bar”);

  28. Joe Larson

    Great article and discussion. For my 2cents when I cache jquery objects I usually suffix with $ so I can remember that this is a jquery object (so “block$” instead of “block”). This is especially useful because I frequently grab the DOM node directly to work with it, so this avoids confusion about which thing I’m actually working on. I realize some people may dislike this for all the usual reason any Hungarian-ish notation is disliked, but in this case I find it very helpful.

  29. monkey

    Speaking of speed, using jQuery.each to iterate over an array is bloody slow.

    Did one of these to test:

    http://jsperf.com/array-iteration-jquery-each-vs-for/2
    (also a lot of stuff to confirm my thoughts on where var’d stuff went)

    That also brought me to trying iterating over a jQuery collection in array style:

    http://jsperf.com/looping-through-jquery-objects/2

    I wonder how big an impact any of this speed stuff is going to make, though.

  30. Adam

    “The IE JavaScript engine moves at the speed of an advancing glacier compared to more modern browsers, so optimizing our code for performance takes on an even higher level of urgency.”

    less than 9, yes I agree. But the engine in IE9, from what I’ve observed is better than any other browser, even those versions that have come along since IE9 was released.

  31. Darren

    I guess I must be missed something? I always use CSS selectors like this:
    $(‘body > section div#content p’)…..
    which must let jQuery know not to bother looking anywhere else other than the top lever section element, etc? Surely the same – if not better – than the ‘find’ options being discussed???

    But great work!

  32. Scott Kosman

    Good question, Darren. Logically you’d think that’s exactly how it works, but it’s actually the opposite. Sizzle (the CSS selector engine built into jQuery) parses selectors from right to left so in your case, it’ll search out all paragraphs, then determine which ones are inside div#content, and so forth.

    I knocked together a quick jsperf using your example and comparing it to a couple of different options using .find(), and .find() is significantly faster.

    http://jsperf.com/yet-another-jquery-selector-test

  33. Katie Blackman

    This is a great guide for those who don’t get Javascript but understand enough jQuery to make things happen. It’s nice to finally get your jQuery commands working after reading and rereading the docs. But, as we do with our HTML and CSS, there is some clean-up always required. Performance is huge. Eight seconds is all it takes before someone’s clicking off your page onto something more interesting. This article is so helpful and will be tucked away in my bookmarks for when I need it next. Thanks!

  34. Jose Galdamez

    I had no clue about the on() method in v1.7. After seeing it in use here it definitely makes more sense to attach event handlers to parent elements as you did in your table example.

    In your section on DOM manipulation, are you also implying that it’s faster to prepend/append strings as opposed to DOM nodes? I’ve heard the former is faster, but never actually done any speed tests myself.

    I noticed one small typo. document.getElementsByTagname should be document.getElementsByTagName (capitalize the “n” in “Name”).

    Excellent post!

  35. Scott Kosman

    Hey Jose, sorry for the late reply.

    There are a ton of different methods for prepending/appending, and unfortunately the best answer to your question about performance is “it depends.” The point I was really trying to make was that, regardless of what kind of element you’re appending, it’s much more performant to append them all at once rather than appending one element in each iteration of the loop.

    I read a fantastic article about this recently that I’m trying to dig up again, it did a bunch of performance testing of various ways of appending using loops (both native for loops and jQuery $.each) and by far the best way was pushing content into an array in each iteration and then using that array to build the appended content after the loop was finished. If I can find it again I’ll post it here. Thanks for the question!

  36. Scott Kosman

    Context was mentioned in the very first comment and I addressed it (along with a number of other things) in a jsperf a few comments later (here). Or is there something more specific you wanted to discuss regarding it?

  37. Dodfr

    Very usefull tips !

    An other “pro” when you use the .on(‘click’,‘selector’,function({}) is that if you dynamically add a some element in ‘selector’ scope, then it will automatically be caught by the click event and you will not have to stick it to the event :-)

    Very usefull when you add new <li> items for example.

  38. Andi Farr

    Great article – I know I’ve been guilty of more than one of these before.

    Quick question on the first section – is $(”#id”).find(“p”) not equivalent to using the context attribute – $(“p”, “#id”)? They do the same, but I prefer the syntax of the second – no idea which is more performant, though!

  39. Jeffrey Way

    Nice article.

    …Just as long as people remember that, as long as you’re at least somewhat considerate of the selectors you pass to jQuery, you really don’t need to worry about selector performance too much. jQuery does an excellent job of optimizing as much as possible. But it’s good to understand the basic concepts, like right-left parsing.

    Sure – $(‘div’).find(‘p’) may potentially be fractionally faster than $(‘div p’), but I won’t lose sleep if I use the latter. Readability is more important in most cases….or unless there is noticeable slowdown in your page’s performance. :)

  40. Spyros Rallis

    The best way to speed up jQuery is to not use it at all.

    Don’t use jQuery because you are afraid (or maybe bored?) to write some simple JavaScript. Don’t use a 30KB framework in place of 10 lines of JavaScript. <em>Think</em> before coding and don’t cut corners just to cut corners — a little bit more work may be a better solution.

    Now, don’t take me wrong — jQuery is a great framework and I love it. But I believe it is being overused.

    You wouldn’t need to optimize your jQuery code if you used the framework only where necessary, right?

    ;-)

Impress us

Be friendly / use Textile