Should CSS always precede JavaScript?
Asked Answered
L

14

975

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.

<!DOCTYPE html>
<html>
  <head>
      <title>test</title>
      <script type='text/javascript'>
          var startTime = new Date();
      </script>
      <link href="http://10.0.0.50:8081/test.css?delay=500" type="text/css" rel="stylesheet">
      <script type="text/javascript" src="http://10.0.0.50:8081/test2.js?delay=400&amp;jsdelay=1000"></script>
  </head>
  <body>
    <p>
      Elapsed time is:
      <script type='text/javascript'>
        document.write(new Date() - startTime);
      </script>
    </p>
  </body>
</html>

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

CSS first

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

JavaScript first

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.

Leporid answered 14/2, 2012 at 3:24 Comment(9)
is 1511 vs 1422 a statistically significant difference? That's 6 percent. The general threshold for notable-to-average-human performance difference is about 20 percent.Diaphane
the point is that reordering eliminates this arbitrary delay, you can set the delay to anything you want, its just a demo of the issue.Leporid
was your delay 100ms? the difference in your screenshots is 89ms. In your URL it is delay=400&amp;jsdelay=1000 and delay=500 which is nowhere near 100ms or 89ms. I guess I'm unclear which numbers you are referring to.Diaphane
"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." - if the JS include is in the head then the JS will be executed before the page is rendered regardless of whether the CSS include was before or after.Ecto
@JeffAtwood the fastest this can possibly render is 1400ms, which is delay=400&amp;jsdelay=1000 ... js downloading is "stop the world" js inline execution is "stop the world" ... once that is done the page can render... the browser is "smart" and can concurrently download the css ... however the "css" downloading appears to be holding back the "js execution" in one of the orders.Leporid
Not sure if you've considered this, but the perception of load time is important too. So, for example, if loading the CSS first gives you even just the page background color/texture, it would seem to be faster. Absolute load times might not be indicative of this.Eddieeddina
I have always read that section as meaning that the pause occurs during parsing and evaluating of the JS, not the network fetch time. But I've only done very basic testing to verify that it makes any difference.Eliath
@JeffAtwood Every little speed increase brings more retention, even if only 1% notices. Users expect faster & faster web, even with poor conditions like clogged wireless.Bead
Your tests are flawed!!! you are using a longer delay for CSS. If you make it equal to or less than JS then both will result in identical time.Autoicous
T
754

This is a very interesting question. I've always put my CSS <link href="...">s before my JavaScript <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.js (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 500 ms.
  • Ran the test 20 times in the three major browsers.

Results

First, with the CSS file delayed by 500 ms (the unit is milliseconds):

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

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

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

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

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

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 style sheets 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 style sheet, it would continue parsing the HTML while it fetched the CSS file (in parallel).

Hence, the widely-repeated advice to put style sheets 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)

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; your mileage may vary 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 JavaScript code 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.

File 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);
});

File 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>

File 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>

File test.js

var jsload = Date.now();


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

jQuery was jquery-1.7.1.min.js

Tondatone answered 14/2, 2012 at 6:37 Comment(9)
this is a fantastic answer, thanks for using the science! Per your result "in modern browsers, it looks like linking to CSS first never provides a performance gain", I think the answer to the question title is yes, the old advice of CSS first is clearly invalid.Diaphane
Regarding @josh3736's update about the inverse on mobile... this is a case in point to not to jump the gun on this significant change. I'd be curious how other mobile browser behave (webkit, gecko, presto, trident, etc.) as performance in mobile is often more important.Drawer
You should also try adding some slowness to printing out the css/js, to simulate speeds of a slow server.Catlike
How about if you use defer or async? Does that change? (try with inline scripts and without inline scripts) Does that change?Rubirubia
@pure_code: Probably because it doesn't hurt, and can help in old/mobile browsers.Tondatone
@Tondatone You are messuring the time the site is interactive and styled. But what is important for the perceived speed is the time the page is styled. So putting css at top and javascript botton is still a good idea.Endothecium
@JeffAtwood -- I should think the answer - eventually - concludes or should conclude that it does make sense to load CSS before script. CSS-first allowed and still allows simultaneous script loading. The reverse was not true. Speculative parsing was to make that also true, but in practice does not seem to be used on mobile devices. Despite the fact that in theory it should. And speed of rendering is a much bigger issue on mobile than on regular devices. So: loading CSS first still makes sense. I wonder what josh3736 thinks of this ultrashort summary and conclusion?Concinnous
"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>)." I would highlight that much earlier in the answer, like at the top. Including a script in head that refers to an external file is almost never correct, from almost any perspective (certainly not a performance one). I don't recall ever having had to do it in real life. The odd line or two of inline script maybe, but that's all. The default, without very good contrary reasons, should be the end of the body.Dillondillow
This was a great writeup! As an update for 2020, it looks like this is no longer true: stackoverflow.com/a/63758850Ithyphallic
B
313

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.

Bookman answered 14/2, 2012 at 6:27 Comment(7)
Honored Steve beat me to the answer, but I'll add an article relevant to what he mentions: stevesouders.com/blog/2009/04/27/…Circumpolar
see which browsers support the async attribute, which Steve is recommending here when he says "even better load them asynchronously" -- #1834577Diaphane
Hey can you tell me why anyone would link CSS files with @import directives?Nonattendance
Steve, honored to have an answer from you, in our particular real-world scenario (this very page) its going to take some heavy lifting to move stubs.js and jquery.js into an async or even better load it with a ControlJS style loader. I was trying to determine what is the best thing to do until we get around to that. Meaning we are stuck with CSS and JS in the head, what is the best way to order it?Leporid
What's the source for 2), and if it's true, can you then explain why on occasion a page will finish loading the content, then the CSS is applied a second or two later? (This has happened, rarely, on my own pages where the CSS was in the <head> tags)Cly
So we should put jQuery + jQuery UI + $(document).ready(function () { }); at the end of the page? Will it always work as expected?Bandylegged
Yeah, wouldn't $(document).ready(function () { }); fix that problem of script executing before dom is loaded?Mccalla
L
48

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

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 JavaScript, 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.

Lucilucia answered 14/2, 2012 at 7:55 Comment(2)
How would you go about guaranteeing all the css was loaded before the javascript executes? Can you? or should your javascript be robust enough to deal with the situation where the styles might not necessarily be loaded.Anchoress
@Anchoress If your JS has a dependency, then you should make that dependency explicit. Otherwise, you will always have rare timing issues. ES6 modules are a good way to make that happen, but there are many libraries that could be used as well.Darkling
R
30

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.

Runthrough answered 14/2, 2012 at 3:25 Comment(2)
I appreciate the point you are making in bold, but the OP is talking about what happens when you vary the order with both at the top, neither at the bottom.Ecto
Thus, "assuming [his] test is valid."Koran
I
24

I include CSS files before JavaScript for a different reason.

If my JavaScript code 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.

Islamism answered 14/2, 2012 at 13:31 Comment(7)
this will break one day on some browser. I am not guessing.Brookite
jcolebrand: Yes, I think I hadn't drunk enough coffee when I wrote this. (In retrospect, I guess the important things are just to avoid dynamic loading of CSS and putting the JS inside a domReady event if you need to do dynamic sizing)Islamism
Scripts should should not change any display. That's the CSS job. HTML = content, CSS = How to display content, javascript change content dynamically. Also js should only act after (or while) the DOMContentLoaded is fired with some little but very specific situations.Rubirubia
@brunoais: Some layouts can only be created with Javascript though. For example, anything that needs to be dynamically resized must be made via Javascript and some things (like having a size be 100% - 20px) need Javascript to be done portably in old browsers.Islamism
@missingno In those cases, just use the DOMContentLoaded event, anyway. But I understand what you mean. (Stupid IE!)Rubirubia
If you are implementing an HTML5 app with a number of DIV pages that are initially hidden by CSS using display: none, you must be sure the CSS is loaded before the $(document).ready() jQuery that "shows" the correct page div the user is currently viewing.Valenta
What is "russing"? Do you mean "running"? Or "rushing"? Or something else? If it is "russing", what does it mean?Gaynor
A
12

Updated 2017-12-16

I was not sure about the tests in OP. I decided to experiment a little and ended up busting some of the myths.

Synchronous <script src...> will block downloading of the resources below it until it is downloaded and executed

This is no longer true. Have a look at the waterfall generated by Chrome 63:

<head>
    <script src="//alias-0.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=1"></script>
    <script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=2"></script>
    <script src="//alias-2.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=3"></script>
</head>

Chrome net inspector -> waterfall

<link rel=stylesheet> will not block download and execution of scripts below it

This is incorrect. The style sheet will not block download but it will block execution of the script (little explanation here). Have a look at performance chart generated by Chrome 63:

<link href="//alias-0.redacted.com/payload.php?type=css&amp;delay=666" rel="stylesheet">
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;block=1000"></script>

Chrome dev tools -> performance


Keeping the above in mind, the results in OP can be explained as follows:

CSS First:

CSS Download  500 ms:<------------------------------------------------>
JS Download   400 ms:<-------------------------------------->
JS Execution 1000 ms:                                                  <-------------------------------------------------------------------------------------------------->
DOM Ready   @1500 ms:                                                                                                                                                      ◆

JavaScript First:

JS Download   400 ms:<-------------------------------------->
CSS Download  500 ms:<------------------------------------------------>
JS Execution 1000 ms:                                        <-------------------------------------------------------------------------------------------------->
DOM Ready   @1400 ms:                                                                                                                                            ◆
Autoicous answered 27/4, 2012 at 12:16 Comment(2)
That's also why document.write() is one of the worst ideas ever made for the HTMLDOM.Rubirubia
The reason is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load. javascript.info/… Why doesn't the same assumption applies in case of JS first? Doesn't make much sense to me, order of executed JS doesn't tell anything about it's purpose.Beater
C
12

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 Window: DOMContentLoaded event:

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 style sheets 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 person's 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 prerequisites 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.
Cocytus answered 25/5, 2013 at 20:0 Comment(2)
The linked article no longer claims "Stylesheet loads block script execution". Is that no longer true?Gullah
@Gullah - It's still true. Scripts need to be able to query DOM .style attributes, so stylesheets still block script execution. They might not block script loading, if they're smart, but they will block script.onLoad events.Asparagus
I
7

The 2020 answer: it probably doesn't matter

The best answer here was from 2012, so I decided to test for myself. On Chrome for Android, the JS and CSS resources are downloaded in parallel and I could not detect a difference in page rendering speed.

I included a more detailed writeup on my blog

Ithyphallic answered 5/9, 2020 at 21:54 Comment(0)
P
5

I'm not exactly sure how your testing 'render' time as your using JavaScript. However, consider this:

One page on your site is 50 kB which is not unreasonable. The user is on the East Coast while your server is on the west. MTU is definitely not 10k so there will be a few trips back and forth. It may take 1/2 a second to receive your page and style sheets. Typically (for me) JavaScript (via jQuery plugin and such) are much more than CSS. There’s also what happens when your Internet connection chokes up midway on the page, but let’s ignore that (it happens to me occasionally and I believe the CSS renders, but I am not 100% sure).

Since CSS is in head, there may be additional connections to get it which means it potentially can finish before the page does. Anyway, during the type the remainder of the page takes and the JavaScript files (which is many more bytes) the page is unstyled which makes the site/connection appear slow.

Even if the JavaScript interpreter refuses to start until the CSS is done, the time taken to download the JavaScript code, especially when far from the server, is cutting into CSS time, which will make the site look not pretty.

It’s a small optimization, but that’s the reason for it.

Poteet answered 14/2, 2012 at 22:33 Comment(1)
the server is on the east coast, fwiw. You are also, apparently, unaware of the fact that they now use a CDN.Brookite
I
3

Here is a summary of all the previous major answers:

For modern browsers, put the CSS content wherever you like it. They would analyze your HTML file (which they call speculative parsing) and start downloading CSS in parallel with HTML parsing.

For old browsers, keep putting the CSS on top (if you don't want to show a naked, but interactive page first).

For all browsers, put the JavaScript content as far down on the page as possible, since it will halt parsing of your HTML. Preferably, download it asynchronously (i.e., an Ajax call).

There are also some experimental results for a particular case which claims putting JavaScript first (as opposed to traditional wisdom of putting CSS first) gives better performance, but there isn't any logical reasoning given for it, and lacks validation regarding widespread applicability, so you can ignore it for now.

So, to answer the question: Yes. The recommendation to include the CSS before JavaScript is invalid for the modern browsers. Put CSS wherever you like, and put JavaScript towards the end, as possible.

Inbreathe answered 20/4, 2016 at 22:42 Comment(1)
One use case for placing javascript script in the head tag and even the before the CSS, is dealing with styling that the browser can't know ahead of time, such as recent Dark/Light mode trend in webapps. It should be a small script just for the purpose for reading session and then setting the style(either css variables or other means) based on the user session. This prevents a flash of rendering default style before the script switches to set style.Earthstar
T
1

Steve Souders has already given a definitive answer, but...

I wonder whether there's an issue with both Sam's original test and Josh's repeat of it.

Both tests appear to have been performed on low latency connections where setting up the TCP connection will have a trivial cost.

How this affects the result of the test I'm not sure and I'd want to look at the waterfalls for the tests over a 'normal' latency connection but...

The first file downloaded should get the connection used for the HTML page, and the second file downloaded will get the new connection. (Flushing the <head> early alters that dynamic, but it's not being done here.)

In newer browsers the second TCP connection is opened speculatively so the connection overhead is reduced / goes away. In older browsers this isn't true, and the second connection will have the overhead of being opened.

Quite how/if this affects the outcome of the tests I'm not sure.

Treble answered 12/3, 2012 at 20:46 Comment(4)
not following, unless you have pipelining which is incredibly rare you are very unlikely to get connection setup reduced... agree the test should be repeated on low latencyLeporid
If you look at this waterfall you can see where Chrome speculatively opens up a second connection before it's needed webpagetest.org/result/… (IE9 does the same)... I was thinking normal latency for TCP purposes rather than low - what sort of environment was the test done in?Treble
Re: "Steve Souders has already given a definitive answer but..." The thing with web evolution is that there are no definitive answers. :) There are 3-4 ways to load scripts and things change. The actual correct semantic should actually have been for Steve to say "Put CSS before Synchronous JavaScript" Otherwise people get it wrong by generalizing as it being a rule for all scripts...Turanian
Yes but most people just include scripts synchronously so Steve's advice is good to the uninitiated.Treble
B
1

I think this won’t be true for all the cases. Because the CSS content will download parallel, but JavaScript code can’t. Consider for the same case:

Instead of having a single piece of CSS content, take two or three CSS files and try it out these ways,

  1. CSS..CSS..JavaScript

  2. CSS..JavaScript..CSS

  3. JavaScript..CSS..CSS

I'm sure CSS..CSS..JavaScript will give a better result than all others.

Besought answered 23/8, 2013 at 8:33 Comment(0)
I
0

We have to keep in mind that new browsers have worked on their JavaScript engines, their parsers and so on, optimizing common code and markup problems in a way that problems experienced in ancient browsers such Internet Explorer 8 or before are no longer relevant, not only with regards to markup but also to use of JavaScript variables, element selectors, etc.

I can see in the not-so-distant future a situation where technology has reached a point where performance is not really an issue any more.

Infrastructure answered 14/2, 2012 at 14:29 Comment(1)
Performance is always an issue. I just almost ignore that browsers that do not follow the spec exist. I just prepare my code such that the ones that follow the spec work at full speed and the others i just do such that it works. So, for example, if it just works in IE8, all ok.Rubirubia
S
-5

Personally, I would not place too much emphasis on such "folk wisdom." What may have been true in the past might well not be true now. I would assume that all of the operations relating to a web-page's interpretation and rendering are fully asynchronous ("fetching" something and "acting upon it" are two entirely different things that might be being handled by different threads, etc.), and in any case entirely beyond your control or your concern.

I'd put CSS references in the "head" portion of the document, along with any references to external scripts. (Some scripts may demand to be placed in the body, and if so, oblige them.)

Beyond that ... if you observe that "this seems to be faster/slower than that, on this/that browser," treat this observation as an interesting but irrelevant curiosity and don't let it influence your design decisions. Too many things change too fast. (Anyone want to lay any bets on how many minutes it will be before the Firefox team comes out with yet another interim-release of their product? Yup, me neither.)

Shadchan answered 14/2, 2012 at 4:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.