Loading and Execution Back
Most browsers use a single process for both user interface (UI) updates and JavaScript execution, so only one can happen at any given moment in time. If we loaded JavaScript between a document, no matter using the closing tag or src
attribute, the browser has to block the user interaction to load and execute the script.
1. Script Positioning
Traditionally, <script>
tags that are used to load external JavaScript files have appeared in the <head>
, along with <link>
tags to load external CSS files and other metainformation(元信息s) about the page.
The theory was that it's best to keep as many styles and behaviour dependencies together, loading them first so that the page will come in looking and behaving correctly.
<html>
<head>
<title>Script Example</title>
<!-- Example of inefficient script positioning -->
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
<script type="text/javascript" src="file3.js"></script>
<link rel="stylesheet" type="text/css" href="style.css"></link>
</head>
</html>
Because scripts block downloading of all resource types on the page, it's recommended to place all <script>
tags as close to the bottom of the <body>
tag as possible so as not to affect the download of the entire page.
<html>
<head>
<title>Script Example</title>
<link rel="stylesheet" type="text/css" href="style.css"></link>
</head>
<body>
<p>Hello World!</p>
<!-- Example of recommended script positioning -->
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
<script type="text/javascript" src="file3.js"></script>
</body>
</html>
2. Grouping Scripts
Since each <script>
tag blocks the page from rendering during the initial download, it's helpful to limit the total number of <script>
tags contained in the page. This applies to both inline scripts as well as those in external files. That's to reduce the delay while the code is executed.
Never putting an inline script after a
<link>
tag, because Steve Souders has found that an inline script placed after a<link>
tag referencing an external stylesheet caused the browser to block while waiting for the stylesheet to download.
Therefore, if a large website or web application has several required JavaScript files, you can use a build tool to concatenate these files together into a single file and import it.
Yahoo! User Interface (YUI) has given you a way to pull in any number of JavaScript files by using a combo-handled URL like http://yui.yahooapis.com/combo?2.7.0/build/yahoo/yahoo-min.js&2.7.0/build/event/event-min.js
3. Nonblocking Scripts
The secret to nonblocking scripts is to load the JavaScript source code after the page has finished loading. In technical terms, this means downloading the code after the load
event of the window
object has been triggered.
3.1 Deferred Scripts
HTML 4 defines an additional attribute for the <script>
tag called defer
. The defer
attribute indicates that the script contained within the element is not going to modify the DOM and therefore execution can be safely deferred until a later point in time.
<script type="text/javascript" src="file1.js" defer>
A <script>
tag with defer
may be placed anywhere in the document. The JavaScript file will begin downloading at the point that the <script>
tag is parsed, but the code will not be executed until the DOM has been completely loaded. (before the onload
event handler is called).
<html>
<head>
<title>Script Defer Example</title>
</head>
<body>
<script defer>
alert("defer");
</script>
<script>
alert("script");
</script>
<script>
window.onload = function () {
alert("load");
};
</script>
</body>
</html>
Here is an example is shown above. In browsers that don't support defer
, the order of the alerts is "defer", "script", and "load", while in browsers that support defer
, the order of the alerts is "script", "defer", and "load".
3.2 Dynamic Script Elements
At the root of a document, the <script>
element isn't any different than any other element on a page: references can be retrieved through the DOM, and they can be moved, removed from the document, and even created. That's why we can easily using standard DOM methods to create a <script>
element.
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'file1.js';
document.getElementsByTagName('head')[0].appendChild(script);
It's generally safer to add new
<script>
nodes to the<head>
element instead of the<body>
, especially if this code is executing during page load. That's because Internet Explorer may experience an "operation aborted" error if all of the<body>
contents have not yet been loaded.
Because there will be problems if the code contains only interfaces to be used by other scripts, we need to track when the code has been fully downloaded and ready for use. In Firefox, Opera, Chrome and Safari 3+, we can use load
event to check this:
var script = document.createElement('script');
script.type = 'text/javascript';
/** Firefox, Opera, Chrome, Safari 3+ */
script.onload = function () {
alert('Script Loaded!');
};
script.src = 'file1.js';
document.getElementsByTagName('head')[0].appendChild(script);
While in Internet Explorer, we can use readystatechange
event to check the property readyState
of a <script>
element. There're 6 possible values for readyState
:
- "uninitialized": The default state
- "loading": Download has begun
- "loaded": Download has completed
- "interactive": Data is completely downloaded but is not fully available
- "complete": All data is ready to be used
As Internet Explorer is inconsistent with which of those values "loaded" and "complete" indicates the final state, the safest way to use this event is to check both of them, and to remove the event handler when either one occurs (to ensure that the event isn't handled twice):
var script = document.createElement('script');
script.type = 'text/javascript';
/** Internet Explorer */
script.onreadystatechange = function () {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
alert('Script Loaded!');
}
};
script.src = 'file1.js';
document.getElementsByTagName('head')[0].appendChild(script);
Finally, if we encapsulate both of them:
function loadScript(url, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
if (script.readyState) {
/** Internet Explorer */
script.onreadystatechange = function () {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
So in the case of loading scripts in an order, you can use this function like this:
loadScript('file1.js', function () {
loadScript('file2.js', function () {
loadScript('file3.js', null);
});
});
3.3 XMLHttpRequest Script Injection
Using an XMLHttpRequest(XHR) object is another way to retrieve code from a JavaScript file. Then, we can use the text
property of a script tag element to inject script into a page:
var xhr = new XMLHttpRequest();
xhr.open('get', 'file1.js', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
var script = document.createElement('script');
script.type = 'text/javascript';
scirpt.text = xhr.responseText;
document.getElementsByTagName('head')[0].appendChild(script);
}
}
};
xhr.send(null);
The advantage of this approach is that:
- You can download the JavaScript code without executing it immediately
- Allow you to defer its execution until you're ready
- Work in all modern browsers without exception cases
The disadvantage is that:
- Due to same-origin policy, only JavaScript files located on the same domain can be loaded (CDN is not allowed)
3.4 The LazyLoad library
The lazyLoad library (available at http://github.com/rgrove/lazyload/) is a more powerful function of loadScript()
, which can detect the correct order of loading multiple JavaScript files:
<script type="text/javascript" src="lazyload-min.js"></script>
<script type="text/javascript">
LazyLoad.js(['first.js', 'rest.js'], function () {
Application.init();
});
</script>
4. Summary
Because of the blocking problem of JavaScript loading, there're some ways to minimize the performance impact of JavaScript:
- Put all
<script>
tags at the bottom of the page, just inside of the closing</body>
. - Group Scripts together.
- Using several ways to download JavaScript in a non-blocking fashion:
defer
attribute- Dynamically create
<script>
element - Use
XMLHttpRequest
Object to download JavaScript files and inject code into the page
As the plugin is integrated with a code management system like GitLab or GitHub, you may have to auth with your account before leaving comments around this article.
Notice: This plugin has used Cookie to store your token with an expiration.