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!
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.
@Julián Landerreche
I've implemented this idea but am wondering how I could add a preloader?
Hey Paul, let's say a page has JavaScript in the HEAD that is used to add a class named js to the HTML element. This js class is used to write CSS that hides elements that will later be displayed using jQuery (e.g., an accordion). Won't these hidden elements remain hidden in browsers that support JavaScript but are not supported by jQuery? If so, should the code below be loaded after the jQuery library is loaded?
document.documentElement.className.replace(/\bjs\b/, ");
jQuery('html').addClass('js');
Is this beneficial? Is there any downside to using this code?
Thanks.
@Josh
> Won't these hidden elements remain hidden in browsers that support JavaScript but are not supported by jQuery?
what browsers are those?
to be honest, it's not at all worth it to go down that road. You may address 0.001% of your audience and spend way too much time with a rearchitecture.
For your purposes, just envision that supports jquery == supports javascript. :)
Hi paul, I don't exactly understand the benefit of a class switch instead of a simple class append.
If you assume an user agent with javascript disabled then there's no need to code your css like ".no-js .yourplugin". So, using a selector like ".yourplugin", will be enough to create the style with no js available.
Besides, using "no-js" everywhere on CSS, for each dynamic element in your page is clearly redundant and from my point of view the only benefit could be a better identification between style for js and style for no-js. Or am I missing something?
Thank you again
@fcaldera
I want a higher selector specificity, so I can use .no-js styles to override previous things.
But I know some people prefer styling by adding .js in front of everything that will rely on javascript. For me, that's just not practical.. half of my styles would start with .js
So having the option to do .no-js or .js suits both types of people.
ok, this sounds reasonable. thank you. :)
Interesting technique, Paul. =) My prior solution to that stupid FOUCing issue was to hide the worst-affected elements in the core CSS, using a style like ".nofouc" to negatively offset the items a few thousand pixels off the edge of the monitor. Once the DOM had loaded, jQuery would run through and strip the class name from said elements, suddenly snapping them into view.
Of course, this would mean JS-disabled browsers would be left staring at an empty screen – so my approach was to stick an old-school tag into the head:
And the noscript.css contained rules for negating the anti-FOUC styles included in earlier stylesheets. Not the prettiest or the most elegant solution (not to mention it doesn't validate against XHTML Strict), but it works like an absolute charm.
The technique is valid HTML5 though, so… one more reason to look forward to the future. ;)
Hi Paul,
I'm using this trick for a while, and now prefer external JS for the class change, because some users (in enterprises mostly) do have JS enabled, but proxies that don't allow external .js files (for security reasons).
Your inline JS would work, but further JS loaded from external .js files wont.
-Nicolas
Paul, first, great article. I'm wanting to do exactly this with two jquery (accordion and tabs) widgets on a page, and I understand the concept, but am struggling with the actual syntax. I'm not pulling any errors at all. Can anyone give me a specific example of using the id as Christof suggested (or class – but will that screw up the pre-defined css for the widget?) with the no-js css and js css and the html/javascript? I know this is probably a js/html/css-101 question and I feel stupid for asking, but I've got to start somewhere with this. Thanks.
@Christof
I just downloaded and added the Moderizer script just above the tag, but I'm not sure if that is the right place to put it. Should it go in the head? The reason I am asking is because it already replaces the no-js class in the html tag with js class. Or should I keep it just above the closing body tag and add the one line class replacement code you show here. Is it redundant to use this method and the modernizer script?
-Felson
First line should have read "Moderizer script just above the tag"
The first line should have read that I added the Moderizer script just above the closing body tag, sorry.
If you want to see the difference between including scripts or css at the bottom or the top you can use this page http://stevesouders.com/cuzillion/
@Paul Irish
Hi.
I see what you've done enabling the use of no-js or js, but I'm with you on the front that I only want to use no-js so I actually don't want the js class available. Is there a cleaner way of removing the class that using this code?:
(function(H){H.className=H.className.replace(/\bno-js\b/,")})(document.documentElement)
That's just yours without the replacement string.
Cheers.
@Mark Weston
p.s. Is there a way of making sure that no spaces are left behind after removing the class i.e. if it's the last class or the first one of multiple classes?
Isn't there a bug with multiple classes? e.g. \bno-js\b is replaced with js so let's say html class='bla no-js' will turn to class='blajs'?
@Pluto
nope? the \b looks for a word boundary not whitespace.
Hi. I simplified this script significantly.
The only difference I see is that this will remove all classes from the html element, which I don't think is a problem. Any other javascript that changes the class of the html element (such as cufon) is not affected, because this executes before any other script. Does anyone see an issue with this?
Hey, i'm trying to implement this but maybe i'm mis-understanding what it's supposed to do – if you put the class="js" and the script in the head, isn't it supposed to remove the "no-js" class for javascript-enabled browsers?
The class seems to remain as "no-js" on all browsers for me. Even your source code is showing the "no-js" class still on the html tag in Chrome with JS enabled?
Hmm, I think I figured this out – the "no-js" was still showing in the source code in Chrome, but not on the actual rendered page itself.
Sorry for the hasty comment, this is a really handy script!
Hi Paul,
do you think you could get the Google+ team to read your blog, at least this post? Or is this a case of prophets and countries?-)
@Claus
I know some G+ people.. Why do you ask?
@Paul Irish
Have you tried visiting any G+ thread with JS switched off, or looked at the HTML source?-)
Most sites don't work too hot with JS off. I'm over it. :)
So you now accept the G+ pattern over the one in your blog post? It is a minor change to markup that would remove an entirely unnecessary barrier to using G+. What happened to "move the web forward", then?-)
@Paul Irish Funny you link to site with exceptionally POOR performance… took a full minute to load! twice!