I have heard and read a few articles about deferring JavaScript loading and am very interested. It seems to be very promising for web apps that may be useful on Mobile platforms where the amount of JavaScript that can be loaded and executed is limited.
Unfortunately, most of the articles talk about this at an extremely high level. How would one approach this?
EDIT
Normally, all JavaScript is loaded on page load, however, there may be functions that are not necessary until a certain action occurs, at which time, the JavaScript should be loaded. This helps ease the burden of the browser on page load.
Specifically, I have a page that very heavily uses JavaScript. When I load the page on my phone, it won't load properly. As I debugged the page, I eliminated some of the JS functions. Once enough was eliminated, the page suddenly worked.
I want to be able to load the JS as needed. And possibly even eliminate the functions simply used for start up.
The basics are simple - breaking up your JavaScript code into logically separate components and loading only what you need. Depending on what you are building you can use:
Loaders:
Modernizr.load (or yepnope.js by itself)
LABjs
Many, many, many other deferred loading libraries.
Dependency managers (which are also loaders):
Require.js
dojo.require
JavaScript MVC's steal.js
Several other dependency management libraries.
These tools make use of a wide variety of techniques to defer the loading of scripts, the execution of scripts, manage dependencies, etc. What you need depends on what you are building.
You may also want to read through this discussion to learn something more about the pros and cons of using such techniques.
Response to edit:
There isn't really a good way to unload JavaScript that you have already loaded - the closest approximation you can get is to keep all of your loading code namespaced inside your application's namespace and then "clean up" by setting that namespace, and all references to it to null.
I have used a simple script published on line with some modification done by me.
Assume that your COMPRESSED Javascript file is in the cache directory in your webserver and you want to defer the loading of this compressed js file.
Your compressed js file:
80aaad2a95e397a9f6f64ac79c4b452f.js
This is the code html code:
<script type="text/javascript" src="/resources/js/defer.js?cache=80aaad2a95e397a9f6f64ac79c4b452f.js"></script>
This is the defer.js file content:
(function() {
/*
* http://gtmetrix.com/
* In order to load a page, the browser must parse the contents of all <script> tags,
* which adds additional time to the page load. By minimizing the amount of JavaScript needed to render the page,
* and deferring parsing of unneeded JavaScript until it needs to be executed,
* you can reduce the initial load time of your page.
*/
// http://feather.elektrum.org/book/src.html
// Get the script tag from the html
var scripts = document.getElementsByTagName('script');
var myScript = scripts[ scripts.length - 1 ];
// Get the querystring
var queryString = myScript.src.replace(/^[^\?]+\??/,'');
// Parse the parameters
var params = parseQuery( queryString );
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = '/cache/' + params.cache; // Add the name of the js file
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
function parseQuery ( query ) {
var Params = new Object ();
if ( ! query ) return Params; // return empty object
var Pairs = query.split(/[;&]/);
for ( var i = 0; i < Pairs.length; i++ ) {
var KeyVal = Pairs[i].split('=');
if ( ! KeyVal || KeyVal.length != 2 ) continue;
var key = unescape( KeyVal[0] );
var val = unescape( KeyVal[1] );
val = val.replace(/\+/g, ' ');
Params[key] = val;
}
return Params;
}
})();
I would like to say thanks to http://feather.elektrum.org/book/src.html that helped me to understand how to get the parameters from the script tag.
bye
Deferring loading til when?
The reason typically why JS is loaded last, is so that the entire DOM has been loaded first.
An easy way is to just use
<body onload="doSomething();">
So you could easily have doSomething() function to load all your JS.
You can also add a function to window.onload, like
window.onload = function(){ };
Also, if you are using JS librarys, such as jQuery and Dojo, they each have their own onReady and addOnLoad methods in order to run some JS only after the document has already loaded.
Here's a useful article on the script element's defer and async attributes. Specifying these attributes will get the browser to defer loading in different ways. You can also load in an external script using JavaScript after page load.
It should also be noted that the position of your script elements within your HTML document will determine load and execution order if neither defer nor async have been specified.
Related
Most of the examples I have found on the web involve loading a URL.
However, if I simply have a string that contains an svg or html and I want to load it into a dom for manipulation, I cannot figure out how to manipulate it.
var fs=require('fs')
var content = fs.read("EarlierSavedPage.svg")
// How do I load content into a DOM?
I realize that, in this example where is a local file is being read, there is a workaround for reading the local file directly, but I am interested more generally in whether a page can be loaded from a string.
I have already looked at the documentation but did not see anything obvious.
The default page in PhantomJS is a comparable to about:blank and is essentially
<html>
<body>
</body>
</html>
It means that you can directly add your svg to the DOM to and render it. It seems that you have to render it asynchronously to give the browser time to actually compute the svg. Here is a complete script:
var page = require('webpage').create(),
fs = require('fs')
var content = fs.read("EarlierSavedPage.svg")
page.evaluate(function(content){
document.body.innerHTML = content;
}, content);
setTimeout(function(){
page.render("EarlierSavedPage.png"); // render or do whatever
phantom.exit();
}, 0); // phantomjs is single threaded so you need to do this asynchronously, but immediately
When you load an HTML file into content, then you can directly assign it to the current DOM (to page.content):
page.content = content;
This would likely also need some asynchronous decoupling like above.
The other way would be to actually load the HTML file with page.open:
page.open(filePathToHtmlFile, function(success){
// do something like render
phantom.exit();
});
Suppose I'm embedding a javascript in HTML page:
<script type="text/javascript" src="www.mydomain.com/script.js?var1=abc&var2=def"></script>
Is there a way I can get the src url inside the script and extract the params?
Given that you are using a regular script element in the HTML source, you can just get the last script element in the document. Since script elements are (in the absence of attributes that you aren't using in your example) blocking, no more will be added to the document until this one has been executed.
var scripts = document.getElementsByTagName('script');
var last_script = scripts[scripts.length - 1];
var url = script.src;
This won't work if you dynamically add a script element before the last script using DOM.
this little hack uses error handling to find the location of external scripts from within:
(function(){ // script filename setter, leaves window.__filename set with active script URL.
if(self.attachEvent){
function fn(e,u){self.__filename=u;}
attachEvent("onerror",fn);
setTimeout(function(){detachEvent("onerror", fn)},20);
eval("gehjkrgh3489c()");
}else{
Object.defineProperty( window, "__filename", { configurable: true, get:function __filename(){
try{document.s0m3741ng()}catch(y){
return "http://" +
String(y.fileName || y.file || y.stack || y + '')
.split(/:\d+:\d+/)[0].split("http://")[1];
}
}})//end __filename
}//end if old IE?
}());
it sets a global "__filename" property when run, so atop an external script, the __filename is in effect for the execution of the whole script.
i strongly prefer to sniff url parts from scr attributes, but this works in most browsers and without knowing the URL ahead of time.
I don't think there is a property already inside the script that points to this url.
From the script, you can read the DOM. So you can lookup the script tag and inspect its src attribute, but if you got multiple scripts (or the DOM was modified), you cannot really know for sure which one it is.
I assume it is for checking input. So to solve this, you can eiter:
Render the script through a server side script (PHP), and let it output variables. Disadvantage: eats more server resources and makes caching a bitch.
Just get parameter from all the scripts loading from your domain. Maybe it doesn't matter much, or you have only one script anyway. Disadvantage: In this case this is possible, but not very reliable and resistant to changes.
My preferred: Add the variables to the script tag (actually, to another script tag) to make them available directly in Javascript, rather than parsing the script url.
Like this:
<script type="text/javascript">
var1 = 'abc';
var2 = 'def';
</script>
<script type="text/javascript" src="www.mydomain.com/script.js"></script>
Here are two other solutions that will work no matter how the script is loaded (even if they are loaded dynamically or with async or defer attributes):
Put an id on the script tag.
<script id="myscript" type="text/javascript" src="www.mydomain.com/script.js?var1=abc&var2=def"></script>
Then, you can find it with the id:
$("#myscript").attr("src")
Or second, if you know the filename, you can search for any script tag that contains that filename:
function findScriptTagByFilename(fname) {
$("script").each(function() {
if (this.src.indexOf(fname) !== -1) {
return this.src;
}
});
}
var url = findScriptTagByFilename("/script.js");
Where is the JS file and is this Async the fastest way to call JS?
I guess they then have PHP calls in the .JS for updating the Ad stats??
The code:
<script type="text/javascript">
(function(){
var acc = "acc_230d269_pub";
var st = "nocss";
var or = "h";
var e = document.getElementsByTagName("script")[0];
var d = document.createElement("script");
d.src = ('https:' == document.location.protocol ?'https://' : 'http://')+"engine.influads.com/show/"+or+"/"+st+"/"+acc;
d.type = "text/javascript";
d.async = true;
d.defer = true;
e.parentNode.insertBefore(d,e);
})();
</script>
I've made your code more readable:
1 <script type="text/javascript">
2 (function () {
3 var acc = "acc_230d269_pub";
4 var st = "nocss";
5 var or = "h";
6 var e = document.getElementsByTagName("script")[0];
7 var d = document.createElement("script");
8 d.src = ('https:' == document.location.protocol ? 'https://' : 'http://') +
9 "engine.influads.com/show/" + or + "/" + st + "/" + acc;
10 d.type = "text/javascript";
11 d.async = true;
12 d.defer = true;
13 e.parentNode.insertBefore(d, e);
14 })();
15 </script>
2,14 An anonymous function wrapper is created, so that variables cannot be access from outside the function ("scope")
3 acc looks like the identifier of the advertiser
4,5 st = "nocss" and or = "h" looks like settings to adjust the appearance
7,10-12 A <script> tag is created. async = Loading the script will not block the execution of the document. defer=true prevents the script from not being executed (can be omitted)
6,13 The newly created script tag is inserted before (13) the first script tag in the document (6,13)
8,9 The URL is constructed:If the current page is transmitted over a secure connection, the injected script will also be transferred over the HTTPS protocol.
The extension of the requested file is omitted. This file could be served using the application/javascript MIME type by server configuration.
It inserts the script tag with a dynamically constructed file name and puts it in the document before the first script tag. The advantage of this approach is that it will run only when the document is loaded, so it will not block the document loading. This way, the user will experience no (or less) delay. It's a good practise to do this for analytical tools and such, because they don't add functionality for the user and you just want to track their actions. It doesn't matter if you miss one or two of those measurements.
There are a couple of ways to include js code into html, one is put the code directly into the tag, just like what you wondered about the code you posted, the other method is to use the following syntax:
<script type="text/javascript" src="path/to/external_file.js"></script>
As a side note, the code you posted uses a technique that prevents js name spacing conflicts by putting the code in the
(function() ...)(); block, which I find to be a very good practice.
Regarding the question about using async in tag, you might want to take a look at this:
http://davidwalsh.name/html5-async
Most of that code is for loading the JavaScript code asynchronously, in a way that works in different browsers. An explanation of how it works is here: http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
Loading asynchronously means that the browser doesn't wait for it to finish. So the ad won't load faster, but the rest of the page will.
If you piece together the string, you'll find that the JavaScript file they're loading is: http://engine.influads.com/show/h/nocss/acc_230d269_pub
The benefits I see are:
Asynchronous loading that would help in faster rendering of the UI
The selective http or https used for the location of the js source following the protocol that current page is loaded with
I am wondering why the js source would not end with a .js extension though
While doing development on a .js file I'd like to just refresh that file instead of the entire page to save time. Anyone know of any techniques for this?
Here is a function to create a new script element. It appends an incremented integer to make the URL of the script unique (as Kon suggested) in order to force a download.
var index = 0;
function refreshScript (src) {
var scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
scriptElement.src = src + '?' + index++;
document.getElementsByTagName('head')[0].appendChild(scriptElement);
}
Then in the Firebug console, you can call it as:
refreshScript('my_script.js');
You'll need to make sure that the index itself is not part of the script being reloaded!
The Firebug Net panel will help you see whether the script is being downloaded. The response status should be "200 OK" and not "304 Not Modified. Also, you should see the index appended in the query string.
The Firebug HTML panel will help you see whether the script element was appended to the head element.
UPDATE:
Here is a version that uses a timestamp instead of an index variable. As #davyM suggests, it is a more flexible approach:
function refreshScript (src) {
var scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
scriptElement.src = src + '?' + (new Date).getTime();
document.getElementsByTagName('head')[0].appendChild(scriptElement);
}
Alexei's points are also well-stated.
I suggest you to use Firebug for this purpose.
See this video, it helped me a lot.
http://encosia.com/2009/09/21/updated-see-how-i-used-firebug-to-learn-jquery/
If you're talking about the unfortunate case of client-side/browser caching of your .js file, then you can simply version your .js file. You can:
Rename the .js file itself (not preferred)
Update the include line to reference yourfile.js?1, yourfile.js?2, etc.. Thus forcing the browser to request the latest version from the server. (preferred)
Unfortunately, you have to refresh the web page to see edits to your JavaScript take place. There is no way that I know of to edit JavaScript in "real-time" and see those edits effect without a refresh.
You can use Firebug to insert new JavaScript, and make real-time changes to DOM objects; but you cannot edit JavaScript that has already been run.
If you just fed up refilling the forms while developing just use form recover extensions like this one https://addons.mozilla.org/ru/firefox/addon/lazarus-form-recovery/
Is it possible from a Javascript code to find out "where" it came from?
I needed this to provide scripts that could run folder-agnostic, e.g.:
http://web1/service-a/script.js
http://web2/some-folder/service-b/script.js
And they're linked in:
http://web1/page1.html
http://web2/page2.html
The file script.js is identical in both locations but I would like them to be able to find out where it originates from so it can call the right web service methods (script.js from service-a should call service-a.method while script.js that is served from service-b should call service-b.method)
Is there a way to find out where script.js came from without using any server-side code?
Well, it's kind of a hack, you could grab all the <script> tags in the document, look at which one has the file name of the file, and do the logic from there.
document.getElementsByTagName('script'); is pretty much all you need. The rest is basic JS.
Even more interesting than looping through all of the returned elements (although that's probably safest), is that we can simply only look at the last element returned by the call above, as Javascript guarantees that must be the <script> tag we're in at the time of it being parsed (with the exception of deferred scripts). So this code:
var all_scripts = document.getElementsByTagName('script');
var current_script = scripts[all_scripts.length-1];
alert(current_script.src);
Will alert the source of the script tag used to include the current Javascript file.
You can analyze source of the html where script.js is included for tag and retrieve path of the script.js from there. Include next function in script.js and use it to retrieve the path.
function getPath() {
var path = null;
var scriptTags = document.getElementsByTagName("script");
for (var i = 0; i < scriptTags.length; i++) {
var scriptTagSrc = scriptTags.item(i).src;
if (scriptTagSrc && scriptTagSrc.indexOf("script.js") !== -1) {
path = scriptTagSrc;
break;
}
}
return path;
}