A Detailed Breakdown of the <script> Tag
Specifying the Scripting Language
The “language” Attribute
There is a second attribute, “language”, which can be used to specify the scripting language. Unlike “type”, the possible values for “language” were never standardized. This attribute has been deprecated for a long time now, but some people still use it. It should not be used under any circumstances.
Inline and External Scripts
The <script> tag allows code to either be embedded directly into HTML, or included from external script files. Inline scripts are created by placing code between <script> and </script> tags in an HTML file. The following code shows an example inline <script>.
Inline scripts are a quick and easy way to add code to web pages. However, large scripts can also lead to cluttered HTML files. The alternative approach is to include code in external script files. The external files are referenced via a URL specified by the “src” attribute. The following example shows how external script files are included. In this example, the external script file is named external.js, and is located in the same directory as the HTML file. Also, note that the closing </script> tag is still required.
XHTML rules are much stricter than those of HTML. When special XML characters (such as & and <) are used in scripts in XHTML files, they cause errors. The simplest workaround is to use external script files. However, if you simply must write inline scripts, then you will need to include CDATA (character data) sections in your file. Within CDATA sections the special XML characters can be used freely. The following example uses a CDATA section that is compatible with both XHTML and HTML syntax. Note that XHTML also requires the use of the “type” attribute.
Dynamic Script Tag Injection
Dynamic script tag injection is a technique in which new <script> elements are created at runtime. When the new <script> is added to the page, its “src” URL is automatically downloaded and executed. The following code shows an example dynamic script tag injection. In the example, a new <script> element is created using the document.createElement() function. Next, the “src” attribute is set to the URL of the script file. Finally, the new element is added to the document’s head, causing the script to be downloaded and executed.
var script = document.createElement("script"); script.setAttribute("src", url); document.head.appendChild(script);
The <script> tag is not subject to the same origin policy. Web pages can exploit this freedom to request cross-site data. JSONP, a technique for making cross-site AJAX-like requests, makes extensive use of dynamic script tag injection. Under the JSONP model, each AJAX request is replaced by a script tag injection.
The “async” Attribute
When a <script> tag is encountered, the browser stops what it is doing and begins downloading/executing the script. This default behavior is known as synchronous blocking. During this period of blocking, the page may seem unresponsive or slow to the user. To mitigate this problem, HTML5 introduced the “async” attribute for <script> tags. “async” is a Boolean attribute which, when specified, indicates that a script should unblock the rest of the page, and execute asynchronously. According to the specification, “async” should only be used with external script files. Unfortunately, “async” is not yet supported in Opera. The following example shows how the “async” attribute is used.
<script src="file.js" async="async"></script>
Normally, “async” defaults to false. However, when dealing with dynamically injected scripts, the default value becomes true. This can lead to problems if scripts with interdependencies are inserted at the same time. For example, assume that a page injects a third-party library originating from a slow server. At (almost) the same time, the page also injects a script that makes calls to the library. Since the scripts are asynchronous, the second file could potentially get downloaded and executed before the library is available. The solution is to preserve the program order by setting “async” to false for both scripts. This concept is illustrated in the following example. In the example, the page will wait for the first script to load before moving on to the second script.
var library = document.createElement("script"); var local = document.createElement("script"); library.async = false; local.async = false; library.setAttribute("src", "remote-library-code.js"); local.setAttribute("src", "local-file.js"); document.head.appendChild(library); document.head.appendChild(local); // use the library code
The “defer” Attribute
<!DOCTYPE html> <html lang="en"> <head> <title>defer Example</title> <meta charset="UTF-8" /> <script src="defer.js" defer="defer"></script> </head> <body> <span id="message"></span> </body> </html>
The code for defer.js is shown below. If the “defer” attribute were not present, this code would fail because the DOM is not ready when the <span> element is accessed.
var span = document.getElementById("message"); span.textContent = "The DOM is ready for scripting!";
It is possible to specify both “async” and “defer” on the same <script> element. This is possible so that browsers that do not support “async” can fall back on the “defer” behavior instead of synchronous blocking. According to the specification, “defer” is overridden by “async”. Unfortunately, “defer” is also unsupported in Opera.
All of the examples on this page have shown <script> tags placed in the document’s head. This can actually degrade performance because the rest of the page is blocked while the scripts are processed. Obviously, the “defer” and “async” attributes offer one solution. Unfortunately, they are not supported everywhere yet. A second option is to move <script> tags to the bottom of the <body> tag whenever possible ― this is usually fine as long as the script does not call document.write().
Using external script files instead of inline scripts is another technique that often improves performance. This leads to smaller HTML files, at the expense of creating additional HTTP requests. However, script files are often shared among multiple HTML pages, and can be cached by the browser. The overall result is a smaller HTML download without the additional server requests. Also remember that scripts can be downloaded dynamically after the page is loaded.
Things to Remember
- The <script> tag always requires a matching </script> tag.
- XHTML files should use external script files or CDATA sections to allow special characters.
- Dynamic script tag injections allow cross-site resource requests.
- The “async” attribute causes a script to execute asynchronously from the rest of the page. It can also preserve order for dynamically inserted scripts.
- The “defer” attribute can postpone script execution until the document is ready.
- Move <script> tags to the end of the <body> when possible.
- Use external script files which can be cached and shared among pages.