Home > front-end development, javascript > Avoiding the FOUC v3.0

Avoiding the FOUC v3.0

September 23rd, 2009

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!

front-end development, javascript

  1. September 23rd, 2009 at 11:10 #1

    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.

  2. September 23rd, 2009 at 14:13 #2

    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.

  3. September 23rd, 2009 at 14:22 #3

    @Pete B Interesting. However grabbing stylesheets is asynchonous so there's the possibility for a FOUC. I like the general idea, though. :)

  4. dizzley
    October 6th, 2009 at 02:37 #4

    As a javascript noob, you make a throw-away remark: "you all put your scripts near the tag, right?" Can you explain?

  5. October 6th, 2009 at 16:30 #5

    @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

  6. dizzley
    October 6th, 2009 at 17:27 #6

    @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.

  7. October 8th, 2009 at 22:50 #7

    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.

  8. November 6th, 2009 at 14:37 #8

    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.

  9. November 6th, 2009 at 15:18 #9

    @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

  10. Christof
    November 30th, 2009 at 08:23 #10

    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?

  11. November 30th, 2009 at 10:16 #11

    @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:

    (function(H){H.id=H.id.replace('no-','')})(document.documentElement)
  12. Christof
    November 30th, 2009 at 15:03 #12

    @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?

  13. December 1st, 2009 at 13:35 #13

    @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.

  14. February 11th, 2010 at 10:53 #14

    You could save another character or two using a "with" statement. But don't tell anyone I said that.

  15. February 11th, 2010 at 10:57 #15

    @BarelyFitz
    Nice catch.

    with(document.documentElement){className=className.replace(/\bno-js\b/,'js')}
  16. March 12th, 2010 at 07:24 #16

    @Julián Landerreche
    I've implemented this idea but am wondering how I could add a preloader?

  17. Josh
    June 11th, 2010 at 16:44 #17

    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.

  18. July 6th, 2010 at 22:32 #18

    @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. :)

  19. fcaldera
    August 23rd, 2010 at 06:07 #19

    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

  20. August 23rd, 2010 at 11:43 #20

    @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.

  21. fcaldera
    August 23rd, 2010 at 14:58 #21

    ok, this sounds reasonable. thank you. :)

  22. September 7th, 2010 at 01:15 #22

    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. ;)

  23. January 29th, 2011 at 15:15 #23

    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

  24. Johnlap
    March 12th, 2011 at 19:03 #24

    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

  25. Felson
    March 13th, 2011 at 10:04 #25

    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

  26. Felson
    March 13th, 2011 at 10:05 #26

    First line should have read "Moderizer script just above the tag"

  27. Felson
    March 13th, 2011 at 10:07 #27

    The first line should have read that I added the Moderizer script just above the closing body tag, sorry.

  28. Martin
    May 28th, 2011 at 13:28 #28

    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/

  29. Mark Weston
    June 14th, 2011 at 09:07 #29

    @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.

  30. Mark Weston
    June 14th, 2011 at 09:09 #30

    @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?

  31. Pluto
    July 25th, 2011 at 09:02 #31

    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'?

  32. July 25th, 2011 at 11:22 #32

    @Pluto
    nope? the \b looks for a word boundary not whitespace.

  33. gary
    August 4th, 2011 at 14:41 #33

    Hi. I simplified this script significantly.

     
     
      document.documentElement.className=''

    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?

  34. September 15th, 2011 at 09:12 #34

    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?

  35. September 15th, 2011 at 09:17 #35

    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!

  36. December 17th, 2011 at 09:05 #36

    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?-)

  37. December 17th, 2011 at 10:03 #37

    @Claus
    I know some G+ people.. Why do you ask?

  38. December 17th, 2011 at 14:18 #38

    @Paul Irish

    Have you tried visiting any G+ thread with JS switched off, or looked at the HTML source?-)

  39. December 18th, 2011 at 10:37 #39

    Most sites don't work too hot with JS off. I'm over it. :)

  40. December 18th, 2011 at 14:22 #40

    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?-)

  41. April 30th, 2012 at 15:31 #41

    @Paul Irish Funny you link to site with exceptionally POOR performance… took a full minute to load! twice!

  1. October 2nd, 2009 at 19:32 | #1
  2. February 25th, 2010 at 16:42 | #2
  3. August 18th, 2010 at 15:39 | #3
  4. September 2nd, 2011 at 13:27 | #4

For code blocks, use <pre lang="javascript">. css and html4strict are also accepted.