<? Header("content-type: application/x-javascript"); sleep(2); // Setting Var for Safari 2 to check when script is loaded ??> var d1 = true;
This is the most common way of including Javascript files in a page:
<head> <title>Loading scripts in header normally</title> <script type="text/javascript"> var start= new Date(); </script> <script type="text/javascript" src="delay1.php"></script> <script type="text/javascript" src="delay2.php"></script> <script type="text/javascript" src="delay3.php"></script> <script type="text/javascript" src="delay4.php"></script> <script type="text/javascript" src="delay5.php"></script> <script type="text/javascript"> var end= new Date(); alert(((end - start) / 1000) + 'seconds'); </script> </head>
The file can be viewed and test here. As Stoyan points out loading scripts in the header blocks everything else on the page, including other scripts. So until all the scripts have loaded one by one nothing shows up on the page. In this case the page takes over 10 seconds to load and is blank that entire time.
The advantages of this method is you know the Javascripts will be loaded and ready so they can be referenced in the page, which if inline scripting is used (which it shouldn’t be without a specific reason) it can be required. The disadvantage is that a user is at best just staring at a blank screen while the Javascript is loading, and at worse has already left the site.
This is the next most common method of including Javascript, putting it at the bottom of the page:
<script type="text/javascript" src="delay1.php"></script> <script type="text/javascript" src="delay2.php"></script> <script type="text/javascript" src="delay3.php"></script> <script type="text/javascript" src="delay4.php"></script> <script type="text/javascript" src="delay5.php"></script> <script type="text/javascript"> var end= new Date(); alert(((end - start) / 1000) + 'seconds'); </script>
The file can be viewed and tested here. The Javascript here still blocks everything and loads the scripts one by one, but in this case at least the user can at least see the content of the page while the scripts are loading. This method still takes over 10 seconds to load but at least the page isn’t blank.
The advantages of this method is that the the page doesn’t appear blank while the Javascript is loading, and as the Javascript is loaded before the end of the page events such as the window load can still be used to initiate the scripts. The disadvantage is that no time was shaved off the download time as the scripts are still download one by one while blocking all the other elements.
This method uses the YUI Get utility to dynamically download the scripts:
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.5.2/build/yahoo/yahoo-min.js&2.5.2/build/get/get-min.js"></script> <script type="text/javascript"> var js_urls = [ "delay1.php", "delay2.php", "delay3.php", "delay4.php", "delay5.php" ]; YAHOO.util.Get.script(js_urls, { onSuccess: function() { var end= new Date(); alert(((end - start) / 1000) + 'seconds'); } }); </script>
The file can be viewed and tested here. This Javascript no longer blocks everything but the YUI Get utility still loads the scripts one by one. As we now need to download the YUI YAHOO and Get scripts as well this page actually takes the longest of the test pages to load. On a real page where there are images and other items to be loaded this should speed things up as it removes the blocking issue. This method takes over 11 seconds to load due to the additional script files required, but on an actual page it would be more likely to speed the page loading up.
The advantages of this method is that the scripts are no longer blocking so other elements such as images can load at the same time (which wasn’t reflected in this test). The disadvantages are the scripts are still downloaded one by one so there is no time savings there. The other disadvantage is that the scripts may not be loaded when events such as the window load or the Dom Ready events fire, so the scripts can’t be set to start on those events.
So far this post has matched pretty closely with Stoyan’s, but from the Google I/O conference I remembered that scripts loaded dynamically could be downloaded simultaneously. It turns out that the YUI Get method is the item limiting the scripts downloading to be one after the other. This method uses multiple YUI Gets to download multiple files at once:
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.5.2/build/yahoo/yahoo-min.js&2.5.2/build/get/get-min.js"></script> <script type="text/javascript"> if (typeof JP == "undefined" || !JP) { var JP = {};} JP.Loader = function (scripts, config) { if ( this instanceof JP.Loader ) { this.config = config || {}; this.scriptsLoaded = ''; var loaded = function(o) { this.scripts_loaded++; if (this.scripts_loaded == this.len) { if (this.config.onSuccess != 'undefined' && this.config.onSuccess) { var sc=this.config.scope || window; this.config.onSuccess.call(sc); } } }; this.scripts_loaded = 0; this.len=scripts.length; var include_varName = false; var varName = this.config.varName || []; if (varName.length = this.len) { include_varName = true; } for ( var i=0; i<this.len; ++i ){ if (include_varName) { YAHOO.util.Get.script(scripts[i], { onSuccess: loaded, scope: this, varName:[varName[i]] }); } else { YAHOO.util.Get.script(scripts[i], { onSuccess: loaded, scope: this }); } } } else { return new JP.Loader(scripts, loaded_func); } }; test_obj = { all_done: function(){ var end = new Date(); alert(((end - start) / 1000) + 'seconds'); } }; var js_urls = [ "delay1.php", "delay2.php", "delay3.php", "delay4.php", "delay5.php" ]; var var_checks = [ "d1", "d2", "d3", "d4", "d5" ]; var js_scripts = new JP.Loader(js_urls, {onSuccess: test_obj.all_done, scope: test_obj, varName: var_checks}); </script>
The file can be viewed and tested here. This Javascript no longer blocks and multiple scripts are downloaded at a single time. Of course the Javascript to load the actual Javascript is now more complex. This method takes between 2.5 and 8.5 seconds depending on the browser to load script files.
The advantages of this method is that the scripts are no longer blocking and are loaded simultaneously dramatically decreasing download time. The disadvantages are there is no guarentee what order the scripts will finish downloading so none of the scripts can require another script until after they’ve all finished loading. The other disadvantage is the same as above that the scripts may not be initialized when events such as the window load or the Dom Ready events fire, as they may not be done loading yet.
| Firefox | Internet Explorer | Safari | Opera | |||
|---|---|---|---|---|---|---|
| 2 | 3 | 6 | 7 | 3 | 9.5 | |
| Javascript in the Header | 10.5 | 10.6 | 10.7 | 10.5 | 10.7 | 10.7 |
| Javascript in the Footer | 10.5 | 10.5 | 10.8 | 10.5 | 10.7 | 10.7 |
| Including with YUI Get | 10.8 | 11.5 | 10.9 | 11.4 | 10.9 | 2.8 |
| Including with Multiple YUI Gets | 6.6 | 2.5 | 6.9 | 6.4 | 4.6 | 4.6 |
Using the Multiple YUI Gets is the fastest across the board, except for Opera in which the single YUI Get call is the fastest. I’ll have to do more research to figure out what’s happening there, but it would appear Opera downloads the single Get method simultaneously as well. Internet Explorer and Firefox 2 are a bit slower as they enforce the limit of only 2 items per domain being able to be downloaded at once. On those browsers the scripts are downloading simultaneously but only two at a time, if the scripts were hosted on different domains then it could further speed up the download for IE and Firefox 2.
Which method should be used? It depends. One thing I can say for certain is I wouldn’t use the Javascript in the Header method unless there is a specific reason it absolutely needs to be there. Unless the are a lot of scripts and or large scripts I would tend to use the Javascript in the Footer method as it’s backwards compatible and the user isn’t waiting with a blank page. It’s also straight forward and easy to use. If there are a lot of scripts or page load speed is essential then I’d use the Multiple YUI Gets method.
In the end it really comes down to testing the different methods out on the specific pages and sites and see how much of a difference each makes.
YUI recently came out with their combined file functionality, which I’m very happy to see. Looking at the chart above I’m curious if it would be faster to include a bunch of individual files at once instead of one large file…
In the Multiple YUI Gets file I load these files (the YAHOO, and Get are already loaded):
http://yui.yahooapis.com/2.5.2/build/utilities/utilities.js
http://yui.yahooapis.com/2.5.2/build/autocomplete/autocomplete-min.js
http://yui.yahooapis.com/2.5.2/build/container/container-min.js
http://yui.yahooapis.com/2.5.2/build/menu/menu-min.js
http://yui.yahooapis.com/2.5.2/build/button/button-min.js
http://yui.yahooapis.com/2.5.2/build/history/history-min.js
http://yui.yahooapis.com/2.5.2/build/json/json-min.js
http://yui.yahooapis.com/2.5.2/build/resize/resize-beta-min.js
http://yui.yahooapis.com/2.5.2/build/tabview/tabview-min.js
For the combined file I load the same files as above but using YUI’s combo file and include it using the Footer method:
http://yui.yahooapis.com/combo?2.5.2/build/utilities/utilities.js&2.5.2/build/autocomplete/autocomplete-min.js&2.5.2/build/container/container-min.js&2.5.2/build/menu/menu-min.js&2.5.2/build/button/button-min.js&2.5.2/build/history/history-min.js&2.5.2/build/json/json-min.js&2.5.2/build/resize/resize-beta-min.js&2.5.2/build/tabview/tabview-min.js
The conclusion is inconclusive. They are both pretty close in the download time with the combined being quicker sometimes and the individual files being quicker at others. It did seem that often the initial load the individual files were quicker, but then on refreshes the load times were neck and neck or the combined file was faster. As those files are set to be cached on YAHOO’s servers (even though I have my browsers set not to cache) it could be affecting it. Either way this will need more testing to determine if there is a noticable difference in the two methods.