Tuesday, May 29, 2012

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.




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


Source: Tips4all

No comments:

Post a Comment