Andreas Grabner About the Author

Andreas Grabner has been helping companies improve their application performance for 15+ years. He is a regular contributor within Web Performance and DevOps communities and a prolific speaker at user groups and conferences around the world. Reach him at @grabnerandi

101 on jQuery Selector Performance

Last week I got to analyze a web page that spent 4.8 seconds in the onLoad event handler of a custom script file. It turned out that 2.8 seconds were consumed by applying a dynamic menu library (will blog about this one separately). The remaining 2 seconds were spent in jQuery selectors. The analysis showed that most of the selectors didn’t return any object and those that returned objects can be improved by using different selectors.

About jQuery Selectors

There are some great blog articles about jQuery Selectors and their Performance Impact. As you can see you can select elements by ID, TagName or ClassName. Depending on the select jQuery can use the native browser methods to query elements by id or tag or needs to manually iterate through the DOM in case of class names (as there is not getElementsByClassName in IE).

Analyzing the 2 seconds in my page time

The onLoad handler uses jQuery to set hide, show, change style sheets, … for certain elements on the page. Here is a code snippet:

Sample jQuery Script exected in onLoad

Sample jQuery Script exected in onLoad

The onLoad event handler was full of these calls. Using the free dynaTrace AJAX Edition you can see the individual $ calls that resolves the selector and the following method call in case the selector resolved at least one object. The following PurePath of the onLoad event handler not only shows me how much time was spent in those selector calls – but also how many of these actually didn’t find a single object (the lack of the following method call indicates that no object was found):

Unnecessary jQuery Selector Calls causing massive overhead

Unnecessary jQuery Selector Calls causing massive overhead

All calls marked in red are calls that didn’t return an element because there was no DOM element based on the selection criteria. The JS column shows the execution time of each individual method call – ranging from 1ms to > 100ms per call. The size column tells us how many JavaScript/DOM calls were made by an individual call. Here we can also see why certain $ calls took so much longer as they made many calls to fulfil the request. The Invocation column tells me how often a method was invoked by its parent. We can see here that some objects were actually resolved multiple times, e.g.: “.pop-cart”. It would have been better to only resolve it once and cache the object.

The first lesson learned from this is that most of these calls were not necessary and just caused massive overhead. If you happen to know which elements are on a page you should only resolve those objects and don’t try to resolve others. I know – having global java script files that handle different pages with different content cause a situation like this – but – do you really want to live with this overhead?

Analyzing difference in jQuery Selectors

The first problem on the analyzed page was that too many unnecessary $ calls were made. The other question that came up was why certain $ methods respond very fast (some milliseconds) where others take rather long (up to 100ms). The theoretical answer can be found in the jQuery Best Practice Blog. Coming back to my page shows me the following:

Selector by ID is the fastest using getElementById

Following image shows a selector using an ID. It uses getElementById and therefore returns very fast.

jQuery Selector by ID

jQuery Selector by ID

Selector by TagName using getElementsByTag

The following example selects elements by TagName and ClassName. jQuery first uses the getElementsByTagName which is a native implementation to retrieve all elements for the specified tag. jQuery then iterates through these items to filter for the ClassName.

jQuery Selector by Tag and ClassName

jQuery Selector by Tag and ClassName

Selector by ClassName needs to iterate through ALL DOM elements

If you use a selector by ClassName only – jQuery needs to iterate through EVERY element in the DOM as there is no native implementation for getElementsByClassName in Internet Explorer (its a different story for FireFox). Following image shows the overhead of a selector on a page with 3460 DOM Elements:

jQuery Selector by Class Name

jQuery Selector by Class Name

Conclusion

Depending on the size of your web site (number of DOM elements) you should consider the overhead of individual selector methods. Instead of selecting by classname you may want to think about using TagName and ClassName or by using unique IDs in case you only have a handful of objects on your page. Also – make sure you cache already resolved objects to avoid the overhead for subsequent resolving calls. And – last but not least – avoid calls that are not necessary. As seen in the page that I analyzed – more than 1.5 seconds of the 2 seconds can be saved by not making those calls.

Comments

  1. jquery-1.1.4? I’m curious to see the same tests in a more recent version of jquery

  2. good point. I try to recreate this site using a later version of jQuery. the general problem however still exists in a newer version – which is that jQuery needs to iterate through all DOM elements when using class names in the selector string. Plus – querying for a non-existing element just causes unnnecessary overhead when you can avoid it

  3. These options (by ID / by tag / by class) are exclusive. The best approach would be to combine them so that the class name selector does not have to do a full page scan.

  4. By which I mean, of course, that the options areN’T exclusive.

    By mixing them you can get close to the performance of an ID lookup with all the cascading goodness of classes.

  5. Thats correct – if you combine it with a tagname then jQuery first uses getElementByTagName and performs the classname match on these results instead of iterating through all DOM elements
    The key here is that we have to understand that looking up elements can be very expensive and that there are different ways to get access to these elements. This can be done by using different queries but may also require you to change the way you “label” your elements in order to speed up the lookup.

  6. Ben Barber says:

    I would be interested to see performance differences with a newer version of jQuery since it falls back to native querySelectorAll for browsers that support it http://ejohn.org/blog/queryselectorall-in-firefox-31/.

    Since querySelectorAll probably uses the same matcher engine as for CSS, i would imagine that the same rules would apply as for writing efficient CSS selectors. Including “Don’t Qualify Class-Categorized Rules with Tag Names”. https://developer.mozilla.org/en/Writing_Efficient_CSS#Don%27t_qualify_class-categorized_rules_with_tag_names due to the right-to-left rule processing.

  7. @Ben: the tests that I have done were on IE7. The new native query methods are not supported by older browser versions (which unfortunately are still making up the biggest market share). Here is an interesting link to check out querySelectorAll on old IE versions: something that doesn’t work
    I will take some time and test newer versions of jQuery on different versions of IE (6, 7 and 8 ) and I am going to publish those results. We should definitely see an improvment in IE8 because IE8 implements several query methods natively

  8. as

  9. thnaks

  10. jQuery 1.1.4 had a *completely* different selector engine than whats in 1.3+. Performance advice was basically flipped on its head when Sizzle landed.

    For some guidance on what helps now, check slides 21-33 of
    http://paulirish.com/2009/perf/

  11. @Paul: great slides – thanks. The general problem that I highlight here is the lack of IE to query elements by class name. Even though jQuery is a great framework with constant performance improvements the lack of IE functionality just kills performance if you execute certain selectors. But thanks to guys like you that highlight best practices people will learn how to write better performing web sites

  12. At 2500 a sizzle request based on a classname, without context, (e.g $(‘.someClass’)) takes around 200 ms in IE7 for our app. Results should still be similar for IE8.

    However optimised sizzle can be, for such a request it has to iterate through the dom and check classnames.

    This is simply a too expensive operation, especially in the case of real webapps. So it is now in our guidelines that such a sizzle request is forbidden.

    I think it is always possible to replace such a request, by proper node caching,or by using getElementById instead (which at least 100 times faster) or by going intensively on Event Delegation. Yes the best selector optimisation technique is : “avoid class-based selectors”. It is not a simple question of syntax, but more of software design.

    I “love the jQuery”, but IMO the basic pattern of jquery -select-traverse-modify- is really showing its limits under the dual pressure of bigger webapps and use by less knowledgable coders.

  13. *2500 nodes

  14. this fo Screenshot tools??

  15. @peng: it is the dynaTrace AJAX Edition. Download it – its free: http://ajax.dynatrace.com

  16. it would be great to see the same test on more recent version of jquery, thanks a lot for your article

  17. great, thanks!

  18. hi,
    I used dynaTrace yesterday, and found 400ms time taken for one selector like $(“#myDiv :text”) in jquery, that was shocked for me.
    So I go for manually findout the time taken by function as below

    var dt_start = +new Date();
    $(“#myDiv :text”).val(“”);
    var dt_end = +new Date();
    alert(dt_end – dt_start)

    As per above code this show me 30ms time take by the jQuery statement.

    Please tell me why this difference come.

  19. Hi Sandeep

    jQuery – at least in newer versions – caches lookup results of previous queries. so – make sure you measures all of your lookups. It could be that this lookup is not the first.

  20. Christoph Wagner says:

    It should be mentioned that by now every modern browser (aka IE9 upwards plus the others) implements a native getElementsByClassName() now: http://www.quirksmode.org/dom/w3c_core.html#t11

    • You are right. The blog post is from 2009 – so – fortunately things have changed to the better in the past years. Still – there are very complex and inefficient selectors out there. Watching out for the performance of these is still a good practice even though you might not find these really bad examples that often any more

Trackbacks

  1. [...] This post was mentioned on Twitter by dynaTrace software, Larry King. Larry King said: 101 on jQuery Selector Performance Performance, Scalability and … http://bit.ly/1haljx #jQuery #Web #Dev [...]

  2. Social comments and analytics for this post…

    This post was mentioned on Twitter by LKWave: 101 on jQuery Selector Performance Performance, Scalability and … http://bit.ly/1haljx
    #jQuery #Web #Dev…

  3. [...] 101 on jQuery Selector Performance Performance, Scalability and Architecture – Java and .NET A… – [...]

  4. [...] 101 on jQuery Selector Performance Performance, Scalability and Architecture – Java and .NET Appli… [...]

  5. 101 on jQuery Selector Performance…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  6. [...] my previous post I talked about the impact of jQuery Selectors on a page that I analyzed. The page took 4.8 seconds [...]

  7. [...] 101 on jQuery Selector Performance [...]

  8. [...] specific to a certain JavaScript Library like Prototype. I recently blogged about the internals of CSS Selectors in jQuery. The same holds true for every JavaScript library that offers CSS Selectors. Certain lookups can be [...]

  9. [...] user. This can very often be seen when expensive CSS Selector Lookups (check out the blogs about jQuery and Prototype CSS Selector Performance) are used or when using dynamic elements like JavaScript [...]

  10. [...] end-user performance. Check out the following blog entries about the performance impact of jQuery and Prototype. It shows how important it is for developers to understand the internals of frameworks [...]

  11. [...] that cause huge overhead on pages with many DOM elements. I wrote two blogs about the performance impact of CSS Selectors in jQuery and [...]

  12. [...] blogged about that many times – are expensive CSS Selectors. Check my recent blog about 101 on jQuery Selector Performance to get background information on this problem. The following screenshot shows me the top CSS [...]

  13. [...] I already blogged about different best practices on things like correct usage of CSS Selectors with jQuery and Prototype and showed the performance impact of bad JavaScript code and incorrect jQuery usage by [...]

  14. [...] Add-Ons like Skype have to iterate through the DOM whenever a page is loaded or modified. The time it takes to iterate the DOM depends on the size of the DOM. That explains why those Add-Ons don’t have the same web site performance impact on every page as it depends on the number of DOM elements. It is a general JavaScript/AJAX Best Practice to limit the number of DOM elements as there are many other implications with large DOM trees, e.g.: Impact on CSS Selector Performance. [...]

  15. [...] on elements identified via their class name or complex lookups with parent->child relations. I wrote an article that explains the performance implications of these lookups and listed other JavaScript/AJAX [...]

  16. [...] 5-10ms (times vary depending on number of DOM elements on your page). More on this in our blogs 101 on jQuery Selector Performance or 101 on Prototype CSS [...]

  17. [...] user. This can very often be seen when expensive CSS Selector Lookups (check out the blogs about jQuery and Prototype CSS Selector Performance) are used or when using dynamic elements like JavaScript [...]

Comments

*


eight − = 1