1 Way To Avoid the Flash of Unstyled Content

read 56 comments

This tutorial describes a way to avoid a flash of unstyled content that sometimes occurs when applying styles with JavaScript on page load. The problem is most evident when there is some content that needs to be hidden initially and when the document is large or complex. We can see an example of the problem on this test page.

The page is a copy of a previous entry on this blog with over 140 comments—quite a lot of content. Look for the bright yellow background before the page finishes loading. That's the part that is supposed to be hidden from the start — a simple unordered list, <ul id="flash">.

In pages with a more reasonable length, the standard jQuery code would work just fine:

JavaScript:
  1. $(document).ready(function() {
  2.   $('#flash').hide();
  3. });

But in this page there is a whole lot of document that has to be ready before anything inside the $(document).ready() function can be executed. Too much, in fact. And putting the <script> tags just inside the closing </body> tag doesn't help either. No, using JavaScript to hide elements like this just won't work unless the scripts are placed in the HTML immediately following the elements they're hiding.

Why Use JavaScript for This, Anyway?

Before we examine how to fix the problem, we might need to answer the question, Why bother? I mean, isn't that what CSS is for? Certainly we could add a rule to our stylesheet, such as #flash {display: none;}, and our problem would be solved.

But the pure CSS solution introduces another problem: the hidden content is completely unavailable to those who have CSS enabled/available but JavaScript disabled/unavailable. These users simply won't be able to view the content. Now, some developers don't have a problem with that, but as a proponent of Progressive Enhancement™, I believe that a user should be able to see content at the very least when visiting a web page (okay, okay, full-scale RIAs could be an exception).

A Simple Solution

Rather than attempt a pure CSS solution or a pure JavaScript solution, why not try a hybrid of the two? Here is how it might look (Important: I'm embedding styles and scripts here for the purpose of demonstration, but linking to separate files instead is recommended):

HTML:
  1. <html>
  2.   <head>
  3.     <!-- etc. -->
  4.     <style type="text/css">
  5.       .js #flash {display: none;}
  6.     </style>
  7.     <script type="text/javascript" src="/scripts/jquery.js"></script>
  8.     <script type="text/javascript">
  9.       $('html').addClass('js');
  10.       $(document).ready(function() {
  11.         // Stuff to do as soon as the DOM is ready
  12.       });  
  13.     </script>
  14.   </head>
  15.   <body>
  16.     <!-- etc. -->
  17.   </body>
  18. </html>

The first thing to note here is that the CSS is hiding an element with id="flash" only if it is a descendent of an element with class="js". Now, which element can we add a class of "js" to immediately, from within the <head>, and have it be applied before the slightest bit of the body is visible? Why, the <html> element, of course!

Line 9 shows the "js" class being added. And since it's being added with JavaScript, only those users who have JavaScript enabled will have their <ul id="flash"> initially hidden. Take a look at the beautiful invisibility demonstrated here.

Significantly, the .addClass() method is not inside $(document).ready(). If we had put it inside, we'd be back to square one.

If you follow the YUI advice to put scripts at the bottom, we can still make this solution work for you. We just use ye plain olde JavaScript for our one-liner to add the "js" class, keep that one in the <head>, and dump the other scripts down below, like so:

HTML:
  1. <html>
  2.   <head>
  3.     <!-- etc. -->
  4.     <style type="text/css">
  5.       .js #flash {display: none;}
  6.     </style>
  7.     <script type="text/javascript">
  8.       document.documentElement.className = 'js';
  9.     </script>
  10.   </head>
  11.   <body>
  12.     <!-- etc. -->
  13.    
  14.     <script type="text/javascript" src="/scripts/jquery.js"></script>
  15.     <script type="text/javascript">
  16.       // Stuff to do as soon as the body finishes loading.
  17.       // No need for $(document).ready() here.
  18.     </script>
  19.   </body>
  20. </html>

Thanks to Brandon Aaron for the tip to use document.documentElement rather than document.getElementsByTagName('html')[0]

This one, demonstrated here, hides <ul id="flash"> as completely and awesomely as the previous one. Simple!

comment feed

56 comments

  1. Klaus Hartl

    Hi Karl, I use a slightly different approach here: what is used with JavaScript only - add it with JavaScript. Thus I add a style sheet via document.write after all other style sheet references. You can neglect the .js part in the selectors in that case.

    document.write of course may have negative impact on page rendering time but as long as it's only a single one I felt it's ok.

    (And for the purists: The HTML spec doesn't have a class attribute for the html element, so a theoretical super strict browser could ignore it. In practice all browsers seem to support it.)

    PS: Nice new design!

  2. manu

    Hi!
    Just a note: beware of relative links for your examples, because it is broken when users read your articles from feedburner or google reader.

    Anyway thanks for the tip!

  3. Klaus,
    Thanks a lot for the comments. I'm glad you like the design!

    The document.write trick is an interesting idea that, admittedly, I hadn't thought of yet. Sam Collet also mentioned it to me on Twitter today (great minds think alike, I suppose). Having come to JavaScript on the wave of the DOM scripting / Unobtrusive JavaScript revolution, I had in mind that document.write was somehow a poor practice. But, hey, if it works and there are no discernible side effects, why not?

    You're right, of course, about the class attribute not being valid for the html element. I meant to note that in the entry, but forgot. Glad you brought it up. I figured that since it works and the class isn't hardcoded into the HTML, most people would be willing to overlook that.

    Thanks again, Klaus. It's always great to see how you tackle these issues.

  4. manu, I appreciate the warning about relative links. I'll go and fix them up now — although I don't know if that will have any effect on feed readers this time around. Do they incorporate post updates? Either way, thanks for mentioning.

  5. This is a great tip. I was so impress beyond even what you mentioned I posted a follow on my blog.
    This applications for this go beyond just fixing the flashing problem.

  6. I apologize if I've missed something in the example, but shouldn't document.getElementsByClassName('html')[0].className = 'js';
    instead be document.getElementsByTagName('html')[0].className = 'js'; ?

    You want the html tag, right?

  7. JFSIII, No apology necessary. You're right. Ack! That's what I get for typing this up so late at night. Duly noted and fixed. Thanks!

  8. I've also usually used document.write to spew necessary css rules into the head, but this is an interesting approach and in some way slightly more elegant. I might try it next time i run up against this problem.

  9. manu

    @Karl: Do they incorporate post updates?
    >> Yep :-)

  10. will haven

    I've been using the code below ever since I came across content "flashing" or rendering of the page without my jQuery modifications applied yet. I link to the js file from the head of my document.

    if (document.getElementById) {
    document.write('
    <style type="text/css" media="screen">
    #element1, .element2 {display:none;}
    </style>
    ');
    }

  11. will haven,

    Yeah, just like Klaus mentioned in the first comment. :)

  12. Ty (tzmedia)

    Another CSS method, that works in some cases, say you are loading slides in a div, and know the size required.
    Specify the size of the Div and have it set to overflow:hidden.
    IT seems to work, and should have an image present, without the fading in slides, etc.
    A technique I've been happy with, until now anyways - thanks gang.

  13. jlee

    I've been using the noscript tag to achieve the same effect...

    ex:

    ...

    <style type="text/css">
    #flash { display:none}
    </style>

    <noscript>
    <style type="text/css">
    #flash {display: block}
    </style>
    </noscript>
    ...

    Also, doesn't IE7 (or some other browser) not allow for DOM manipulation until the DOM is fully loaded? (or does adding a class name not factor in as a DOM change?)

  14. Klaus Hartl

    By the way, you don't have to necessarily document.write a style tag. You can as well use a link.

  15. Klaus Hartl

    @jlee I like that solution, unfortunately the HTML spec doesn't allow style elements inside noscript. To me this is clearly an oversight. Thus it's allowed in HTML 5 under certain circumstances, but the Spec did quite confuse me here.

  16. Ricardo

    Why not


    $('head').append('<style type="text/css">#fox {display:none}</style>')

    outside ready()?

  17. Ricardo,
    I suppose that would be fine, too. It might get a little messy if you have multiple style rules you want to apply.

  18. Thanks for the tip Karl, this is just what I needed for a website I've been working on!! :D

    Keep up the good work!

  19. Maniquí

    I've been using a similar technique: avoiding flickering in jQuery.

  20. If you put your JS includes at the bottom of the document you can use the document.documentElement reference instead of looking for it using getElementsByTagName.

    Replace this:
    document.getElementsByTagName('html')[0].className = 'js';
    With this:
    document.documentElement.className = 'js';

  21. thanks for the tip, Brandon!

  22. I'm actually using .nojs rather than .js in the html tag. So I'm going against the tide - I hard code .nojs and then remove the class at the earliest possible point.

    There is no difference techincally but in terms of time consideration I could concentrate on the majority of users first then bake in for those that don't have Javascript later.

  23. Ca-Phun Ung, one difference is that the class attribute is not valid on the html element, so if you're concerned at all about your page validating, adding it with JavaScript would be better than removing it with JavaScript.

  24. @karl: Point taken - that never crossed my mind :)

  25. And what about progressive enhancement?

    It is explained in this ALA article

  26. Markus,

    I'm well aware of what progressive enhancement means. That's the whole point of this post. It allows you to initially hide certain elements for those with JavaScript enabled, and those without JavaScript still get to see the content.

  27. I'm sorry, I didn' mean to question whether or not you are aware of progressive enhancement, my real intention was to point the whole article as a solution to the topic of this post, not to teach you what it is progressive enhancement.

    Well, in the post, a guy named Scott Jehl, designer at Filament Group, talks about a technique to test the user's device capabilities and make the enhancement only if the user's device meets some requirements. I think his technique is an interesnting solution to avoid the flash of unstyled content, as the script only loads the "enhanced" stylesheet when the dom is ready.

  28. I'm sorry, too, Markus. I shouldn't have been so defensive. I've read Scott's article and enjoyed it.

  29. To avoid the purists who won't let you put a class on the <HTML> element, you could just put it on the <BODY> element. I use <BODY> class switching a lot; it's like instantly changing stylesheets without the headache of enabling/disabling each one.

    • Hi Daniel,
      Yes, setting (and switching) a body class is a time-honored tradition. Unfortunately, though, it sort of negates the benefit that you get with setting a class on the html element, which is that you can do it with JavaScript before anything in the body is registered/loaded and thus avoid the flash of unstyled content.

      • Daniel Wachsstock

        Putting the <SCRIPT> right after the opening <BODY> would avoid the flash; the element is created as part of the DOM as soon as the opening tag is parsed.

  30. That so Interesting KARL ,
    YOU are so prepomnderant in DOM .
    fantastic

  31. Wow, great tip here, I'm definitely going to start using this more!

  32. JP

    Thanks for the inspiration Karl! This article helped fix a FOUC problem we've had for a while now.

  33. Searched a lot to find a solution to hide content fast enough and here we go. Brilliant solution. Thank you very much.

  34. Fantastic, just what I was looking for. Many thanks!

  35. Mark Courtnell

    Great, implemented this and even put a animated loading gif, which disappears once my componets have loaded. BUT this works perfect in IE7, but in Firefox3, it hides the content shows my animated gif, then for a split second i see the hidden content before it bounces into action.
    Where it use to sit there for 3-4 seconds before bouncing into action.
    Karl if you want to see this in action i can email you a website link, its in development didn't want to post it here

  36. one thing that could be a major problem with this technique is that it hides large portions of your HTML with display: none; from your stylesheet instead of with javascript.

    In the past google has mentioned that this is a technique that is used for blackhat SEO purposes and your text either isn't indexed or your page rank suffers (can't remember which)...

    The way I ended up getting the best of both worlds (thanks to a lot of the info above) was to use old fashioned DOM scripting technique to add the jsActive class to the HTML tag, then also use DOM scripting to add a style tag to the head of the document and feed it the divs I need to hide... This way you don't have display: none added with CSS...

    // add class of .jsActive to pages when javascript is available
    document.documentElement.className = 'jsActive';

    // add a style tag to the head of the document and hide some stuff up front before the DOM is ready so there's no flash of content
    var headTag = document.getElementsByTagName('head')[0];
    script = document.createElement('style');
    script.type = 'text/css';
    script.innerHTML = '#JSInd, #home #ShortGrass, .Hide { display: none; }';
    headTag.appendChild(script);

    I'm pretty sure this would be enough for google to at least think you have good intentions... Maybe someone who knows more about the SEO angle can chime in, because you could also use javascript to hide key words, so maybe google is savvy to this as well?

    brent
    @
    mimoYmima.com

    • That's interesting. I hadn't considered the SEO angle. Still, since the elements are only receiving the display:none; with JavaScript enabled, I'm not sure it makes a difference. Another way to handle it is to avoid using display:none altogether. You could set your .js descendant elements that you want hidden to have text-indent: -1000em; or position:relative; left: -1000em; instead.

      • hiding the text by positioning it off screen was my first idea, but it didn't work for me with .show() for some reason.

      • It's a good point that the display: none isn't used unless javascript is active. That might be enough to make it not matter that it's in the stylesheet... Has anyone done any tests using hidden content to see what's indexed and what isn't by chance?

    • if anyone wants to use the method above you'll need to change the part that adds the style tag to the document to this:

      var styleTag = document.createElement("style");
      var text = document.createTextNode("#JSInd, #home #ShortGrass, .Hide { display: none; }");
      styleTag.appendChild(text);
      document.getElementsByTagName("head")[0].appendChild(styleTag);

      I'm not a veteran with DOM Scripting methods, but the other code wasn't working in Chrome, but changing the method from innerHTML to .createTextNode seems to fix the problem

  37. Ok, I am going nuts trying to get this to work. Does it matter if the #flash is buried inside other div's? It just doe snot want to co-operate. Using FF 3.5

  38. works in your page no problem. I sent you an email with the login.

  39. Karl, this is great. Forgive the noob question, but what actually goes in place of " // Stuff to do as soon as the DOM is ready" ? I can get it to hide but not reappear at my site. Thanks!

    • Hi Nathan,

      Maybe a different example would help. Let's say you have a utility CSS class called "js-hide" that you want to add to all elements that should be hidden when the page loads. You could do that like so:

      <script type="text/javascript">
        document.documentElement.className = 'js';
      </script>
      <style type="text/css">
        .js .js-hide { display: none; }
      </style>
      

      Then, you might have an element in your html like this (with two classes, "js-hide" and "something"):

      <div class="js-hide something">this is initially hidden</div>
      

      You can show that div later on, based on some user input or some amount of elapsed time or whatever. For example, you could toggle its visibility in response to a user clicking on a button:

      
      <script type="text/javascript">
        $(document).ready(function() {
          $('#my-button').click(function() {
            $('div.something').toggle();
          });
        });
      </script>
      

      I hope that helps!

  40. to add part of html, then it is possible so:

    $('html').html();
    $('#add_class_html').hide();

11 Pings

  1. The Complete jQuery Resource List for You to Become an Almighty Developer...

    Have you ever had to develop something yourself only to find out that there had already been a plugin developed?

    Don't you enjoy dreaming about what you could have on your site and finding the right plugin right away?

    Then you will find anything y...

  2. [...] entire row the same cursor as the checkbox. The rule is applied only if JavaScript is enabled (see my previous post for [...]

  3. [...] ‘FOOOOOOK’ by John Hicks) while the Javascript is being downloaded. This trick is very well explained by Karl Swedberg on the excellent learningjquery.com [...]

  4. [...] недавно случайно нашел замечательное решение одной небольшой проблемы, которая давно мозолила [...]

  5. [...] Añade una clase al objeto HTML para modificar elementos por CSS cuando el documento se haya cargado [...]

  6. [...] Karl Swedberg has posted (some time ago) a great way to avoid the dreaded flash of “hidden” content when loading lengthy pages. [...]

  7. [...] 1 (Awesome) Way To Avoid the (Not So Excellent) Flash of (Amazing) Unstyled Content » Learning... - Schon etwas älter, aber immer wieder äußerst praktisch: Durch quot;ye plain olde JavaScriptquot; lassen sich Elemente per CSS dann verändern, wenn JavaScript aktiv ist. So lässt sich jede beliebige Oberfläche durch den kleinen Zusatz .js für die jeweiligen Browsergegebenheiten anpassen. [...]

  8. [...] entire row the same cursor as the checkbox. The rule is applied only if JavaScript is enabled (see my previous post for [...]

  9. [...] 可以来这里读取Karl Swedberg关于这个例子的全文: [...]

  10. [...] .JS #myDiv{display:none;} [/js] So, what this means is that we can hide content when JavaScript is switched on and then use jQuery to show it when necessary (e.g. by collapsing some panels and expanding them when the user clicks on them), while those with JavaScript off (and search engine spiders) see all of the content as it's not hidden. I'll be using this one a lot in the future. To read his full article click here. [...]

  11. [...] good. Scripts in the body block rendering so we can do better[1]: <head> <script>document.documentElement.className += [...]

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.