Saturday, February 24, 2007

Workaround for the Safari FOUC bug

The Safari browser occasionally exhibits the dreaded Flash of Unstyled Content (FOUC) bug, in which the webpage is unstyled black-on-white text for a second or two before the styles kick in. This happens because Safari loads CSS and JavaScript in parallel - if the JavaScript accesses certain properties, it triggers a page render in Safari, even if the CSS hasn't fully loaded. Hence, the Flash of Unstyled Content. (Firefox does not suffer from this because it loads CSS and JavaScript in series rather than in parallel).

A workaround is to hide the body before the CSS files have loaded, and show the body afterwards:

body { display: none; }

body { display: block; }

Save these lines as safari-fouc-workaround-1.css and safari-fouc-workaround-2.css, respectively. Then place the following at the top of the <head> section, before your CSS declarations:

<link rel="stylesheet" type="text/css" media="screen" href="/safari-fouc-workaround-1.css" />

And after your CSS declarations, at the bottom of the <head> section, put:

<link rel="stylesheet" type="text/css" media="screen" href="/safari-fouc-workaround-2.css" />

(Even better, add these lines only if you detect that the browser is Safari.)

Update: The best way to implement this workaround is as follows:

Put the following in <head>:

<?php
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== FALSE) { ?>
<!-- First part of FOUC workaround -->
<style type="text/css">body { display: none; }</style>
<?php
}
and the following at the end of your main CSS file:
/* Second part of FOUC workaround */
body { display: block; }

31 comments:

  1. Update: To save an HTTP request, the first statement can be done inline rather than in a separate file.

    <style type="text/css">body { display: none; }</style>

    ReplyDelete
  2. This didn't work for me at first but when I created two inline styles and put one in the header just after the "title" tag and the other just BEFORE the "/html" tag (closing html tag) then everything worked; I no longer get the 'flash'.

    Yours a fairly elegant solution that I've spent much time looking for. Thanks.

    ReplyDelete
  3. Actually I am finding that, rather than putting "body { display: block; }" in a separate file, it is better to put it at the end of your main css file. This will ensure that, in Safari, the page will not display anything until the main css file finishes loading.

    ReplyDelete
  4. Here's the PHP code for what goes at the top:

    if (mb_strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== FALSE) { ?>
    <style type="text/css">body { display: none; }</style>
    <?php
    }

    ReplyDelete
  5. Hi Jonathan, I also found that using your snippet of php in the header to set display none for safari and just having body { display:block; } at the end of my master stylesheet was the only was to stop the fouc, great fix thanks man!

    ReplyDelete
  6. Thanks Jonathan and Varen.
    I tried Varen's adaptation and this worked beautifully!

    ReplyDelete
  7. that is like my nightmares ended...
    that works nice in IE too!!!

    thanks a lot!

    ReplyDelete
  8. Hey Jonathan! First thank you for this workaround. I have tried your code but it doesn't work for me. I'm using Safari 4.0 and when i'm previewing my site in safari i see this white flash between pages. Can you some how show me an example of some code with your implemented workaround or just point me a link.I'll be very thankful.

    ReplyDelete
  9. Hi Antoniya—We’re using it for Ning sites, e.g., http://accp.ning.com/.

    What’s the URL of your site?

    ReplyDelete
  10. some how i cant find the snippet on your page (i'm not the best web master heheh). my site is http://ironspiderart.com/demo/index.html and if you can offer me some solution it will be great. thank you in advance

    ReplyDelete
  11. Actually it’s weird—I see the “body { display: none; }” and “body { display: block; }” when I do View Source on Chrome but not on Safari 3.2.1 for Windows. Do you see the two lines when you do View Source?

    ReplyDelete
  12. no nothing here my page is clear . i was trying and maybe the site needs time to refresh but now is without any workaround. im using safari 4.0 with mac

    ReplyDelete
  13. k—make sure those two lines appear on Safari, and hopefully the Flash Of Unstyled Content will go away.

    ReplyDelete
  14. i think i'm inserting them not on the right place and i'm not shure if i have to insert the php code also. is there a chance that you can insert this lines in my website code and send them back to me??
    i'll really appreciate this . i hope i'm not causing you extra job :)

    ReplyDelete
  15. I used this work around perviously for version 2 + 3 of safari and it worked well. However the FOUC bug does not seem to like this fix when using Safari 4. The exact same pages now show the FOUC behavior in Safari 4.

    Any other suggestions? Thanks

    ReplyDelete
  16. It's exactly what i was looking for the last 8 hours but when i red to the bottom of the thread I realized that it doesn't work anymore for Safari 4. Let me know if see or come up with any other solution for it? I would greatly appreciate it.

    ReplyDelete
  17. CAVEAT: Putting the second inline style tag between the closing body and html tags will lead to invalid (X)HTML. Nothing should go between these tags.

    ReplyDelete
  18. Just fixed my problem with the white flash which is seen just before loading a page with a dark background.

    In the opening html tag add the background color you want.

    (Turn the symbols >< around, I couldn't post these lines otherwise because of the html tags)

    >html style="background-color:#000000;"<

    so this works for me in a standard opening html tag:

    >html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" style="background-color:#000000;"<

    ReplyDelete
  19. This comment has been removed by the author.

    ReplyDelete
  20. The FOUC also appears on Chrome, so having the php condition is probably not the best solution.

    Anyway, as some people pointed out, it seems the trick is not working anymore...

    [EDIT]
    However, it seems that having body, html instead of only body does the trick!

    ReplyDelete
  21. Cheers... this saved me some time!

    ReplyDelete
  22. Actually I am finding that, rather than putting "body { display: block; }" in a separate file, it is better to put it at the end of your main css file.
    Desert Safari

    ReplyDelete
  23. 1. I would not do this hack in 2014. If you are doing the hack, I would recommend removing it.

    2. Put js scripts at the bottom of your page, not at the top. The running of the js scripts will block the rest of the page from rendering.

    ReplyDelete