Should CSS Always Precede JavaScript

Should CSS always precede Javascript?

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

Does Javascript have higher precedence than CSS?

It's not JavaScript that has higher precedence, it's rules directly on the element (in the style attribute). Yes, those have higher precedence than CSS rules from a stylesheet — unless the CSS rules have !important on them (but using !important is a last-ditch thing). (The style rules can override that by using !important.)

The rules are here:

6.4.3 Calculating a selector's specificity

A selector's specificity is calculated as follows:

  • count 1 if the declaration is from is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.)
  • count the number of ID attributes in the selector (= b)
  • count the number of other attributes and pseudo-classes in the selector (= c)
  • count the number of element names and pseudo-elements in the selector (= d)

The specificity is based only on the form of the selector. In particular, a selector of the form "[id=p33]" is counted as an attribute selector (a=0, b=0, c=1, d=0), even if the id attribute is defined as an "ID" in the source document's DTD.

Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity.

Some examples:

*             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */

style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
<HEAD>
<STYLE type="text/css">
#x97z { color: red }
</STYLE>
</HEAD>
<BODY>
<P ID=x97z style="color: green">
</BODY>

In the above example, the color of the P element would be green. The declaration in the "style" attribute will override the one in the STYLE element because of cascading rule 3, since it has a higher specificity.

and here

6.4.4 Precedence of non-CSS presentational hints

The UA may choose to honor presentational attributes in an HTML source document. If so, these attributes are translated to the corresponding CSS rules with specificity equal to 0, and are treated as if they were inserted at the start of the author style sheet. They may therefore be overridden by subsequent style sheet rules. In a transition phase, this policy will make it easier for stylistic attributes to coexist with style sheets.

For HTML, any attribute that is not in the following list should be considered presentational: abbr, accept-charset, accept, accesskey, action, alt, archive, axis, charset, checked, cite, class, classid, code, codebase, codetype, colspan, coords, data, datetime, declare, defer, dir, disabled, enctype, for, headers, href, hreflang, http-equiv, id, ismap, label, lang, language, longdesc, maxlength, media, method, multiple, name, nohref, object, onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onload, onload, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onreset, onselect, onsubmit, onunload, onunload, profile, prompt, readonly, rel, rev, rowspan, scheme, scope, selected, shape, span, src, standby, start, style, summary, title, type (except on LI, OL and UL elements), usemap, value, valuetype, version.

For other languages, all document language-based styling must be translated to the corresponding CSS and either enter the cascade at the user agent level or, as with HTML presentational hints, be treated as author level rules with a specificity of zero placed at the start of the author style sheet.

The following user style sheet would override the font weight of 'b' elements in all documents, and the color of 'font' elements with color attributes in XML documents. It would not affect the color of any 'font' elements with color attributes in HTML documents:

b { font-weight: normal; }
font[color] { color: orange; }

The following, however, would override the color of font elements in all documents:

font[color] { color: orange ! important; }

Should I inline all CSS and JS for a static SPA?

This is actually a really good question.

The only major drawback I can see is caching.

You can't cache your CSS or JavaScript neatly, and you have to somehow invalidate the cache on all of your page (including JS and CSS) on every change to any of them.

I might even take it one step further and inline all your images using Data URIs, the reasoning being that extra HTTP requests are more expensive than loading an additional ~33% of data, thanks to how TCP works (It starts slowly, then exponentially increases download speed until packets begin to get lost), an extra few KBs at the top speed might just be better than a bunch of new requests.

Does JavaScript have an order of precedence like CSS?

You cannot really override anything that a previous script adds to a DOM element.

It will run, and then you just have to do something else to it.

Is ordering of <link rel=stylesheet ...> and <script ...> tags significant?

for performance, CSS first then JS...(but JS yields best performance at the end of the markup as noted in some of the other answers)

stylesheets should always be specified in the head of a document for better performance, it's important, where possible, that any external JS files that must be included in the head (such as those that write to the document) follow the stylesheets, to prevent delays in download time.

quote from Optimize the order of styles and scripts (from Google's "Make the Web Faster" Best Practices docs)

CSS attribute not always being grabbed by javascript correctly

The reason is because the resizeBttn code is firing before the image has actually finished downloading and loading into the DOM. I made these changes in your fiddle:

var imgHeight = $('#mainImg').height();
var currImg = 0;

var imagesSet = ["https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif","https://images.sftcdn.net/images/t_app-logo-xl,f_auto/p/ce2ece60-9b32-11e6-95ab-00163ed833e7/1578981868/the-test-fun-for-friends-logo.png", "https://hiveconnect.co.za/wp-content/uploads/2018/05/800x600.png", "https://upload.wikimedia.org/wikipedia/commons/b/b5/800x600_Wallpaper_Blue_Sky.png"];
var imageLoc = "images/zalman/"


$(document).ready(function() {
resizeBttn();
});

$( window ).resize(function() {
/* imgHeight = $('#mainImg').height() */; // commented out; we do this in resizeBttn now
resizeBttn();
});

$('#bttnLeft').click(function(){
prevImg();
/* imgHeight = $('#mainImg').height() */; // commented out; we do this in resizeBttn now
/* resizeBttn() */; // we do this as an `onload` to the image now
});

$('#bttnRight').click(function(){
nextImg();
/* imgHeight = $('#mainImg').height() */; // commented out; we do this in resizeBttn now
/* resizeBttn() */; // we do this as an `onload` to the image now
});
function nextImg(){
currImg++;
if(currImg>=imagesSet.length){
currImg=0;
}
$('#mainImg').attr("src",imagesSet[currImg]);
}

function prevImg(){
currImg--;
if(currImg<0){
currImg=imagesSet.length-1;
}
$('#mainImg').attr("src",imagesSet[currImg]);
}

function resizeBttn() {
imgHeight = $('#mainImg').height()
// removed superfluous doc.ready
$('#bttnLeft').css("height",imgHeight);
$('#bttnLeft').css("bottom",imgHeight/2-5);
$('#bttnRight').css("height",imgHeight);
$('#bttnRight').css("bottom",imgHeight/2-5);
}

And then rewrote your <img /> tag to call resizeBttn on onload:

<img id="mainImg" src="https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif" onload="resizeBttn()"/>

You can see this in action in this fiddle.

Also, a few additional notes on your code, at a glance:

  • You have some invalid HTML; you're going to want to run that through an HTML validator and fix it, because sometimes it is fine, but sometimes it can lead to all sorts of strange behavior.
  • You're playing fast and l0ose with global variables in your JS that get set in different functions; it might work OK while the script is small, but as things scale it can quickly become difficult to maintain
  • You should really avoid abusing the onclick to get link-like behavior from <li> elements; it can impact SEO as well as accessibility. I'd recommend simply using an anchor element inside or outside the <li>
  • I'd recommend taking a close look at this answer by user camaulay; he makes an excellent point that this may not require JS at all- if a more elegant solution exists w/ CSS it is probably going to be more performant and maintainable.

JavaScript: different behavior if CSS is inline vs inside <head>

As you thought, and as others have said, your javascript doesn't recognize the background to white. This is because element.style is used to get or to set the inline css. You can read more here.

What is usually done instead is to set a class to your elements:

<td class="white" onclick="myScript(this)">2</td>

In your css you would define what your classes do, and you will then use Javascript just to toggle between the classes

Javascript and css in header vs including separate document?

The advantage is mostly modularity. If you use the same JS or CSS in multiple files, it's best to keep them in one place. That way, if you make a change to them, you don't have to update all the files, you just update it in one place.

But if the JS or CSS is specific to a particular file, you might as well put them directly in the file rather than force a separate request.



Related Topics



Leave a reply



Submit