Script preload without execution

Problem

To improve page performance , I need to pre-load the scripts that I will need to work on the bottom page .

I would like to take control when a script is parsed, compiled and executed.

I should avoid the script tag because it is a blocker for the usual rendering mechanisms (geeko, etc.).

I cannot load it using the defer property, because I need to control when the script is executed. In addition, the async property is not possible.

:

<html><head> //preload scripts ie: a.js without use the script </head><body> ..... all my nice html here //execute here a.js </body></html> 

This allows me to maximize the rendering performance of my page, as the browser will start loading the contents of the scripts and it will display the page at the same time. Finally, I can add a script tag, so the browser will parse, compile and execute the code.

The only way I could do this was to use a hidden image tag. (This is a simplified version of Stoyan )

i.e.

  <html><head> <img src="a.js" style=display:none;> </head><body> ..... all my nice html here <script src="a.js"> </body></html> 

Question

I have not found any problem using this technique, but does anyone know a better way to do this? Is there any prefetch?

Additional Information

I use requirejs , so I'm trying to preload the module code without executing it, because this code depends on the DOM elements.

+9
performance javascript browser requirejs
Jun 19 '12 at 14:00
source share
6 answers

You should 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 how ember.js uses a tool called minispade and preprocessing with ruby ​​to quickly complete the process of loading, parsing and running javascript modules.

0
Jun 19 '12 at 20:30
source share

Using a similar technique, you can preload scripts and stylesheets using img for Internet Explorer and the 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 describing caveats: Preload CSS / JavaScript without execution .

But why don't you just use dynamically added scripts as suggested in another answer, this is likely to lead to a cleaner solution with more control.

+6
Jun 19 '12 at 14:25
source share

You can use the prefetch attribute of the link tag to preload any resource, including javascript. At the time of this writing (August 10, 2016), it is not supported in Safari, but almost everywhere:

<link rel="prefetch" href="(url)">

More about support here: http://caniuse.com/#search=prefetch

Please note that IE 9.10 is not listed in the caniuse matrix because Microsoft has stopped supporting them.

More information here and additional options for pre-loading, such as prerender and much more.

+4
Aug 10 '16 at 13:25
source share

For each script that you want to load without execution, create an object containing the name and URL, and put these objects in an array.

Scrolling through an array, use jQuery.ajax with dataType: "text" to load your scripts as text. In the done handler of the ajax call, save the text content of the file (which is passed in the first argument) in the corresponding object, increase the counter and call the "alldone" function when this counter is equal to the number of files that you load in this way.

In the "alldone" (or later) function, do the following: loop through your array and use document.createElement("script") , document.createTextNode(...) and (...scriptNode...).appendChild(...) for each entry (...scriptNode...).appendChild(...) for dynamically creating scripts that have an alleged inline source, and not through the "src" attribute. Finally, do document.head.appendChild(...scriptNode...) , which is the point when the script is executed.

I used this technique in a project where I needed to use frames, where several frames and / or a set of frames needed identical JavaScript files to make sure that each of these files is requested only once from the server.

Code (verified 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> 

another issue closely related to the above approach another related issue

+1
Feb 27 '17 at 13:32
source share

Why not try it?

 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 the .onload event to control when it loads. One caveat is that .onload () does not work in IE, and you can use this:

 script.onreadystatechange = function() { if (/^loaded|complete$/i.test(this.readyState)) { // loaded }; } 

In addition, adding scripts through the DOM does not block, and I believe that you can achieve your goals with this approach.

0
Jun 19 2018-12-12T00:
source share

I answered the same question:

stack overflow

just use the <link> tag to preload your script and then you can use it with the <script>

for example: <link href="/js/script-to-preload.js" rel="preload" as="script">

0
Sep 08 '17 at 17:09 on
source share