load scripts asynchronosly and have a fallback - javascript

My objective here is to load scripts asynchronously when the browser supports defer or async.
If the browser supports neither I don't care about asynchronous loading (not my bad).
I want to make sure that any script is only executed when the prerequisites for it are fulfilled e.g. jQuery loaded.
I want to load my scripts while other scripts are being loaded (only if the browser supports defer or async).
I want to do this using only the browsers' API. I don't want the browser to load any reliable (or not) scripts that do that for me no matter how small they are.
This must work with IE8+, Gecko v.1.9.1+ (e.g. firefox 3.5.* or firefox 9.0+), webkit (e.g. chrome), presto (e.g. Opera). For the ones I didn't mention the version, I mean the latest stable version.
If possible I don't want any non easy scripts. I just need something simple to do this job. This means:
If possible, I don't want stuff like AJAX calls or fancy Objects with some methods to do some workarounds like I've seen in other pages. Those are to force async loading of the script in browsers that do not support async or defer
I repeat: I don't need any fancy things to make a script asynchronous. If the browser does not support defer or async I don't care. I just care is that the script is loaded so that each part is executed after its prerequisites are met and use async or defer if the browser supports it.

First, using a library such as jQuery makes this whole process infinitely easier, and reliable across browsers. It may increase the download size of your pages (by a very small amount) but the speed gained by efficient script loading/executing will nearly always outweigh that.
Regarding script async and defer attributes:
async="async": on a script tag is not supported at all by IE8/9, script executes immediately (which is ok according to your question).
defer="defer": on a script tag will begin loading after everything in the order the defer scripts appear in the HTML, prior to DOM Ready. BUT, on Firefox, scripts will often execute AFTER dom ready. This difference makes defer unreliable as a means of ensuring that scripts are loaded before executing functions after dom ready.
General Guidelines when not using jQuery:
If a script has downstream dependancies you have to place it as a standard script tag at the end of the body tag and have your inline tags all execute after document ready. Otherwise there is no guarantee that the script will be executed prior to the execution of the dependencies. Firefox is the main issue here, a "defer" script may not have finished even after DOM ready.
If a script has no downstream dependnacies, then place it at the end of the body tag, and use async="async" attribute on the script tag. IE will render it immediately and the others will render it when they receive it.
General Guidelines when using jQuery:
Place only jQuery in your <head>.
Execute all other scripts as $.getScript().
If a script needs to execute ASAP (such as analytics) use a $.getScript at the top of the body (this will be a non-blocking request, but will process as soon as the client receives the file).
If a script can wait till DOM ready, wrap the $.getScript() call in $(function() {});
If a script has many downstream dependancies, have each one register itself to the callback function for a specific script.
$(function() {
$.getScript("script.js", function() {
for(var i = 0; i < myCallbacks.length;i++) {
myCallbacks[i]();
}
});
});

Related

Will setting defer on my polyfill and other scripts guarantee that they're loaded in order?

I'm using polyfill.io to polyfill Promise and fetch for older clients. On their website they recommend using a script loader or their callback to make sure the script has loaded completely before running the modern code:
We recommend the use of the async and defer attributes on
tags that load from the polyfill service, but loading from us in a
non-blocking way means you can't know for certain whether your own
code will execute before or after the polyfills are done loading.
To make sure the polyfills are present before you try to run your own
code, you can attach an onload handler to the https://cdn.polyfill.io
script tag, use a more sophisticated script loader or simply use our
callback argument to evaluate a global callback when the polyfills are
loaded:
However, shouldn't setting defer on both scripts already guarantee that they are loaded async but still in the order in which they appear in the document (unless the browser doesn't support defer)?
<script src="https://cdn.polyfill.io/v2/polyfill.min.js" defer></script>
<script src="modernscript.js" defer></script>
According to MDN documentation defer attribute just defines a point of page loading time when script loading will occur.
From documentation that you've citated it can be seen:
To make sure the polyfills are present before you try to run your own
code, you can attach an onload handler to the https://cdn.polyfill.io
script tag
Since (as pointed into comments to this answer) it can't be clearly seen if defer scripts will be executed (1, 2) and taking in mind possible browser implementation differences - it may be not the best idea to rely on such behavior.
So better way would be either:
to use some script loader (RequireJS for example)
to add proposed onload handler to first <script> tag and create dynamic <script> tag for loading your code inside this handler
to bundle your code together with Promise polyfill (manually or using bundler like webpack) and load as single bundle.
UPDATE: As pointed by #PeterHerdenborg in comment - MDN document now clearly states that:
Scripts with the defer attribute will execute in the order in which they appear in the document.

"Load" event on script with async and/or defer

When embedding scripts like:
<script src="..." async defer></script>
Is there a way to know when they're finished loading?
Usually when the window.load event is called, one would expect all scripts to be ready as well. But I don't know if that still holds when you load them with async or defer. I've read some docs online but couldn't find anything conclusive on this issue.
Answer:
You could take advantage of the onload event attribute in order to perform some kind of callback once your script is loaded.
Example: In the example html script element below when the script (jquery library from google api) finishes loading asynchronously, an alert will pop up saying 'resource loaded'.
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" async defer onload="alert('resource loaded');">
Note: The src script will load very fast because it is hosted by google so the pop up will most likely appear as soon as the page/DOM has loaded.
Edit: added important information originally from comment.
window.onload waits for everything to load before firing whereas document.onload fires when the Document Object Model (DOM) is ready.
So if you've got async scripts document.onload will execute first while window.onload will wait for those asynchronous scripts to finish loading.
To summarize:
window.onload will take async scripts into account.
document.onload will not take async scripts into account.
Emphasis mine:
Is there a way to know when they're finished loading?
Usually when the window.load event is called, one would expect all scripts to be ready as well. But I don't know if that still holds when you load them with async or defer. I've read some docs online but couldn't find anything conclusive on this issue.
Addressing the points in bold (for specific single scripts you can use their onload events), the TL;DR is:
The document.DOMContentLoaded event will happen after all normal and deferred scripts load and execute, but doesn't care about async scripts.
The window.load event will happen after all normal, async, and deferred scripts load and execute.
Note: A script that has both async and deferred set will act as deferred on legacy browsers that don't support async, and will act as async otherwise. So the safe bet is to think of them as async.
The HTML specification does say this, albeit indirectly. The spec defines three distinct script collections that every document has (I'm naming them S1, S2, and S3):
Each Document has a set of scripts that will execute as soon as possible, which is a set of script elements, initially empty. [S1]
Each Document has a list of scripts that will execute in order as soon as possible, which is a list of script elements, initially empty. [S2]
Each Document has a list of scripts that will execute when the document has finished parsing, which is a list of script elements, initially empty. [S3]
Just above that, in the section about preparing script elements, it details how scripts are distributed to those collections. Generally speaking, during load:
These are placed in S1 (see step 31.2.2):
All async external (scripts with a src) scripts.
All async module (depends on type attribute) scripts.
These are placed in S2 (defer is irrelevant for these) (see step 31.3.2):
Non-async, injected (e.g. by the browser) external scripts.
Non-async, injected module scripts.
These are placed in S3 (see step 31.4):
Deferred, non-async, non-injected external scripts.
All non-async, non-injected module scripts (defer is irrelevant for these).
These are executed synchronously and aren't placed in any of the collections:
Non-deferred, non-async, non-injected external scripts (see step 31.5).
All inline (without a src) scripts (neither async nor defer apply to these) (see step 32).
In simplified terms:
S1 contains all the async external/module scripts.
S2 contains all the non-async injected external/module scripts.
S3 contains all the deferred external/module scripts.
Inline scripts and vanilla external scripts are executed as they're loaded and parsed (as part of the parsing operation).
The HTML spec then goes on to define what happens after parsing is complete, where the relevant parts are, in order:
Change document's ready state to "interactive"; fires document.readystatechange (see step 3)
Execute all scripts in S3 (deferred non-async non-injected) (see step 5)
Queued (will happen >= now): Fire a DOMContentLoaded event on document (see step 6.2)
Wait until all scripts in S1 (async) and S2 (non-async injected) have been executed (see step 7)
Wait until any other load-blocking operations have been completed (see step 8)
Queued:
Change document's ready state to "complete"; fires document.readystatechange (see step 9.1)
Fire a load event on window (see step 9.5)
Fire a pageshow event on window (see step 9.11)
If the document is in some container (e.g. an iframe), fire a load event on the container (see link in step 9.12)
In simplified terms, the events that depend on script executions are:
document.DOMContentLoaded happens after all the deferred scripts are executed.
document.readystatechange ("complete") and window.load happen after all scripts are executed.
window.pageshow also happens after all scripts are executed, although it happens at other times later, too.
If there's a container like an iframe or something, its load event happens after all scripts are executed as well.
Btw, as for scripts with both async and defer set, the part describing these attributes says:
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 blocking behavior that is the default.
For "modern" browsers, I assume the behavior when both are specified is to just adhere to the logic above, i.e. those scripts end up in S1 and defer is essentially ignored.
So uh... yup.

Why use defer with Google Maps Javascript?

The Google Maps javascript does some heavy DOM manipulation. Even so, the fine docs suggest to load it with the defer flag:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>
Why would the defer flag be suggested for a script that performs DOM manipulations? I ask to learn both about the defer flag and to learn about the Google Maps API as I seem to have a misunderstanding about what one of them is doing.
Normally, a script tag tells the browser to stop parsing the HTML, fetch the script, run it, and only then continue parsing the HTML. This is because the script code may use document.write to output to the HTML token stream.
async and defer are both mechanisms for telling the browser that it's okay to go ahead and keep parsing the HTML in parallel with downloading the script file, and to run the script file later, not right away.
They slightly different, though; this diagram from the script section of the WHAT-WG version of the HTML spec is useful for envisioning the differences:
Full details in the linked spec above, but in brief, for "classic" scripts (the kind you're used to; but module scripts are coming soon!):
Both async and defer allow the parsing of the HTML to continue without waiting for the script to download.
defer will make the browser wait to execute the script until the parsing is complete.
async will only make the browser wait until the script download is complete, which means it may run the script either before parsing is complete or afterward, depending on when download finishes (and remember it could come from cache).
If async is present and supported by the browser, it takes precedence over defer.
async scripts may be run in any order, regardless of the order in which they appear in the HTML.
defer scripts will be run in the order they appear in the HTML, once parsing is complete.
async and defer are well-supported in even semi-modern browsers, but are not properly supported in IE9 and earlier, see here and here.
Why would the defer flag be suggested for a script that performs DOM manipulations?
Two reasons:
It allows the parsing to continue while the script is downloaded, and
It means the script isn't run until parsing is complete.
If you didn't use defer and you placed your script tags non-optimally, using defer helps the API script behave properly by letting the browser finish building the DOM before the script tries to manipulate it.
A lot of people still put script tags in the head section of the document, even though that's usually the worst place to put them unless you use defer (or async). In most cases, the best place (unless you have a reason to do something else) is at the very end, just before the closing </body> tag, so that A) Your site renders quickly, without waiting for scripts; and B) The DOM is fully built before you try to manipulate it. Recommending defer may be saving them support hassles from people putting their script tags too early in the HTML.
The google maps examples use both async and defer flags.
The async flag allows the script to load in parallel to the DOM
parsing, and to execute as soon as the API is ready.
The defer flag allows the script to load in parallel to the DOM
parsing, but guarantees that the script will not execute until the
DOM is finished parsing.
async is supported by modern HTML5 browsers, while defer support is universal. When the tags are used together, defer is just a fallback for older browsers, and will be ignored if async is supported.
In these simple examples, either async or defer will work, though neither are necessary. In this case it's for performance only.
Refs:
Speed up Google Maps(and everything else) with async & defer
async vs defer attributes - Growing with the Web

How to defer parsing barebone javascript in liferay (if possible) [duplicate]

I have a few <script> elements, and the code in some of them depend on code in other <script> elements. I saw the defer attribute can come in handy here as it allows code blocks to be postponed in execution.
To test it I executed this on Chrome: http://jsfiddle.net/xXZMN/.
<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>
However, it alerts 2 - 1 - 3. Why doesn't it alert 1 - 2 - 3?
A few snippets from the HTML5 spec: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async
The defer and async attributes must
not be specified if the src attribute
is not present.
There are three possible modes that
can be selected using these
attributes [async and defer]. If the async attribute is
present, then the script will be
executed asynchronously, as soon as it
is available. If the async attribute
is not present but the defer attribute
is present, then the script is
executed when the page has finished
parsing. If neither attribute is
present, then the script is fetched
and executed immediately, before the
user agent continues parsing the page.
The exact processing details for these
attributes are, for mostly historical
reasons, somewhat non-trivial,
involving a number of aspects of HTML.
The implementation requirements are
therefore by necessity scattered
throughout the specification. The
algorithms below (in this section)
describe the core of this processing,
but these algorithms reference and are
referenced by the parsing rules for
script start and end tags in HTML, in
foreign content, and in XML, the rules
for the document.write() method, the
handling of scripting, etc.
If the element has a src attribute,
and the element has a defer attribute,
and the element has been flagged as
"parser-inserted", and the element
does not have an async attribute:
The element must be added to the end of the list of scripts that will
execute when the document has finished
parsing associated with the Document
of the parser that created the
element.
The real answer is: Because you cannot trust defer.
In concept, defer and async differ as follows:
async allows the script to be downloaded in the background without blocking. Then, the moment it finishes downloading, rendering is blocked and that script executes. Render resumes when the script has executed.
defer does the same thing, except claims to guarantee that scripts execute in the order they were specified on the page, and that they will be executed after the document has finished parsing. So, some scripts may finish downloading then sit and wait for scripts that downloaded later but appeared before them.
Unfortunately, due to what is really a standards cat fight, defer's definition varies spec to spec, and even in the most recent specs doesn't offer a useful guarantee. As answers here and this issue demonstrate, browsers implement defer differently:
In certain situations some browsers have a bug that causes defer scripts to run out of order.
Some browsers delay the DOMContentLoaded event until after the defer scripts have loaded, and some don't.
Some browsers obey defer on <script> elements with inline code and without a src attribute, and some ignore it.
Fortunately the spec does at least specify that async overrides defer. So you can treat all scripts as async and get a wide swath of browser support like so:
<script defer async src="..."></script>
98% of browsers in use worldwide and 99% in the US will avoid blocking with this approach.
(If you need to wait until the document has finished parsing, listen to the event DOMContentLoaded event or use jQuery's handy .ready() function. You'd want to do this anyway to fall back gracefully on browsers that don't implement defer at all.)
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."
defer can only be used in <script> tag for external script inclusion. Hence it is advised to be used in the <script>-tags in the <head>-section.
As defer attribute works only with scripts tag with src. Found a way to mimic defer for inline scripts. Use DOMContentLoaded event.
<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
// Your inline scripts which uses methods from external-scripts.
});
</script>
This is because, DOMContentLoaded event fires after defer attributed scripts are completely loaded.
The defer attribute is only for external scripts (should only be used if the src attribute is present).
Have a look at this excellent article Deep dive into the murky waters of script loading by the Google developer Jake Archibald written in 2013.
Quoting the relevant section from that article:
Defer
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
Spec says: Download together, execute in order just before DOMContentLoaded. Ignore “defer” on scripts without “src”.
IE < 10 says: I might execute 2.js halfway through the execution of 1.js. Isn’t that fun??
The browsers in red say: I have no idea what this “defer” thing is, I’m going to load the scripts as if it weren’t there.
Other browsers say: Ok, but I might not ignore “defer” on scripts without “src”.
(I'll add that early versions of Firefox trigger DOMContentLoaded before the defer scripts finish running, according to this comment.)
Modern browsers seem to support async properly, but you need to be OK with scripts running out of order and possibly before DOMContentLoaded.
Should be also noted that there might be problems in IE<=9 when using script defer in certain situations. More on this: https://github.com/h5bp/lazyweb-requests/issues/42
<script defer> -
As soon as the browser interacts with the script tag with defer
It starts fetching the script file while also parsing the HTML side-by-side.
In this case, the script is only executed once the HTML parsing is completed.
<script async> —
As soon as the browser interacts a script tag with async
It starts fetching the script file while parsing the HTML side-by-side.
But stops the HTML parsing when the script is completely fetched, after which it starts executing the script, post which the HTML parsing continues.
<script> —
As soon as the browser interacts with a script tag
It stops the parsing of HTML, fetches the script file,
In this case, executes the script, and then continues the parsing of HTML.
This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed. Since this feature hasn't yet been implemented by all other major browsers, authors should not assume that the script’s execution will actually be deferred. Never call document.write() from a defer script (since Gecko 1.9.2, this will blow away the document). The defer attribute shouldn't be used on scripts that don't have the src attribute. Since Gecko 1.9.2, the defer attribute is ignored on scripts that don't have the src attribute. However, in Gecko 1.9.1 even inline scripts are deferred if the defer attribute is set.
defer works with chrome , firefox , ie > 7 and Safari
ref: https://developer.mozilla.org/en-US/docs/HTML/Element/script
The defer attribute is a boolean attribute.
When present, it specifies that the script is executed when the page has finished parsing.
Note: The defer attribute is only for external scripts (should only be used if the src attribute is present).
Note: There are several ways an external script can be executed:
If async is present: The script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)
If async is not present and defer is present: The script is executed when the page has finished parsing
If neither async or defer is present: The script is fetched and executed immediately, before the browser continues parsing the page

What exactly is the benefit of the HTML5 async attribute on script elements?

I have some confusion around the new async attribute to the script element in HTML5 that I hope someone can give a clear answer to.
Browsers are capable of Parallel Connections, therefore images will be downloaded in parallel. But any external javascript is not downloaded in parallel with other external javascript and images. Scripts block page loading until they have been downloaded and executed.
To download a script without blocking the rest of the page loading, the most common technique is to create a script element, like Google Analytics snippet does:
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.src = '...ga.js';
ga.async = true;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
I'm not sure of how that works exactly -
either
the browser parses and renders the page, then once it has finished it notices the DOM has changed, resulting in the ga.js script being downloaded and executed
or
the browser starts downloading the javascript in parallel with other resources.
I think it is the latter.
The new asynchronous Google Analytics snippet includes the HTML5 async attribute in the script element it creates. That will not help the page blocking problem - that has already been solved by the "Script DOM Element" technique. So what does async add to the picture? According to w3schools, "if async is present, the script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)".
And according to Steve Souders site, "the main benefit of this [async attribute] is it tells the browser that subsequent scripts can be executed immediately – they don’t have to wait for ga.js".
So are async and the Script DOM element technique both solving the same problem?
Will work:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>$('body').append('Yey');</script>
Will not work:
<script async src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>$('body').append('Yey');</script>
The async attribute is just a clearer (no ambiguity very straightforward) and cleaner (it will, or is already, part of the respected HTML5 specification) approach to solve the problem. If your site serves scripts from another domain (or CDN) then the async attribute gives you a little reliability (allow the user to at least read the static content) in that the page won't block while a script from a slow (possibly down) remote host is trying to load.
There was a great article from Jake Archibald on html5rocks which addresses this topic.
According to https://www.html5rocks.com/en/tutorials/speed/script-loading/ if a <script> element is added dynamically it may not be executed until DOMContentLoaded is fired. That is, some user agents (e.g. MSIE 10) will wait until DOM is ready before running dynamically added <script> elements.
I guess Google wants to get their analytics code running faster on those user agents and as such they need to add async flag to tell the browser (e.g. MSIE 10) that it's okay to start executing the script as soon as possible. HTML5 compatible browsers would execute as if the async true even if it was not defined so the async=true has been added only to improve performance with non-HTML5 browsers.
setting async attribute to true makes sure that the script is loaded along with the rendering of html in parallel.This is essential because if script is placed at end of body and in html we are using something that depends on javascript code,so it won't be loaded and creates issue
defer can be used but defer just pauses execution of script and renders html
Async scripts are executed as soon as the script is loaded, so it doesn't guarantee the order of execution (a script you included at the end may execute before the first script file )
Defer scripts guarantees the order of execution in which they appear in the page.

Categories