Now you see me… show/hide performance

read 55 comments

I just got back from the jQuery conference in San Francisco. Wow, what an event. In addition to some incredible talks, I had the opportunity to speak with Rey Bango, Johnathon Sharp, and, of course, John Resig. Any conference where you get to talk to some of the most influential people in jQuery is a win in my book. The "High Performance JQuery" presentation especially caught my attention when the speaker, Robert Duffy, said that .hide() and .show() were slower than changing the css directly. Not having occasion to ask him why, I benchmarked the various ways to hide DOM elements and looked into the jQuery source to find out what is going on.

The HTML I tested against was a page of 100 div tags with a class and some content, I cached the selector $('div') to use with each method to exclude the time needed to find all the div elements on the page from the test. I used jQuery 1.4.2 for the testing, but keep in mind that the algorithms behind the method calls can change dramatically from version to version. What is true for 1.4.2 is not necessarily true for other versions of the library.

The methods I tested were .toggle(), .show() & .hide(), .css({'display':'none'}) & .css({'display':'block'}), and .addClass('hide') & .removeClass('hide'). I also tested modifying an attribute of a <style> element.

.show() & .hide()

These were, in fact, comparatively slow methods of hiding DOM elements across all browsers. One of the main reasons is that .hide() has to save the notion of what the display attribute was before, so that .show() can restore it. It does this using the .data() jQuery method, storing that information on the DOM element. In order to do so, .hide() loops through every element twice: once to save the current display value, and then once to update the display style to none. According to a comment in the source, this prevents the browser from reflowing with every loop. The .hide() method also checks to see if you pass in a parameter to animate the hiding with an effect. Even passing in a 0 dramatically slows down the performance. Performance was slowest on the first call to .hide(); subsequent calls were faster.

Browser      hide/show
FF3.6 -         29ms / 10ms 
Safari 4.05 -   6ms / 1ms
Opera 10.10 -   9ms / 1ms
Chrome 5.0.3 -  5ms / 1ms
IE 6.0  -       31ms / 16ms 
IE 7.0  -       15ms / 16ms 

.toggle()

This was, by far, the slowest method of hiding all of the div elements. It iterates through every element returned by the selector, checks to see if the element is currently visible, and then calls .hide() on visible elements one at a time and .show() on hidden ones one at a time. It also has to check to see if you are passing in a boolean to force everything to .show() or .hide() and check to see if you are passing in functions to toggle instead of toggling visibility. There seems to be some opportunity for optimization of this function, as one could select all of the hidden elements of the selector and call .show() on them all at once and then select the remaining elements in the selector and call .hide() on them at the same time. If you are so inclined, I encourage you to check out the source and see if you can eke out any performance gains.

Browser      hide/show
FF3.6 -         80ms / 59ms 
Safari 4.05 -   24ms / 30ms 
Opera 10.10 -   67ms / 201ms
Chrome 5.0.3 -  55ms / 20ms 
IE 6.0  -       296ms / 78ms 
IE 7.0  -       328ms / 47ms 

.addClass() & .removeClass()

These are pretty snappy methods of hiding/showing elements of the DOM, twice as fast as .show() & .hide() in Firefox and three times as fast in Safari. The differences in IE 6, IE7, Chrome, and Opera are negligible. It's also worth noting that with 100 DOM nodes, we're talking a total difference of 18ms in Firefox and 4ms in Safari. The speed difference will only be relevant for very large selections. Adding and removing a class requires a bit more management on your part, since you have to create the class that has a display of none and then have to keep track of CSS priority to make sure your elements get hidden. The way jQuery adds and removes a class is through string manipulation, so I imagine that as the number of classes on an element grows, this method will get slower, but that is untested speculation on my part.

Browser      hide/show
FF3.6   -       11ms / 11ms 
Safari 4.05 -   2ms / 2ms
Opera 10.10 -   6ms / 3ms
Chrome 5.0.3 -  3ms / 1ms
IE 6.0  -       47ms / 32ms
IE 7.0  -       15ms / 16ms

.css({'display':'none'}) & .css({'display':'block'});

These methods were very snappy. They showed an improvement over .addClass() and .removeClass() in Opera and IE 6/7 and about the same in other browsers. They work great if you know the current display style of all the elements you are changing, or at least have not changed the display style inline. If you have changed the display style inline, then you will need to make sure you set the correct value when you make the element visible again. If you are just using the elements' default display value or set the display value in the css, then you can just remove the style like so, .css({'display':''}), and it will revert to whatever value it has in the css or by default. As a library, jQuery can't assume that the display element wasn't set inline, so it has to manually keep track of it. That is the main slowness you can avoid since you know you won't be setting the display inline.

Browser      hide/show
FF3.6   -       14ms / 12ms
Safari 4.05 -   2ms / 1ms
Opera 10.10 -   2ms / 2ms
Chrome 5.0.3 -  2ms / 1ms
IE 6.0  -       16ms / 16ms
IE 7.0  -       0ms / 0ms  // The usual caveat about inaccuracy of IE clocks applies.

Disabling stylesheets

For fun, I thought, "What if instead of manipulating every DOM node and changing things, we just futz with the stylesheet?" Could there be speed improvements there? I mean, the methods benchmarked above are plenty fast for everyday use, but what if I had 10,000 nodes on a page I wanted to show and hide? It would be slow just selecting them all. But, if I could manipulate the stylesheet, I could avoid the entire overhead. Let me just tell you that way is fraught with peril.

There are, of course, cross browser issues when manipulating stylesheets, since jQuery doesn't abstract them away for you. First, I tried to see if I could append a style tag with the css class as a string using jQuery, but got inconsistent results across browsers. Then I tried creating the stylesheet node and class using JavaScript, but there were different APIs and it ended up being too slow to justify. So finally, forgoing an attempt to do this in a programmatic way, I ended up just writing a style tag with a class in the head of the document. It's far too slow to create the stylesheet programmatically, but if it's already there then it is trivial to give it an ID and use its disabled attribute.

HTML:
  1. <style id="special_hide">.special_hide { display: none; }</style>
  2. <!--  ...  -->
  3. <div class="special_hide">Special hide DIV</div>

Then in javascript…

JavaScript:
  1. $('#special_hide').attr('disabled, 'true');

and BAM, you just displayed all of your elements with a class of “special_hide”. To hide them all again, just do…

JavaScript:
  1. $('#special_hide').attr('disabled', 'false');

and they are now all hidden. The total javascript processing time was 0-1ms across all browsers. The only javascript you are doing is changing an attribute. Of course, there is still the time the browser takes to reflow and repaint the page, but you've virtually eliminated all the javascript processing time. If you call any of the other methods, .toggle(), .hide(), or .css(), this method will stop working on those elements because they set the css style inline, which has higher precedence than other css. To make this method work again, simply do a .css(‘display’, ‘’) to remove the inline style. This method also requires the most work on your part, because you have to define the class and give the class to all of the elements on the page you want to show/hide at the same time, but if you are dealing with extremely large sets, this might just be worth it.

To recap, here is a list of methods to change the display of elements in order from fastest to slowest:

  1. Enabling/Disabling a stylesheet
  2. .css('display', ''), .css('display', 'none')
  3. .addClass(), .removeClass()
  4. .show(), .hide()
  5. .toggle()

Also note that for the majority of use cases, all of these methods are plenty fast to use. When you start having to manipulate large jQuery collections, .show() and .hide() might become too slow in IE, and you might need to bump up to .addClass() or .removeClass(). Enabling/disabling of stylesheets would only be necessary in the most extreme cases, but if things are hiding to slowly for you, you might want to give it a try.

comment feed

55 comments

  1. whoa, i have never seen a <style> block with an ID before, that is a way cool trick! thanks

  2. What about toggleClass() ? Should perform little slower than add/remove but worth mentioning in the article imo.

  3. Great post, I always wondered what was faster: .css('display', 'none') or .hide()

  4. Awesome post, actually most of the developers will not see those scenarios - they check whether the action is working or not.

    In this way if we see, we can increase the performance of the page [I mean about the rendering part of the page].

    Thanks for sharing this and it was an awesome research which you have done! cheers!

    Thanks again,
    Vivek [Founder of DeveloperSnippets - http://www.developersnippets.com

  5. Thanks for this post! Although now you make me want to switch from .hide() to .css({'display':'none'}) everywhere, even though I know I'll only gain a few ms. Probably not worth the effort.

    Still, a great thing to be aware of if I end with pages requiring a lot of manipulation. I think the point of this is not to go around optimizing all your JS like crazy to shave off a few ms no one will notice, but to be aware of things like this for when you run into real performance problems.

  6. Very nice article indeed. Keep going.

    • Josh Powell

      Thanks everyone for the compliments, love to hear them :)

      @Michael Mior - You are right, don't over optimize your sites. .hide() & .show() are plenty fast for everyday use, and so very simple to use.

      @Andreas - Good point about toggleClass. I'll try and benchmark that later.

      • Shanimal

        I've built a datagrid component and i'm pretty happy with it, but I need to optimize the heck out of it, esp after it reaches 1000 or more cells. If the user clicks a row (w/o pressing command) I have to turn off all rows that are already selected and then select the ones that should be selected. So I have to iterate through all rows to do this.

        Is it faster to call toggleClass on all rows:

        row.toggleClass("selected",false)

        or would it be better to check all rows first then toggleClass only if its not already selected?

        if(row.is("selected")) row.toggleClass("selected",false);

      • Shanimal

        One more thing I can't just do $(".selected").toggle("selected",false) because I have other tables. But also what is the performance difference in that case?

      • Shanimal

        I ran my own little "Benchmark"

        Seems like a fair test... tell me what you think.

        I like option 3. I'm going with it.
        grid.find(".selected").toggleClass("selected",true);

        Also, point me to a good benchmark plugin if jquery has one. :)

  7. Adrien

    Great article indeed.

    I wouldn't have thought of disabling a . That's interesting.

    The main problem while doing many changes in a page is that the browser reflow after each change. There a google conference about that and how to make changes out of the page on an object and then insert it in the page.
    Maybe jQuery developpers should take a look at that (maybe they already did).
    But it's not the topic of your article, you're just a web developper trying to do the best he can using jQuery.

    Link to the conference : http://www.youtube.com/watch?v=mHtdZgou0qU

  8. Very interesting test, but no surprise that Safari & Google Chrome are dominating the tests. Thx for sharing your infos!

  9. " If you call any of the other methods, .toggle(), .hide(), or .css(), this method will stop working on those elements because they set the css style inline, which has higher precedence than other css."

    Regarding inline styles overriding the style block enabling/disabling, there is actually a way to prevent that provided your target browsers all support CSS attribute selectors. I've put a short demo together at http://shaneriley.com/jquery/learning_jquery_05042010/ if anyone's looking to use this method.

  10. Rob

    Thanks for this analysis. I've always tended to use .show() and .hide() for these sorts of page manipulations but speed is definitely an issue when it is applied on a fairly large scale so I'm going to look into changing my methods. As @webstandards-blog said, no suprise Safari and Opera are leading the way.

  11. GREAT Article and I learned something (disabling of a <style> tag using an id, genius).

    However, I did want to point a small caveat out. Many ppl consider assigning CSS to an element through the use of .css an anti-pattern, since you're injecting it into the mark-up. While the speed is slightly better for .css(...) over .addClass(...) and .removeClass(...), they are "more" correct, imo.

  12. This is valuable if only to help us understand exactly what’s happening under the hood.

    Obviously you are toggling 100 elements to make the time taken more measurable. If you truly want to toggle the visibility of 100 elements at once, I would expect manipulating a shared parent is going to be faster?

    For example toggling a class to apply a descendant selector:

    <style>
      .hidekids div{display:none};
    </style>
    <script>
      $('#parent').toggleClass('hidekids');
    </script>
    <div id="parent" class="hidekids">
      <div>hidden</div>
      <div>hidden</div>
      <p>not hidden</p>
    </div>
  13. Great analysis, I really enjoyed reading it.

    Maybe I'm missing something but in the disabling stylesheet section, shouldn't the class of the div element be 'special_hide' rather than 'special hide'?

    • Josh Powell

      Not sure what you mean, the class is "special_hide" The text inside the div is "special hide", maybe that is causing confusion?

      • Michael

        For some reason, on my browser (and presumably Steven's), the underscore in the class declaration of the div is not visible and that character looks like a space. But when you copy and paste, you can see that there is an underscore.

  14. " If you call any of the other methods, .toggle(), .hide(), or .css(), this method will stop working on those elements because they set the css style inline, which has higher precedence than other css."

    Regarding inline styles overriding the style block enabling/disabling, there is actually a way to prevent that provided your target browsers all support CSS attribute selectors. I've put a short demo together at http://shaneriley.com/jquery/learning_jquery_05042010/ if anyone's looking to use this method.

  15. The benchmarking you have done has proven invaluable for me. Thanks! I use it for this:

    http://www.twoevils.org/html/mygames/

    And I found that going from .hide()/.show() to display none/'' gave me a roughly 40% boost in searching performance. I'm going to try the final method next. Even that 40% is not enough speed when searching over 2000 records, I've found.

    BTW, this code fragment in your article:
    $('#special_hide').attr('disabled, 'true');
    ... is missing an apostrophe after disabled. Just thought you might like to know. :)

  16. Mario

    This really helped a lot!!!! I saved more than 2 seconds filtering/hiding 1,500 tr's

    Thank you very much.

    Best Regards

  17. first of all thanks very much for the great analysis and benchmarks, I wouldn't have thought of the disabling stylesheets method in 10 years ;)

    got it running on my site and it's exactly what I needed, but there's one problem: it doesn't work in safari or chrome, the styles don't get disabled on page load so the content never shows up. all the other browsers are fine.

    I tried everything I could think of, but still, the line that disables the styles is ignored in the webkit browsers.

    Has anybody come across similar problems?

    here is my code:

    
     	.cont { display: none; }
    	$(function() {
    			 	$('#cont').attr('disabled', true);
    			});
    

    do you see anything wrong with it?

    • the .cont { display: none; } is in an style-tag, obviously ;)

    • Josh Powell

      Thanks, I'm glad you enjoyed the article. You need to give the style tag an id and disable the style tag.

      <style id="cont">
      .cont {}
      </style>

      ...

      $('#cont').attr('disabled', true);

      • thanks for the quick answer! I'm sorry, I messed up my sample code, on the site i've got several style tags with ids, like you described.

        But I guess, if you've never experienced troubles with webkit browsers, my error probably originates somewhere else. I'll investigate some more!

      • As a workaround I've discovered you can just overwrite the stylesheet on the fly for the same effect. For example:

        To hide a bunch of stuff:
        $('#special_hide').text(".special_hide{ display: none; }")

        To show it:
        $('#special_hide').text("")

        Has a lot of the advantages of enable/ disable but works in webkit.

  18. Okay, but I don't understand the workaround. Is it possible to use it on my site?

    • Josh Powell

      Yes, you just have to use plain old javascript instead of jquery for now, until webkit fixes the bug or jquery takes the webkit bugs into account. See my comment above for the code that does it.

  19. When looping through a table for example, how does using $(this).css('display', 'none'); compare to this.style.display = 'none';?

    In my testing with thousands of table rows (looped through with $("table tbody tr").each( function() { code code code });, for example, I found that the latter was a smidge faster, placing it closer to your fastest result, but without all the complications of having to assign classes to rows and disabling the class on page load.

    You can see the results here (complete with keystroke delay timer) here:
    http://www.twoevils.org/html/mygames/

    Even when I loaded it up with several thousand rows of fake data (instead of the 250 or so currently there) it filtered the contents of the page more or less instantaneously.

  20. One of the thing I noticed with show() and hide() function in jQuery is that, it only works when its parents must be visible.

    If one of the parent is hidden then the show and hide wouldn't work at all, even when you show the hidden parent.

  21. Wow, I never imagine what kind of work I do through Jquery. Now one words I am going to use Jquery functionality to my web site.
    please tell me which book is better for the learner .
    thanks....

  22. Hey dude,

    Thank for posting show/hide performance article. I found this very helpfu.

    Mark Kumar

  23. Kumar

    Using stylesheet is still an issue (bug/feature) in IE as IE limits the number of stylesheets used in a page to 31. http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx

  24. Hide / Show are two of my most frequently-used jQuery actions. So this really came in handy. I never realized there was such a huge variance in response times across the different methods. I guess for large collections, I'll stick with .addClass() or .removeClass(). AND I will be using the trick @Ollie pointed out above (thanks!).

  25. Nick

    I would say the variance is much larger than this article shows as I suspect that the author was only changing one or a few elements. I used the show/hide on a table with 4k elements as it took over 2 minutes to finish on ff 3.6. however once I changed to using the .css('display', ''), .css('display', 'none') it takes about 3 seconds.

    Nearly a 2 order of magnitude improvement at the large end of the scale (where it really counts). Thanks for the article though, it pointed me to the correct solution :-)

  26. Great site, and great information!

    I do not understand though why changing the CSS is faster than show/hide. Isn't that exactly what is happening behind the scene?

  27. philk

    Great tip!
    however,

    $('#special_hide').attr('disabled, 'true')

    does not seem to work, while

    $('#special_hide')[0].disabled = true; 

    does.
    Any ideas?

    • You're missing a closing quote after disabled, but assuming that was correct in your code, the first one accesses a jquery collection of one element and then disables it, while the second one accesses the DOM node directly and disables it. I don't know without digging into the code why one works and one doesn't. If you investigate it, please update me here.

      • philk

        Josh, I don't have the actual code at hand anymore. Because of performance issues on the mobile device (with IE6) I have reverted back to css('display') approach.
        But I will use your trick on the desktop ;)

  28. philk

    Just another note: The fastest method described here is horribly slow on IE6 on Windows CE (mobile). I will test css('display' , 'none') next.

  29. The results of this benchmarking are logical and should be obvious without any testing. There are several ways to make an element visible or invisible with css and show() and hide() methods are slow because they have to check all those ways and set appropriate properties in the style attribute. toggle() is even slower because it has to check all those css properties to determine if it should do hide() or show(). Simply setting an attribute with addClass(), removeClass() or css() is faster than those other, 'clever', methods. Both when setting class and style attribute, jQuery has to read the existing attribute, analyse it and then generate the new one. style attribute has more complex structure than the class attribute, so css() method is slightly slower than addClass and removeClass().

    If your element is visible by default and you hide it with jQuery, I think the fastest way to make it visible again is using removeAttr('style') method, which will remove the style attribute generated by jQuery and thus revert the element's style to the style specified in your stylesheet(s). This works correctly only if you are not setting any other properties with css() or animate() method.

  30. Julius Epman

    Nice benchmark indeed. However the id attribute of the style tag is non standard compliant. So using the title attribute instead might work better if a standard mode is required.

  31. Parmeisan

    Thank you so much! When you're working with filtering even 2 thousand records, show/hide could take up to 20 seconds to perform. It wasn't until I tried to optimize my comparisons that I discovered to my surprise that the string comparisons were like *nothing* to the show/hide. Switching to the CSS method (ironically, after having switched *from* that method while I was testing with only a few hundred records) gives a nearly instantaneous response. Much better!

  32. Matt Rohr

    We were dealing with this at work and I came up with a benchmarking page to test how much this actually impacts performance.
    http://mjrohr.dyndns.org/showhide.html

  33. Nice tips on jQuery. I really appreciate.

  34. Jegs

    Kudos! I'm a performance OC fanatic and i would love to read more articles like these...

    This site will be on my delicious.

  35. San

    Hi, I want to show/hide a div that is created by a plugin. I'm using an mp3 player and the playlist is visible by default, so I added a button that will serve the purpose of hidding and showing that playlist... I've tried with many examples and none seems to work. The only way of hide the div is by css: display:none; but then I can't make it appear again with the button. What could I do? Thanks!

  36. blindfish

    This was really useful and confirmed a sneaking suspicion...

    Re: "Also note that for the majority of use cases, all of these methods are plenty fast to use. "

    Presumably you were benchmarking on a reasonably fast computer? I'd be interested to know the performance gains these changes might result in on mobile devices and whether they're worth implementing even when showing/hiding a relatively small number of elements (say 10 to 20). Definitely something to look into :)

  37. Felix

    Thanks a bunch for this article. In my case, a performance milestone went from average 250 ms down to 5 ms. This by changing from show()/hide() to css("display" "block/none").

  38. An oldie but goodie. Thanks so much for posting this. I was using show()/ hide() on a large list of approximately 1000 elements for filtering and it was taking 2-3 seconds. Using the direct css approach it is now between 20-50ms.

  39. Anna

    How can I get the show("slow") effect if I just use $("#elementId").css("display", ""); ?

12 Pings

  1. [...] }Josh Powell Read Post I just got back from the jQuery conference in San Francisco. Wow, what an event. In addition to [...]

  2. [...] jQuery .hide(), .show() långsamma, eller långsammare än andra lite krångligare sätt iaf. http://www.learningjquery.com/2010/05/now-you-see-me-showhide-performance [...]

  3. [...] Josh Powell tests out high performance jQuery experiments are “.high” “.show” slower than changing [...]

  4. [...] Now you see me? show/hide performance Via / @karlpro [...]

  5. [...] most extreme cases, but if things are hiding to slowly for you, you might want to give it a try.via Now you see me… show/hide performance – Learning jQuery. Personally I like using the add/remove class the advantage of it already being slightly faster for [...]

  6. [...] Now you see me… show/hide performance Filed under: In the news — Shaun Kester on June 3, 2010 [...]

  7. [...] LearningjQuery has an interesting article about different speeds of show and hide html elements. [...]

  8. [...] let’s look at an interesting article by Josh Powell show/hide performance. According to his tests this is a list of methods to change the display of elements in order from [...]

  9. [...] but if it’s already there then it is trivial to give it an ID and use its disabled attribute. PLAIN TEXT [...]

  10. [...] performance consideration,  based on jQuery tutorial from learning jQuery, you can use .css(‘display’,'none’) to hide [...]

  11. [...] Using .css({‘display’:'none’}) turns out to be WAY faster than hide(), so no need for the loading animation… This article showed me the light: show/hide performance. [...]

Sorry, but comments for this entry are now closed.