Avoiding the FOUC v3.0
FOUC is an unwelcome guest to your soirée of intertube funtimes. He comes in and distracts users eyes with things they shouldn't be seeing, and then departs ever so quickly. We don't like him.
So you've likely seen code like this:
<body> <script>document.body.className += 'js';</script>
No good. Scripts in the body block rendering so we can do better[1]:
<head> <script>document.documentElement.className += 'js';</script>
Here we'll be adding the class to the HTML element instead of the body, which we have access while we're in the HEAD. (Naturally CSS works just fine with a html.js selector, though this doesn't validate in HTML4)
But here's my big hang-up:
I prefer to write unique css for the no-javascript user. I don't want to be writing .js in front of every selector for my basic accordion/carousel/etc widgets. It's terribly tedious. I really just want a .no-js hook.
My solution:
<html class="no-js"> <head> <script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script>
The markup has a no-js class on HTML by default but we'll very safely change that to 'js' inside the head. That compressed line of javascript is basically:
document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/,'js');
But I make it small as it's one of the only scripts that should run in your head. Because everyone has their scripts near their </body> tag, right?
We use the same approach in Modernizr, because we want the classes to be all set on the HTML element right when the BODY content starts loading in. Fight the FOUC!
Follow me on twitter: @paul_irish
btw- If I'm not using Modernizr, I'll typically use this in addition to the HTML5 enabling script that does the document.createElement loop for IE. And only those two scripts go in the head.
I've started to use javascript to load stylesheets as needed.
This has the advantage of not cluttering other stylesheets with ui widget styles, and also since the styles will only be applied if javascript is enabled no '.js' prefixing is necessary.
@Pete B Interesting. However grabbing stylesheets is asynchonous so there's the possibility for a FOUC. I like the general idea, though. :)
As a javascript noob, you make a throw-away remark: "you all put your scripts near the tag, right?" Can you explain?
@dizzley
The yahoo exceptional performance site should explain that recommendation:
http://developer.yahoo.com/performance/rules.html#js_bottom
Also the google code speed site has a good summary:
http://code.google.com/speed/page-speed/docs/payload.html#DeferLoadingJS
@Paul Irish
The Yahoo recommendations make good reading… I'll read the Google code page tomorrow.
I see.
After a gazillion years in the business I've seen optimization worries with every innovation. The day will surely come when tools/servers optimise this kind of thing for you, like compilers routinely make register optimizations now.
I thing it's a reasonable pattern in sw development to:
1) get it working
2) get it right
3) refactor for changes and fixes
4) refactor for optimization
(4) comes last, but (1) and (2) are best served by the zen developer who is in tune with good practice.
Thanks for the response.
Hi Paul, first time visitor here.
First, nice improvement of a now rather common trick.
I usually apply the "js" class to body element, by simple adding $('body').addClass('js') on document ready. But I like your proposed solution, as it let you have the class on a parent element already applied even before the body is loaded, a trick that may bring some benefits.
Also, I like the "no-js" to "js" switch, a nice extra tip I'll have to experiment with.
I usually write the unique CSS for javascript visitors, but I will try the other way around, as you also suggested.
But let me tell you what brought me to your site.
To avoid FOUC, I have used this other trick (Avoiding Flickering in jQuery) many many times (basically, body is hidden via JS by setting display:none and then displayed again on document ready).
That trick may trigger some undesired issues. It may trigger problems if any trick relies on testing dimensions (width, height, etc) on elements.
But until today, I didn't notice that it also break any #anchor link (aka local scroll). That means that if someone tried to visit http://example.com/faq#f20, the page wouldn't scroll to element with id="f20".
So, while looking for a solution, I arrived here, and it inspired me to try a variation on the "Avoiding flickering in jQuery" trick.
Instead of applying "display:none" (via JS, to body element) I tried applying "visibility:hidden" and then, on document.ready, setting it again to "visibility:visible".
That only change fixes the local anchor scroll while keeping the FOUC away.
And now that Google is including anchor links on their SERPs, it's good to have them (the local anchors) working :)
Please, let me know what you think, and feel free to publish this discoverings on "Avoiding the FOUC v4" (as I don't have my own blog to do it).
Thanks.
Would it not be easier to drop the "no-js" class and assume the user has javascript disabled, then style specificly for the "js" class and overwrite the default selectors if it is enabled? On the plus, I guess it's easier when you're reading the CSS doc.
@Jordan Boesch
Yeah this emerged because I prefer styling overrides for the .no-js class.
Like if I'm gonna style a carousel or modal window I hate to prefix like every style with .js
The idea to change the class from nojs to withjs of the html element (not just for FOUD purposes but for handling other dynamic stuff) is nice. Problem only that class is not an allowed attribute on the html element. But id is and should work the same, also one could simple swap the id valid (as id obviously [currently] has a single value only). Any problems known?
@Christof
Yup, classing the HTML element will cause an error when validating to HTML4 or XHTML1.1. (It is fine in HTML5).
You could use an ID if you want, though I think it adds a specificity that's too high, and limits your use of the html ID later on. But yah you could do that, definitely:
@Paul: Yes there may be an issue with specificity but actually why should there be? I would only define very specific stuff for #js or #nonjs subelements so id may even be quicker? Anyway, just a follow up question, for this script I would just use "document.documentElement.id = 'js'", there isn't a real benefit in using the non name leaking function approach, or is it?
@Christof,
Specificity aint a big deal, really. :) You could do that yup, but like I mentioned, I prefer to write my styles for the .no-js case instead of the .js case. Personal preference, I suppose.
You could save another character or two using a "with" statement. But don't tell anyone I said that.
@BarelyFitz
Nice catch.