Is the recommendation to include CSS before JavaScript invalid?

Is the recommendation to include CSS before JavaScript invalid?

In countless places online I have seen the recommendation to include CSS prior to JavaScript. The reasoning is generally, of this form:

When it comes to ordering your CSS and JavaScript, you want your CSS
to come first. The reason is that the rendering thread has all the
style information it needs to render the page. If the JavaScript
includes come first, the JavaScript engine has to parse it all before
continuing on to the next set of resources. This means the rendering
thread can’t completely show the page, since it doesn’t have all the
styles it needs.

My actual testing reveals something quite different:
My test harness
I use the following Ruby script to generate specific delays for various resources:
require ‘rubygems’
require ‘eventmachine’
require ‘evma_httpserver’
require ‘date’

class Handler < EventMachine::Connection include EventMachine::HttpServer def process_http_request resp = EventMachine::DelegatedHttpResponse.new( self ) return unless @http_query_string path = @http_path_info array = @http_query_string.split("&").map{|s| s.split("=")}.flatten parsed = Hash[*array] delay = parsed["delay"].to_i / 1000.0 jsdelay = parsed["jsdelay"].to_i delay = 5 if (delay > 5)
jsdelay = 5000 if (jsdelay > 5000)

delay = 0 if (delay < 0) jsdelay = 0 if (jsdelay < 0) # Block which fulfills the request operation = proc do sleep delay if path.match(/.js$/) resp.status = 200 resp.headers["Content-Type"] = "text/javascript" resp.content = "(function(){ var start = new Date(); while(new Date() - start < #{jsdelay}){} })();" end if path.match(/.css$/) resp.status = 200 resp.headers["Content-Type"] = "text/css" resp.content = "body {font-size: 50px;}" end end # Callback block to execute once the request is fulfilled callback = proc do |res| resp.send_response end # Let the thread pool (20 Ruby threads) handle request EM.defer(operation, callback) end end EventMachine::run { EventMachine::start_server("0.0.0.0", 8081, Handler) puts "Listening..." } The above mini server allows me to set arbitrary delays for JavaScript files (both server and client) and arbitrary CSS delays. For example, http://10.0.0.50:8081/test.css?delay=500 gives me a 500 ms delay transferring the CSS. I use the following page to test.


test


Elapsed time is:


When I include the CSS first, the page takes 1.5 seconds to render:

When I include the JavaScript first, the page takes 1.4 seconds to render:

I get similar results in Chrome, Firefox and Internet Explorer. In Opera however, the ordering simply does not matter.
What appears to be happening is that the JavaScript interpreter refuses to start until all the CSS is downloaded. So, it seems that having JavaScript includes first is more efficient as the JavaScript thread gets more run time.
Am I missing something, is the recommendation to place CSS includes prior to JavaScript includes not correct?
It is clear that we could add async or use setTimeout to free up the render thread or put the JavaScript code in the footer, or use a JavaScript loader. The point here is about ordering of essential JavaScript bits and CSS bits in the head.

Solutions/Answers:

Solution 1:

This is a very interesting question. I’ve always put my CSS <link href="...">s before my JS <script src="...">s because “I read one time that it’s better.” So, you’re right; it’s high time we do some actual research!

I set up my own test harness in Node (code below). Basically, I:

  • Made sure there was no HTTP caching so the browser would have to do a full download each time a page is loaded.
  • To simulate reality, I included jQuery and the H5BP CSS (so there’s a decent amount of script/CSS to parse)
  • Set up two pages – one with CSS before script, one with CSS after script.
  • Recorded how long it took for the external script in the <head> to execute
  • Recorded how long it took for the inline script in the <body> to execute, which is analogous to DOMReady.
  • Delayed sending CSS and/or script to the browser by 500ms.
  • Ran the test 20 times in the 3 major browsers.

Results

First, with the CSS file delayed by 500ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 583ms  36ms  | 559ms  42ms  | 565ms 49ms
St Dev      | 15ms   12ms  | 9ms    7ms   | 13ms  6ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 584ms  521ms | 559ms  513ms | 565ms 519ms
St Dev      | 15ms   9ms   | 9ms    5ms   | 13ms  7ms

Next, I set jQuery to delay by 500ms instead of the CSS:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 597ms  556ms | 562ms  559ms | 564ms 564ms
St Dev      | 14ms   12ms  | 11ms   7ms   | 8ms   8ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 598ms  557ms | 563ms  560ms | 564ms 565ms
St Dev      | 14ms   12ms  | 10ms   7ms   | 8ms   8ms

Finally, I set both jQuery and the CSS to delay by 500ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 620ms  560ms | 577ms  577ms | 571ms 567ms
St Dev      | 16ms   11ms  | 19ms   9ms   | 9ms   10ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 623ms  561ms | 578ms  580ms | 571ms 568ms
St Dev      | 18ms   11ms  | 19ms   9ms   | 9ms   10ms

Conclusions

First, it’s important to note that I’m operating under the assumption that you have scripts located in the <head> of your document (as opposed to the end of the <body>). There are various arguments regarding why you might link to your scripts in the <head> versus the end of the document, but that’s outside the scope of this answer. This is strictly about whether <script>s should go before <link>s in the <head>.

In modern DESKTOP browsers, it looks like linking to CSS first never provides a performance gain. Putting CSS after script gets you a trivial amount of gain when both CSS and script are delayed, but gives you large gains when CSS is delayed. (Shown by the last columns in the first set of results.)

Given that linking to CSS last does not seem to hurt performance but can provide gains under certain circumstances, you should link to external stylesheets after you link to external scripts only on desktop browsers if the performance of old browsers is not a concern. Read on for the mobile situation.

Why?

Historically, when a browser encountered a <script> tag pointing to an external resource, the browser would stop parsing the HTML, retrieve the script, execute it, then continue parsing the HTML. In contrast, if the browser encountered a <link> for an external stylesheet, it would continue parsing the HTML while it fetched the CSS file (in parallel).

Hence, the widely-repeated advice to put stylesheets first – they would download first, and the first script to download could be loaded in parallel.

However, modern browsers (including all of the browsers I tested with above) have implemented speculative parsing, where the browser “looks ahead” in the HTML and begins downloading resources before scripts download and execute.

In old browsers without speculative parsing, putting scripts first will affect performance since they will not download in parallel.

Browser Support

Speculative parsing was first implemented in: (along with the percentage of worldwide desktop browser users using this version or greater as of Jan 2012)

  • Chrome 1 (WebKit 525) (100%)
  • IE 8 (75%)
  • Firefox 3.5 (96%)
  • Safari 4 (99%)
  • Opera 11.60 (85%)

In total, roughly 85% of desktop browsers in use today support speculative loading. Putting scripts before CSS will have a performance penalty on 15% of users globally; YMMV based on your site’s specific audience. (And remember that number is shrinking.)

On mobile browsers, it’s a little harder to get definitive numbers simply due to how heterogeneous the mobile browser and OS landscape is. Since speculative rendering was implemented in WebKit 525 (released Mar 2008), and just about every worthwhile mobile browser is based on WebKit, we can conclude that “most” mobile browsers should support it. According to quirksmode, iOS 2.2/Android 1.0 use WebKit 525. I have no idea what Windows Phone looks like.

However, I ran the test on my Android 4 device, and while I saw numbers similar to the desktop results, I hooked it up to the fantastic new remote debugger in Chrome for Android, and Network tab showed that the browser was actually waiting to download the CSS until the JavaScripts completely loaded – in other words, even the newest version of WebKit for Android does not appear to support speculative parsing. I suspect it might be turned off due to the CPU, memory, and/or network constraints inherent to mobile devices.

Code

Forgive the sloppiness – this was Q&D.

app.js

var express = require('express')
, app = express.createServer()
, fs = require('fs');

app.listen(90);

var file={};
fs.readdirSync('.').forEach(function(f) {
    console.log(f)
    file[f] = fs.readFileSync(f);
    if (f != 'jquery.js' && f != 'style.css') app.get('/' + f, function(req,res) {
        res.contentType(f);
        res.send(file[f]);
    });
});


app.get('/jquery.js', function(req,res) {
    setTimeout(function() {
        res.contentType('text/javascript');
        res.send(file['jquery.js']);
    }, 500);
});

app.get('/style.css', function(req,res) {
    setTimeout(function() {
        res.contentType('text/css');
        res.send(file['style.css']);
    }, 500);
});


var headresults={
    css: [],
    js: []
}, bodyresults={
    css: [],
    js: []
}
app.post('/result/:type/:time/:exec', function(req,res) {
    headresults[req.params.type].push(parseInt(req.params.time, 10));
    bodyresults[req.params.type].push(parseInt(req.params.exec, 10));
    res.end();
});

app.get('/result/:type', function(req,res) {
    var o = '';
    headresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    o+='\n';
    bodyresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    res.send(o);
});

css.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <link rel="stylesheet" href="style.css">
        <script src="jquery.js"></script>
        <script src="test.js"></script>
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

js.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <script src="jquery.js"></script>
        <script src="test.js"></script>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

test.js

var jsload = Date.now();


$(function() {
    $.post('/result' + location.pathname.replace('.html','') + '/' + (jsload - start) + '/' + (bodyexec - start));
});

jquery.js was jquery-1.7.1.min.js

Solution 2:

There are two main reasons to put CSS before JavaScript.

  1. Old browsers (Internet Explorer 6-7, Firefox 2, etc.) would block all subsequent downloads when they started downloading a script. So if you have a.js followed by b.css they get downloaded sequentially: first a then b. If you have b.css followed by a.js they get downloaded in parallel so the page loads more quickly.

  2. Nothing is rendered until all stylesheets are downloaded – this is true in all browsers. Scripts are different – they block rendering of all DOM elements that are below the script tag in the page. If you put your scripts in the HEAD then it means the entire page is blocked from rendering until all stylesheets and all scripts are downloaded. While it makes sense to block all rendering for stylesheets (so you get the correct styling the first time and avoid the flash of unstyled content FOUC), it doesn’t make sense to block rendering of the entire page for scripts. Often scripts don’t affect any DOM elements or just a portion of DOM elements. It’s best to load scripts as low in the page as possible, or even better load them asynchronously.

It’s fun to create examples with Cuzillion. For example, this page has a script in the HEAD so the entire page is blank until it’s done downloading. However, if we move the script to the end of the BODY block the page header renders since those DOM elements occur above the SCRIPT tag, as you can see on this page.

Solution 3:

I would not emphasize too much on the results that you have got, I believe that it is subjective, but I have a reason to explain you that it is better to put in CSS before js.

During the loading of your website, there are two scenarios that you would see:

CASE 1: white screen > unstyled website > styled website > interaction > styled and interactive website

CASE 2: white screen > unstyled website > interaction > styled website > styled and interactive website

I honestly can’t imagine anyone choosing Case 2. This would mean that visitors using slow internet connections will be faced with an unstyled website, that allows them to interact with it using Javascript (since that is already loaded). Furthermore, the amount of time spend looking at an unstyled website would be maximized this way. Why would anyone want that?

It also works better as jQuery states

“When using scripts that rely on the value of CSS style properties,
it’s important to reference external stylesheets or embed style
elements before referencing the scripts”.

When the files are loaded in the wrong order (first JS, then CSS), any Javascript code relying on properties set in CSS files (for example the width or height of a div) won’t be loaded correctly. It seems that with the wrong loading order, the correct properties are ‘sometimes’ known to Javascript (perhaps this is caused by a race condition?). This effect seems bigger or smaller depending on the browser used.

Solution 4:

Were your tests performed on your personal computer, or on a web server? It is a blank page, or is it a complex online system with images, databases, etc.? Are your scripts performing a simple hover event action, or are they a core component to how your website renders and interacts with the user? There are several things to consider here, and the relevance of these recommendations almost always become rules when you venture into high-caliber web development.

The purpose of the “put stylesheets at the top and scripts at the bottom” rule is that, in general, it’s the best way to achieve optimal progressive rendering, which is critical to the user experience.

All else aside: assuming your test is valid, and you really are producing results contrary to the popular rules, it’d come as no surprise, really. Every website (and everything it takes to make the whole thing appear on a user’s screen) is different and the Internet is constantly evolving.

Solution 5:

I include CSS files before Javascript for a different reason.

If my Javascript needs to do dynamic sizing of some page element (for those corner cases where CSS is really a main in the back) then loading the CSS after the JS is russing can lead to race conditions, where the element is resized before CSS styles are applied and thus looks weird when the styles finally kick in. If I load the CSS beforehand I can guarantee that things run in the intended order and that the final layout is what I want it to be.

Solution 6:

Is the recommendation to include CSS before JavaScript invalid?

Not if you treat it as simply a recommendation. But if your treat it as a hard and fast rule?, yes, it is invalid.

From https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded

Stylesheet loads block script execution, so if you have a <script>
after a <link rel="stylesheet" ...> the page will not finish parsing
– and DOMContentLoaded will not fire – until the stylesheet is loaded.

It appears that you need to know what each script relies on and make sure that execution of the script is delayed until after the right completion event. If the script relies only on the DOM, it can resume in ondomready/domcontentloaded, if it relies on images to be loaded or stylesheets to be applied, then if I read the above reference correctly, that code must be deferred until the onload event.

I don’t think that one sock size fits all, even though that is the way they are sold and I know that one shoe size does not fit all. I don’t think that there is a definitive answer to which to load first, styles or script. It is more a case by case decision of what must be loaded in what order and what can be deferred until later as not being on the “critical path”.

To speak to the observer that commented that it is better to delay the users ability to interact until the sheet is pretty. There are many of you out there and you annoy your counterparts that feel the opposite. They came to a site to accomplish a purpose and delays to their ability to interact with a site while waiting for things that don’t matter to finish loading are very frustrating. I am not saying that you are wrong, only that you should be aware that there is another faction that exists that does not share your priority.

This question particularly applies to all of the ads being placed on web sites. I would love it if site authors rendered just placeholder divs for the ad content and made sure that their site was loaded and interactive before injecting the ads in an onload event. Even then I would like to see the ads loaded serially instead of all at once because they impact my ability to even scroll the site content while the bloated ads are loading. But that is just one persons point of view.

  • Know your users and what they value.
  • Know your users and what browsing environment they use.
  • Know what each file does, and what its pre-requisites are. Making everything work will take precedence over both speed and pretty.
  • Use tools that show you the network time line when developing.
  • Test in each of the environments that your users use. It may be needed to dynamically (server side, when creating the page) alter the order of loading based on the users environment.
  • When in doubt, alter the order and measure again.
  • It is possible that intermixing styles and scripts in the load order will be optimal; not all of one then all of the other.
  • Experiment not just what order to load the files but where. Head? In Body? After Body? DOM Ready/Loaded? Loaded?
  • Consider async and defer options when appropriate to reduce the net delay the user will experience before being able to interact with the page. Test to determine if they help or hurt.
  • There will always be trade offs to consider when evaluating the optimal load order. Pretty vs. Responsive being just one.