Javascript Load Order with Defer Attribute
HTML rendering and Javascript are sometimes mystery to me, and usually I can only understand how something works by experimenting. The implementation differs quite a bit between MSIE, Mozilla, Netscape 4 and other commonly used browsers as well, and it can be quite frustrating sometimes. And when the outcome of a script differs from its specification, it then really annoys me.
I was playing with some Javascript loading order test this morning, and here’s a simple page that I used for the test.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script type="text/javascript">
function handleOnLoad() {
alert('On load');
}
alert('Head');
</script>
</head>
<body onload="handleOnLoad()">
<script type="text/javascript">
alert('Body 1');
</script>
<script type="text/javascript" defer="true">
alert('Body 2');
</script>
<script type="text/javascript">
alert('Body 3');
</script>
<h1>Javascript Test.</h1>
</body>
</html>
The order of alter() triggered is:
- Head
- Body 1
- Body 2
- Body 3
- On load
Well. There is nothing surprising, except for “Body 2″ that is executed and triggered between “Body 1″ and “Body 3″, even though defer="true" attribute has been set. According to some Javascript FAQ and W3C’s own HTML 4 reference, defer="true" attribute is designed to execute scripts asynchronously, i.e. after the document has been parsed. Therefore I was expecting “Body 2″ to be displayed in between “Body 3″ and “On load”, or even after “On load” as it might be appended to the end of the message queue. But obviously the experiment says it is not, and I think I might need to go back fixing some of my other scripts as I always assume so. I guess according to the reference, defer="true" only provides as a hint that it is not going to affect the document structure, but the browser implementation does not need to accept the hint.
By the way, it is tested on both MSIE 6SP1 and Mozilla 1.4, and in the case of MSIE, the alert dialog box of “Body 2″ is actually around 50 pixels lower and further to the right, comparing with other alerts triggered. On Mozilla 1.4, there is no difference.
Updated at 11:58am: The example above does generate a different result if the deferred Javascript is fetched from else where…
Here is some changes to the HTML document:
<script type="text/javascript">
alert('Body 1');
</script>
<script type="text/javascript" defer="true" src="test.js">
</script>
<script type="text/javascript">
alert('Body 3');
</script>
Instead of doing an alert() to print out “Body 2″, it loads an external Javascript file named “test.js”. And the content of that file is simply…
alert("Body 2");
Now, Mozilla 1.4 will continue to print out “Body 1″, “Body 2″ and “Body 3″ in order, but MSIE 6SP1 will honour the defer="true" attribute and print out “Body 1″, “Body 3″ and “Body 2″, and then finally “On Load”. If defer="true" attribute is not specified, then both Mozilla and MSIE will bring out the alerts in the order they are in the document.
I think MSIE’s behaviour is actually correct by asynchronously deferring operations that might be blocked on IO. I guess it makes more sense to Win32 developers as they are used to event driven networking programming. That also implies that MSIE might finish parsing the main document faster than Mozilla if there are a lot of external scripts linked over slow network. Obviously, parsing HTML document and displaying them involves much more than the network programming model, but here’s just a little thought on the implication.
Back to more coding…
Comments
More proper? How do you figure? The defer attribute looks for a boolean; you know, yes/no, true/false, 1/0. You do understand that concept right?
Here’s some W3C for you, Mozilla boy.
defer [CI]
When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no “document.write” in javascript) and thus, the user agent can continue parsing and rendering.
actually, defer=”true” is quite wrong, as stated in the DTD and by the validator:
HTML 4.01 DTD:
<!ATTLIST SCRIPT
charset %Charset; #IMPLIED -- char encoding of linked resource --
type %ContentType; #REQUIRED -- content type of script language --
src %URI; #IMPLIED -- URI for an external script --
defer (defer) #IMPLIED -- UA may defer execution of script --
event CDATA #IMPLIED -- reserved for possible future use --
for %URI; #IMPLIED -- reserved for possible future use --
>
HTML validator output:
Line 15, column 48: value of attribute “DEFER” cannot be “TRUE”; must be one of “DEFER” (explain…).
<script type="text/javascript" defer="true">
That said, the HTML 4.01 standard does not explicitly state a behavior, and does not require the user agent to recognize the attribute:
“When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no “document.write” in javascript) and thus, the user agent can continue parsing and rendering.”
This is a poor specification, because an ambigious load order will cause problems if there are interdependencies between the script elements.
> More proper? How do you figure? The defer attribute
> looks for a boolean; you know, yes/no, true/false,
> 1/0. You do understand that concept right?
Great to see a complete idiot trying to talk smart and then find a correction right below it. The boolean operation is evaluated on the presence of a defer-attribute, meaning you can pretty much put anything as a value. It’s not evaluated by the string (“true”) in your value, since that is a STRING.
The fact that you use a value at all is that XHTML does not allow properties without values on elements, same thing with and so on.
YOU do understand the concept, right?
Great Discovery man…
i got to know that IE defers javascript only when the script is externaly linked
good work
Actually the correct procedure is defer=”defer” and mozilla based browsers will not accept this (and/or just ignore it) but in IE this JS attribute will execute the javascript INLINE or EXTERNAL only AFTER the page finisged loading.
Some links regarding this matter:
https://bugzilla.mozilla.org/show_bug.cgi?id=28293
http://www.hunlock.com/blogs/Deferred_Javascript
Add a comment
Gravatar is used. Email address is required but will not be displayed. Please keep your comment on topic. No spamming and/or bad language. First time poster will be moderated. Scott reserves the right to delete/edit your comments.

Mozilla might behave better if you were to use the more proper defer=”defer” rather than defer=”true”.