1 (Awesome) Way To Avoid the (Not So Excellent) Flash of (Amazing) Unstyled Content
read 31 commentsThis 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:
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>
-
<head>
-
<!-- etc. -->
-
<style type="text/css">
-
.js #flash {display: none;}
-
</style>
-
<script type="text/javascript" src="/scripts/jquery.js"></script>
-
<script type="text/javascript">
-
$('html').addClass('js');
-
$(document).ready(function() {
-
// Stuff to do as soon as the DOM is ready
-
});
-
</script>
-
</head>
-
<body>
-
<!-- etc. -->
-
</body>
-
</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>
-
<head>
-
<!-- etc. -->
-
<style type="text/css">
-
.js #flash {display: none;}
-
</style>
-
<script type="text/javascript">
-
document.getElementsByTagName('html')[0].className = 'js';
-
</script>
-
</head>
-
<body>
-
<!-- etc. -->
-
-
<script type="text/javascript" src="/scripts/jquery.js"></script>
-
<script type="text/javascript">
-
// Stuff to do as soon as the body finishes loading.
-
// No need for $(document).ready() here.
-
</script>
-
</body>
-
</html>
This one, demonstrated here, hides <ul id="flash"> as completely and awesomely as the previous one. Simple!
[P.S. Have you noticed all the (awesome) articles lately showing the incredibly (excellent) things you can do with the ( amazing) jQuery JavaScript Library?]
31 comments
3 Pings
-
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...
-
[...] entire row the same cursor as the checkbox. The rule is applied only if JavaScript is enabled (see my previous post for [...]
-
[...] ‘FOOOOOOK’ by John Hicks) while the Javascript is being downloaded. This trick is very well explained by Karl Swedberg on the excellent learningjquery.com [...]












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
.jspart 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
htmlelement, so a theoretical super strict browser could ignore it. In practice all browsers seem to support it.)PS: Nice new design!
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!
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
htmlelement. 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.
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.
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.
I apologize if I've missed something in the example, but shouldn't instead be
document.getElementsByTagName('html')[0].className = 'js';?You want the
htmltag, right?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!
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.
@Karl: Do they incorporate post updates?
>> Yep
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>
');
}
will haven,
Yeah, just like Klaus mentioned in the first comment.
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.
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?)
By the way, you don't have to necessarily document.write a
styletag. You can as well use alink.@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.
Why not
$('head').append('<style type="text/css">#fox {display:none}</style>')
outside ready()?
Ricardo,
I suppose that would be fine, too. It might get a little messy if you have multiple style rules you want to apply.
Thanks for the tip Karl, this is just what I needed for a website I've been working on!!
Keep up the good work!
I've been using a similar technique: avoiding flickering in jQuery.
If you put your JS includes at the bottom of the document you can use the
document.documentElementreference instead of looking for it usinggetElementsByTagName.Replace this:
document.getElementsByTagName('html')[0].className = 'js';With this:
document.documentElement.className = 'js';thanks for the tip, Brandon!
I'm actually using
.nojsrather than.jsin thehtmltag. So I'm going against the tide - I hard code.nojsand 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.
Ca-Phun Ung, one difference is that the class attribute is not valid on the
htmlelement, so if you're concerned at all about your page validating, adding it with JavaScript would be better than removing it with JavaScript.@karl: Point taken - that never crossed my mind
And what about progressive enhancement?
It is explained in this ALA article
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.
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.
I'm sorry, too, Markus. I shouldn't have been so defensive. I've read Scott's article and enjoyed it.
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.
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.