How exactly does script defer=defer work?
UPDATED: 2/19/2016
Consider this answer outdated. Refer to other answers on this post for information relevant to newer browser version.
Basically, defer tells the browser to wait "until it's ready" before executing the javascript in that script block. Usually this is after the DOM has finished loading and document.readyState == 4
The defer attribute is specific to internet explorer. In Internet Explorer 8, on Windows 7 the result I am seeing in your JS Fiddle test page is, 1 - 2 - 3.
The results may vary from browser to browser.
http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx
Contrary to popular belief IE follows standards more often than people let on, in actuality the "defer" attribute is defined in the DOM Level 1 spec http://www.w3.org/TR/REC-DOM-Level-1/level-one-html.html
The W3C's definition of defer: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer:
"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."
For what is defer=defer in js?
It causes the browser to defer parsing of the script (not necessarily delay loading).
Normally when the browser encounters a script tag, it pauses any further procesing of the HTML until the script has been downloaded, fetched, parsed (then compiled on most browsers) and executed. This is necessary to allow the javascript to inject html via document.write().
However this often means that the user is left looking at a blank screen for a long time.
With the defer tag, the script is not parsed/compiled until the HTML is fully loaded. If you have multiple script tags with the defer atrtribute, then the order in which they are parsed is maintained.
This is all extensively documented on the internet. The w3c pages are a good place to start (but can be a bit terse).
Script Tag - async & defer
Keep your scripts right before </body>
. Async can be used with scripts located there in a few circumstances (see discussion below). Defer won't make much of a difference for scripts located there because the DOM parsing work has pretty much already been done anyway.
Here's an article that explains the difference between async and defer: http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/.
Your HTML will display quicker in older browsers if you keep the scripts at the end of the body right before </body>
. So, to preserve the load speed in older browsers, you don't want to put them anywhere else.
If your second script depends upon the first script (e.g. your second script uses the jQuery loaded in the first script), then you can't make them async without additional code to control execution order, but you can make them defer because defer scripts will still be executed in order, just not until after the document has been parsed. If you have that code and you don't need the scripts to run right away, you can make them async or defer.
You could put the scripts in the <head>
tag and set them to defer
and the loading of the scripts will be deferred until the DOM has been parsed and that will get fast page display in new browsers that support defer, but it won't help you at all in older browsers and it isn't really any faster than just putting the scripts right before </body>
which works in all browsers. So, you can see why it's just best to put them right before </body>
.
Async is more useful when you really don't care when the script loads and nothing else that is user dependent depends upon that script loading. The most often cited example for using async is an analytics script like Google Analytics that you don't want anything to wait for and it's not urgent to run soon and it stands alone so nothing else depends upon it.
Usually the jQuery library is not a good candidate for async because other scripts depend upon it and you want to install event handlers so your page can start responding to user events and you may need to run some jQuery-based initialization code to establish the initial state of the page. It can be used async, but other scripts will have to be coded to not execute until jQuery is loaded.
Script defer doesn't seem to work as expected
defer
only works on external scripts:
This attribute must not be used if the
src
attribute is absent (i.e. for inline scripts), in this case it would have no effect.To achieve a similar effect for dynamically inserted scripts use
async=false
instead. Scripts with the defer attribute will execute in the order in which they appear in the document.
Also,
Scripts without
async
ordefer
attributes, as well as inline scripts, are fetched and executed immediately, before the browser continues to parse the page.
Because local scripts are executed before the page finishes parsing, defer
will not apply. defer
gets applied after parsing, but before DomContentLoaded.
wordpress script_loader_tag in function.php
Replace
$scripts_to_defer = array('jquery-core-js','fortuna.lib-js');
With this
$scripts_to_defer = array( 'jquery-core', 'fortuna.lib' );
You don't need to add -js
to handle because WordPress automatically adds when enqueue scripts Try the below code.
function add_defer_attribute( $tag, $handle ) {
// add script handles to the array below
$scripts_to_defer = array( 'jquery-core','fortuna.lib' );
foreach( $scripts_to_defer as $defer_script ) {
if ($defer_script === $handle) {
return str_replace( ' src', ' defer="defer" src', $tag );
}
}
return $tag;
}
add_filter( 'script_loader_tag', 'add_defer_attribute', 10, 2 );
function add_async_attribute( $tag, $handle ) {
// add script handles to the array below
$scripts_to_async = array( 'jquery-core', 'fortuna.lib' );
foreach( $scripts_to_async as $async_script ) {
if ($async_script === $handle) {
return str_replace( ' src', ' async="async" src', $tag );
}
}
return $tag;
}
add_filter( 'script_loader_tag', 'add_async_attribute', 10, 2 );
Tested and working fine.
Can you use both the async and defer attributes on a HTML tag?
From the specification: https://www.w3.org/TR/2011/WD-html5-20110525/scripting-1.html#attr-script-async
The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the synchronous blocking behavior that is the default.
(Check the reference link below to see a visual representation of the differences between normal scripts and scripts with defer and async)
References:
- async vs defer attributes
- Efficiently load JavaScript with defer and async
document.createElement('script') vs script src=
The <script src=...>
blocks the browser while document.createElement('script')
loads the JavaScript asynchronously; this is the primary reason.
The <script src=...>
tag blocks browser from displaying rest of the page until the script is loaded and executed. This ensures that scripts are executed in correct order and any document.write()
in that script work as expected. However this creates a laggy browsing experience for the user.
When the script is loaded asynchronously, the browser can download the script without blocking the page display. This improves the browsing experience dramatically.
To load the scripts asynchronously one can use HTML markup:
<script src="..." async defer></script>
The async
attribute was introduced in HTML5 while the defer
attribute can be added as a fallback for older versions of IE. This document describes how async and defer attribute work.
Alternately, one can use JavaScript to build a script tag:
var s = document.createElement('script');
s.src = "...";
document.getElementsByTagName("head")[0].appendChild(s);
JavaScript generated script tags work in most browsers even if they do not understand the async
attribute or .async = true
property.
About schemeless URIs (//example.com/script.js
): schemeless URIs seem to work almost everywhere (see this question).
About the Google Analytics example: both old and new code use JavaScript to detect the protocol then load http://www.
or https://ssl.
which is not possible via HTML markup.
Related Topics
Changing CSS Values With JavaScript
How to Change Div Content With JavaScript
How to Autosize a Textarea Using Prototype
How to Get the Pure Text Without HTML Element Using JavaScript
Resize Image With JavaScript Canvas (Smoothly)
How to Impose Maxlength on Textarea in HTML Using JavaScript
Difference Between Relative Path and Absolute Path in JavaScript
Double Quote in JavaScript String
Launch Bootstrap Modal on Page Load
Change Link Color of the Current Page With Css
Why Does This Simple Jsfiddle Not Work
Appending HTML String to the Dom
Jquery Load() Only Working in Firefox
Load More Posts Ajax Button in Wordpress
"Mixed Content Blocked" When Running an Http Ajax Operation in an Https Page