The FOUC Problem
FOUC stands for Flash of Unstyled Content. This situation occurs whenever a Web browser ends up showing your Web page’s content without having any style information yet. It’s an interesting technical problem, because when/how a browser ends up committing the crime of FOUCing depends heavily on how the browser’s engine is architected and on interesting assumptions made by Web site authors when designing their sites.
When a browser loads a Web page, it first fetches an HTML file from the Web site. As that HTML file is downloaded from the Web site, the browser engine begins to feed the file’s contents to its HTML parser. The browser engine builds up a tree that represents the structure of the Web page (the DOM tree) as it encounters elements and nodes in the file. At some point while parsing, the engine is going to hit a stylesheet loading directive, either inside a <style> block or in a <link> element.
It is at this point that things get interesting. The browser engine now has a choice to make. It can either stall the parsing process while it waits for the remote stylesheet to load, or it can plunge bravely ahead. Let’s examine each option.
If the browser engine stalls parsing then the processing of the entire page is now held up. Although the network layer (typically on another thread) can still provide data to the engine, this data basically queues up unprocessed. In effect the entire engine is doing nothing while it waits for this stylesheet to finish. Gecko has this behavior.
One major disadvantage of this approach is that stylesheets and scripts cannot load in parallel. If a Web page has a link element followed by a script element, the engine won’t even begin loading the script element until it has completely loaded the stylesheet. The situation gets even worse if the stylesheet itself wants to import other stylesheets, since the engine will continue to stall until the entire stylesheet tree represented by the directive in the HTML file has loaded.
This choice effectively makes stylesheets behave the same as scripts and effectively guarantees that no load parallelization will occur on anything in the head of the Web page. This sounds bad, so why do it? Well, the fundamental reason is that Web authors have essentially already written code that makes the assumption that style information accessed via scripts will be current.
For example, consider a script following a link element. If that script asks for the offsetHeight of an element, and the height was specified in the CSS file, Web page authors expect to get the right height. Even though this behavior is not part of any standard, Web designers have implicitly built the serial loading assumption into their scripts.
How did this happen? Well, primarily because existing engines behaved this way, but also because of the synchronous nature of script elements. Because scripts already stall parsing, authors just made the natural assumption that stylesheets would stall parsing too.
Verdict: Thumbs down.
WebKit has the opposite behavior and will continue parsing a page even after a stylesheet directive has been encountered so that stylesheet and script loads can be parallelized. That way everything can be ready for display much earlier.
The problem with this behavior is what to do when a script attempts to access a property that involves having accurate layout/style information to answer. Shipping Safari’s current behavior when this happens is as follows: it will go ahead and lay out what it’s got even though it doesn’t have the stylesheet yet. It will also display it. This means you see FOUC whenever a script tries to access properties like scrollHeight or offsetWidth before the stylesheet has loaded.
We’ve improved things in the WebKit nightlies a bit so that at least the rendering of unstyled content will be suppressed. However we will still give back information that the page might consider to be incorrect, since the stylesheet that supplies the up-to-date information hasn’t loaded yet.
This approach ends up being superior in speed to the stalling approach in the case where no style/layout properties are accessed. However it ends up possibly being inferior in speed to the stalling approach if such a property is accessed, depending on how severe the contortions the engine has to perform between the unstyled rendering and the final styled rendering are.
Verdict: Thumbs down.
The hurdle with this approach is that you have to be able to halt scripts mid-execution and resume them later. A technically inferior solution might be to just stall the execution of *all* scripts, but given the preponderance of script elements on most Web sites, that approach is really not much better than stalling all the time.
Verdict: Thumbs up.
Why does this matter?
The FOUC problem would normally be a minor occurrence. However with the advent of Google AdSense, FOUC has become a Safari epidemic. Because these Google ads not only execute inline script but access layout information that they often don’t even end up using in the page, the problem of FOUC is much more severe than it should be.
It is poor coding to access style/layout information repeatedly during the loading cycle of a page, because in order for the engine to supply you with an answer, it has to ensure that its layout and style are up to date. Think of every use of a property like offsetWidth as a bottleneck that stalls Web page display while the engine computes an accurate snapshot of the entire page just to give the script a correct answer. If a script accesses such properties repeatedly while making numerous other layout/style changes between accesses, it can cripple the load speed of a Web site in every modern browser.