
« Hadoop creator Doug Cutting speaking at OSCON | Main | AJAX and PHP Action At OSCON »
July 12, 2007
Rule 5 described how stylesheets near the bottom of the page prohibit progressive rendering, and how moving them to the document HEAD eliminates the problem. Scripts (external JavaScript files) pose a similar problem, but the solution is just the opposite: it’s better to move scripts from the top to as low in the page as possible. One reason is to enable progressive rendering, but another is to achieve greater download parallelization.
With stylesheets, progressive rendering is blocked until all stylesheets have been downloaded. That’s why it’s best to move stylesheets to the document HEAD, so they get downloaded first and rendering isn’t blocked. With scripts, progressive rendering is blocked for all content below the script. Moving scripts as low in the page as possible means there's more content above the script that is rendered sooner.
The second problem caused by scripts is blocking parallel downloads. The HTTP/1.1 specification suggests that browsers download no more than two components in parallel per hostname. If you serve your images from multiple hostnames, you can get more than two downloads to occur in parallel. (I've gotten Internet Explorer to download over 100 images in parallel.) While a script is downloading, however, the browser won’t start any other downloads, even on different hostnames.
In some situations it’s not easy to move scripts to the bottom. If, for example, the script uses document.write to insert part of the page’s content, it can’t be moved lower in the page. There might also be scoping issues. In many cases, there are ways to workaround these situations.
An alternative suggestion that often comes up is to use deferred scripts. The DEFER attribute indicates that the script does not contain document.write, and is a clue to browsers that they can continue rendering. Unfortunately, Firefox doesn't support the DEFER attribute. In Internet Explorer, the script may be deferred, but not as much as desired. If a script can be deferred, it can also be moved to the bottom of the page. That will make your web pages load faster.
Steve Souders
[Steve Souders is Yahoo!'s Chief Performance Yahoo!. This is one in a series of Best Practices for Speeding Up Your Web Site. This article is based on Steve's book High Performance Web Sites, published by O'Reilly.]
Posted at July 12, 2007 5:04 PM
How do things like onload and using YUI events to trigger scripts on load affect this?
Posted by: Scott at July 12, 2007 12:16 AM
The onload event won't fire until all scripts are downloaded, so if you move your SCRIPT tag lower in the page, you'll still be guaranteed that it's loaded _before_ the onload event.
Posted by: Steve Souders at July 12, 2007 12:27 AM
Sorry, the comment system stripped out my html in the previous comment. Resending:
Very good to know. Thanks! One more question :) I assume the best place to put the scripts are right before the BODY tag, right? Are there any common reasons (I am sure there are exception cases) where scripts MUST be put in the HEAD section and wouldn't work at the end of the BODY section?
Thanks for this tip. I have been annoyed by some slow load times on some pages and this tip + the tip about HTTP headers and expiry times hs been very useful.
Posted by: Scott at July 12, 2007 12:00 PM
You don't need to put scripts in the HEAD. You can put them right before the close BODY tag.
Posted by: Steve Souders at July 12, 2007 12:33 PM
Great post. Too often web developers neglect simple techniques like this. It's great to see that some don't. Thanks for posting!
Posted by: Stephen at July 12, 2007 8:52 PM
There is a simple solution I can think of that would meet in the middle ground. You could include your javascript in the html document, not as an external dependency. If you have a short javascript function, it would actually load much faster not having to deal with the overhead of requesting a separate download. Even if your js function needs to be in the header, what's 1kB, even to a 56k modem?
Posted by: Chris at July 17, 2007 10:13 PM
What about loading the scripts once the document has loaded ? Some libraries already do this (scriptaculous I think) and I assume it improves loading times because only one small script is loaded, and the remaining javascript files load when the document has finished loading.
Posted by: David at July 21, 2007 5:47 AM
Yes, the "post-onload download" technique is a great idea if the script is not needed for rendering the page. In Rule 8 - Make JavaScript and CSS External I mention combining inlining with post-onload download. This is definitely a great way to speed up your pages.
Posted by: Steve Souders at July 21, 2007 9:29 AM
External scripts will be downloaded before the onLoad event fires, but what about for YUI's own onDOMReady event? I find this event to be much more useful than onLoad.
@Chris: You have to be careful with internal Javascript. Besides losing some of the obvious advantages of external scripts like separated development concerns, reusable code and browser caching, performance for a page can actually turn out worse than before. Just the other day I tried pasting the contents of 5 or 6 CSS and Javascript files into html (using velocity's #include directive), figuring that the page would load faster since it's the same amount of data, but over fewer requests. Instead, the loading time for the html seemed to double what the total loading time was when those files were separate requests. I'm a bit mystified. The only thing I can think of is that Firefox was downloading those in parallel, even though they appeared to be staggered in Firebug. Any thoughts/suggestions?
Posted by: Josh Singer at July 25, 2007 10:29 AM
I am using YUI in a few places of an application I am building. In some cases, I have not been able to put the script references at the bottom of the page. The best example is where I am using the Menu component and am generating my menu from existing markup. If I include the YUI script references at the bottom, there is a (small) period of time where my menu markup renders in HTML before YUI takes over and cleans it up.
Of course, the workaround is to make the YUI menu generate from JavaScript, but I just wanted to point that out.
Outside of the situation where YUI performs actions against existing markup, I am able to put my JS at the bottom.
Posted by: Scott at July 25, 2007 2:58 PM
What about web standards. Don't SCRIPT elements have to appear in the head of a document?
Posted by: Jack at August 2, 2007 8:15 AM
Looks like this point is annoying. I'm overruling the rule 'minalize js', but i use this rule too. When dealing with a total amount of .. 6-7 rules of javascript code, when shifting all scripts to the bottom, i found that scripts that used too be quik, now have some form of LAGG.
Any ideas on this? :P
Posted by: J. de Boer at August 19, 2007 8:53 AM
Yes, this rule is definitely confusing. I get a lot of questions about this one.
Let's first tackle external .js scripts that aren't needed for rendering the page. For example, suppose you have a DHTML feature that is unlikely to be exercised until after the page loads and renders. For these external scripts, post-onload download is the best solution.
If you have JS code that is part of rendering the page, like setting focus or drawing a tree, then I recommend moving the external script in the HTML document to be placed right above these HTML elements. That way, any resources and text above these scripts can be downloaded and rendered without blocking.
If your situation doesn't fit into one of these, it's safest to put them in the HEAD.
Posted by: Steve Souders at August 20, 2007 1:51 PM
I too am concerned about script tags not allowed outside the head.
Posted by: UI Engineer at August 24, 2007 5:50 PM
To the people about script tags outside the head, have a look at the HTML DTD:
http://www.w3.org/TR/1999/REC-html401-19991224/sgml/dtd.html
script is allowed:
* directly inside the body
* directly inside blockquote
* directly inside form
* directly inside head (of course)
* within any inline, which includes: span, sub, sup, %flow, address, a, p, ... the list goes on.
Posted by: Jeremy Dunck at August 28, 2007 9:44 PM
@Jeremy Dunck
And what about XHTML strict?
Posted by: Xscratch at September 5, 2007 6:18 AM
From http://www.quirksmode.org/js/placejs.html:
Generally you place JavaScripts in the of a page. Only when you want to write a message in the page, you'll have to place the script in the correct place in the HTML.
Placing scripts in the makes sure that any functions are loaded before the buttons, links or other things that call them are loaded. If you put your scripts at the very end of a page, it is possible that a user already sees part of the page including a button with a JavaScript function call, while the rest of the page hasn't loaded yet. Result: user pushes button and gets JavaScript Error Message Alerts, because the browser can't find the script (yet).
Unless there's a good reason to do otherwise, place your scripts in the
-----------------------------------------------
I have seen this where users that click on an element that has "onclick" defined before the function has time to load will get errors which end up breaking the functionality completely. If you are going to put js at the end of the file how best do you determine that these user driven events have the code that they need? Perhaps what is needed is a wrapper function which you load in the header which checks to see if the function you really want exists and if not do a setTimeout to try again later. What are people's thoughts on this?
Posted by: James Irwin at October 4, 2007 5:14 PM
In the HTML document define a "stub" function that is tied to the element in the page. That way, even if the external script has not been downloaded, the user won't get any JS errors. Then when the script is downloaded, the new function definition will override the empty one. You can see this in My Yahoo, for example. Notice functions like
function ycsp_do_menu(){return true;}
That are redefined in a later script.
Posted by: Steve Souders at October 8, 2007 10:38 PM
@Xscratch: I think the best is to attach the onclick events using javascript executed on domready. This way the user will find ordinary html elements that doesn't spout out errors before domready has fired.
Posted by: Motin at October 22, 2007 5:15 PM
Steve, your new High Performance book is amazing. Thanks so much for writing it.
As for moving Prototype/Script.aculo.us to the bottom of the page, it's impossible because, for whatever reasons, certain javascript libraries are not being parsed and have to be included before (in the html) any usage of said library is used.
Eg, if I include the libraries at the bottom of the page and I do (function (){Effect.BlindUp($('div_id'));}).delay(5), the browser spits out some type of error regarding that function is unknown.
More specifically: "Error: (function () {Effect.Fade($("error_bulletin"));}).delay is not a function". However, if I move the js libraries back up to the top, it works perfectly fine.
Posted by: Don Wilson at November 6, 2007 1:28 PM
Hi, Don.
Thanks for the feedback on the book. It makes those 10 months of late nights worth it!
Yes, you can't move a script to the bottom if it's symbols are referred to above. You could move the script higher in the page (not great) or find a way to call the desired code _after_ the script is download (eg, onload handler).
Posted by: Steve Souders at November 6, 2007 2:12 PM
Where to place style elements?
I'm surprised that anyone has to recommend putting style sheets in the head - that is the *only* place they are allowed in valid HTML.
Posted by: RobG at November 13, 2007 9:19 PM
While I didn't notice any performance gain by moving scripts to the bottom of the document (may be because i'm on localhost), I did notice an issue with this method. I use ondomready to add a few classes to the body such as "jsEnabled browser-(ie|gecko|webkit)" and so on. I have different CSS rules for the different classes, i'm sure you guys know about this technique. Anyways, on IE, when the page gets loaded, there's a "style change flicker" as the new style takes effects after my code added the class names to the body. For example, if I have the following css rules:
/* default width */
#container {
width: 100px;
}
/* say you want to increase the width on IE */
body.browser-ie #container {
width: 200px;
}
when the page gets loaded, I notice that the #container element quickly changes its width. This is on localhost and using domready so there isn't any other latency factors. This is only noticeable on IE(7). Anyone else notice this?
Posted by: Tuan at November 15, 2007 1:05 AM
Just regarding the Web Standards and script lement positioning in XHTML documents, script elements are allowed in either the Head OR the Body, as you like, according to this reference:
http://www.w3.org/TR/2003/WD-xhtml2-20030506/mod-scripting.html#edef_scripting_script
Posted by: Phil GRAHAM at November 17, 2007 7:27 AM
I did a source lookup on this page and found a script tag between and . That seems to be a really weird place to place a script tag.
It will be nice if there is any recommendation on where exactly to place the script, in a cross browser safe, standards compliant way.
On this note I would also like to thank the Yslow team for having built such an awesome tool - I use it all the time!
Posted by: Ajay at December 17, 2007 9:35 AM
There are some cases where you wouldn't want your JS at the bottom. For example, if you were using an "ondomready" event that many of the js libraries supply, you would want the code to execute towards the top. If you were to move all your JS to the bottom of the page, the event would be rendered useless.
However, it's nice to know that having it at the top is a performance hit so that I can weigh the positives with the negatives before moving forward.
Posted by: rob at February 6, 2008 7:56 AM
The yahoo mail Beta is heavy on javascript, and they don't go by this rule. I suppose they should..
Posted by: alex at February 6, 2008 11:11 AM
Comment Policy: We encourage comments and look forward to hearing from you. Please note that Yahoo! may, in our sole discretion, remove comments if they are off topic, inappropriate, or otherwise violate our Terms of Service.
Copyright © 2008 Yahoo! Inc. All rights reserved.
Privacy Policy - Terms of Service - Copyright Policy - Job Openings