In order to improve the page performance I need to preload scripts that I will need to run on the bottom page.
I
With similar technique you may preload scripts and stylesheets using img
for Internet Explorer and object
tag for every other browser.
var isMSIE = /*@cc_on!@*/false;
var resources = ['a.js', 'b.js', 'c.css'];
for (var i=0; i<resources.length; i++){
if (isMSIE){
new Image().src = resources[i];
} else {
var o = document.createElement('object');
o.data = resources[i];
document.body.appendChild(o);
}
}
There is a blog post describing such a technique and outlining caveats: Preload CSS/JavaScript without execution.
But why don't you want to just use dynamically added scripts just like suggested in other answer, this will probably lead to a cleaner solution with more control.
Why not to try this?
var script = document.createElement('script');
script.src = 'http://path/to/your/script.js';
script.onload = function() {
// do something here
}
document.head.appendChild(script);
you can use .onload event to control when it is loaded. One caveat is that .onload() doesn't work in IE and you can use this:
script.onreadystatechange = function() {
if (/^loaded|complete$/i.test(this.readyState)) {
// loaded
};
}
Additionally adding scripts via DOM is non-blocking and i believe you can perfectly achieve your goals with this approach.
You can use the prefetch
attribute of a link tag to preload any resource, javascript included. As of this writing (Aug 10, 2016) it isn't supported in Safari, but is pretty much everywhere else:
<link rel="prefetch" href="(url)">
More info on support here: http://caniuse.com/#search=prefetch
Note that IE 9,10 aren't listed in the caniuse
matrix because Microsoft has discontinued support for them.
More info here and more options for preloading, like prerender and more
For each script you'd like to download without executing, make an object containing a name and a url, and put those objects into an array.
Looping through the array, use jQuery.ajax
with dataType: "text"
to download your scripts as text. In the done
handler of the ajax call, store the text content of the file (which is passed in first argument) in the appropriate object, increment a counter, and call an "alldone" function when that counter is equal to the number of files you are downloading in this manner.
In the "alldone" function (or later) do the following: Loop through your array again, and for each entry, use document.createElement("script")
, document.createTextNode(...)
, and (...scriptNode...).appendChild(...)
to dynamically generate scripts having the intended source inline, rather than via "src" attribute. Finally, do document.head.appendChild(...scriptNode...)
, which is the point when that script is executed.
I have used this technique in a project where I needed to use frames, where several frames and/or the frameset need identical JavaScript files, in order to make sure each of those files is requested only once from the server.
Code (tested and working) follows
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<script id="scriptData">
var scriptData = [
{ name: "foo" , url: "path/to/foo" },
{ name: "bar" , url: "path/to/bar" }
];
</script>
<script id="scriptLoader">
var LOADER = {
loadedCount: 0,
toBeLoadedCount: 0,
load_jQuery: function (){
var jqNode = document.createElement("script");
jqNode.setAttribute("src", "/path/to/jquery");
jqNode.setAttribute("onload", "LOADER.loadScripts();");
jqNode.setAttribute("id", "jquery");
document.head.appendChild(jqNode);
},
loadScripts: function (){
var scriptDataLookup = this.scriptDataLookup = {};
var scriptNodes = this.scriptNodes = {};
var scriptNodesArr = this.scriptNodesArr = [];
for (var j=0; j<scriptData.length; j++){
var theEntry = scriptData[j];
scriptDataLookup[theEntry.name] = theEntry;
}
//console.log(JSON.stringify(scriptDataLookup, null, 4));
for (var i=0; i<scriptData.length; i++){
var entry = scriptData[i];
var name = entry.name;
var theURL = entry.url;
this.toBeLoadedCount++;
var node = document.createElement("script");
node.setAttribute("id", name);
scriptNodes[name] = node;
scriptNodesArr.push(node);
jQuery.ajax({
method : "GET",
url : theURL,
dataType : "text"
}).done(this.makeHandler(name, node)).fail(this.makeFailHandler(name, node));
}
},
makeFailHandler: function(name, node){
var THIS = this;
return function(xhr, errorName, errorMessage){
console.log(name, "FAIL");
console.log(xhr);
console.log(errorName);
console.log(errorMessage);
debugger;
}
},
makeHandler: function(name, node){
var THIS = this;
return function (fileContents, status, xhr){
THIS.loadedCount++;
//console.log("loaded", name, "content length", fileContents.length, "status", status);
//console.log("loaded:", THIS.loadedCount, "/", THIS.toBeLoadedCount);
THIS.scriptDataLookup[name].fileContents = fileContents;
if (THIS.loadedCount >= THIS.toBeLoadedCount){
THIS.allScriptsLoaded();
}
}
},
allScriptsLoaded: function(){
for (var i=0; i<this.scriptNodesArr.length; i++){
var scriptNode = this.scriptNodesArr[i];
var name = scriptNode.id;
var data = this.scriptDataLookup[name];
var fileContents = data.fileContents;
var textNode = document.createTextNode(fileContents);
scriptNode.appendChild(textNode);
document.head.appendChild(scriptNode); // execution is here
//console.log(scriptNode);
}
// call code to make the frames here
}
};
</script>
</head>
<frameset rows="200pixels,*" onload="LOADER.load_jQuery();">
<frame src="about:blank"></frame>
<frame src="about:blank"></frame>
</frameset>
</html>
other question closely related to above approach other related question
You should have a look at the following links:
http://calendar.perfplanet.com/2011/lazy-evaluation-of-commonjs-modules/
http://tomdale.net/2012/01/amd-is-not-the-answer/
And at how ember.js is using a tool called minispade and preprocessing with ruby to make the process of loading, parsing and running javascript modules fast.
I've answered the same question there:
https://stackoverflow.com/a/46121439/1951947
just use the <link>
tag to preload your script and then you can use it with the <script>
tag
eg: <link href="/js/script-to-preload.js" rel="preload" as="script">