JavaScript: Defer Execution

First introduced by Internet Explorer 4, the defer attribute of the script element is now part of the HTML 4 and XHTML specifications. The defer attribute gives a hint to the browser that the script does not create any content so the browser can optionally defer interpreting the script. This can improve performance by delaying execution of scripts until after the body content is parsed and rendered.

Here's the brief paragraph describing the defer attribute from the HTML 4.01 specification:

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.

Defer Loading or Execution?

A frequent question online is if the defer attribute delays the loading of an external script until after the body content is parsed. In our tests IE4+ deferred the execution of deferred scripts, not their loading. IE4+ loaded external files sequentially, regardless of the defer attribute value. However, the execution order of scripts can be changed by the defer attribute. The HTML 4.01 DTD gives us a hint here saying that the "UA may defer execution of script."

The Bugzilla bug report cited below gives us another hint on how IE works when processing scripts. It appears that IE executes scripts in the following order:

All non-deferred scripts in order of occurrence

Deferred inline scripts in order of occurrence

Deferred external scripts in order of occurrence

So it appears that deferring an external script, regardless of where it's included, causes it to be executed after all inline scripts, deferred or not. This, it turns out, is the behavior that we observed. The scripts in our test page have the following order:

In the case of IE 6/Win98 and IE 6/Win2K, the defer attribute did have an effect on the execution order of the scripts. The first time we ran our test page on Internet Explorer 6 for Windows 2000 the scripts executed in the following order (see Figure 1):

Or does it? When I had Porter Glendenning test our same test page from a remote location, the "Inline Head Deferred" executed later in the execution sequence (see Figure 2).

Figure 2: Internet Explorer 6 Windows 2000 can also execute deferred inline head scripts later in the execution sequence

In IE6, deferred inline scripts in the head of XHTML documents exhibited inconsistent execution order. They either executed at the end of the head sequence, or just after the body script executed. The non-deferred scripts are executed in their order of occurrence, but deferred inline scripts execute after any non-deferred scripts within the head or body sections of the page. Deferred external scripts execute after the head and body scripts, deferred or otherwise.

So to defer scripts to the very end of the execution order you can defer external scripts. In this case the defer attribute delays script execution until all other scripts have been executed, in all versions of IE/Windows that we tested.

Firebird, Opera, and IE Mac and the defer attribute

Firebird 0.7 and Opera 7.11 (Win2K) and IE 5.1.7 Mac all executed the scripts in the order of occurrence (see Figures 3 and 4):

Note that in Firefox, deferred scripts block rendering and parallel downloads, so use the defer attribute with caution. Our you could defer scripts only for IE using browser sniffing, or place them at the end of the body element to enable progressive rendering.

What about HTTP Compression and Deferred JavaScripts?

You can delay the execution of external scripts by placing them just before the end of the closing body tag. However, for older browsers compressed external JavaScripts are not reliably decompressed when they are placed within the body section, only when they are placed within the head are they reliably decompressed. So the defer attribute allows you to defer the execution of compressed JavaScripts, when they are placed within the head of your XHTML documents.

This requirement to place compressed JavaScripts within the head section is a legacy behavior from Netscape's original specification for JavaScript 1.1. This specification implied that external scripts should appear within the head section of HTML documents. Scripts placed within the body section are not pre-processed, and cannot be compressed. So the defer attribute gives another performance option for webmasters who elect to compress their external scripts (see Speed Up Your Site, Chapter 18: "Compressing the Web," for more details).

Conclusion

The defer attribute gives authors another performance option to speed up the display of their web pages. Deferred scripts are interpreted later in the page execution cycle, allowing non-deferred scripts and body content to be parsed and displayed first. Deferring inline body and external head scripts gave consistent results in our tests. Deferring inline head scripts gave us inconsistent results and should be avoided unless the execution sequence is taken into account. While the defer attribute is only currently supported by Internet Explorer 4+ for Windows, this standards-based technique can help speed up your site for the majority of users today.

This test page builds a compound alert box using deferred and non-deferred inline and external scripts throughout the page to show how different browsers change the execution order based on the defer attribute.