which of these async loading methods is the best? document write? - javascript

Is there difference between these two async JavaScript loading methods?
<div class="g-plusone"></div>
<script type="text/javascript">
(function(d, t) {
var g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.async = true;
g.src = 'https://apis.google.com/js/plusone.js';
s.parentNode.insertBefore(g, s);
})(document, 'script');
</script>
<script type="text/javascript">
//<![CDATA[
(function() {
document.write('<fb:like href="http://www.sandrophoto.com/' + location.pathname + '" send="true" width="360" show_faces="false" font=""></fb:like>');
var s = document.createElement('SCRIPT'),
s1 = document.getElementsByTagName('SCRIPT')[0];
s.type = 'text/javascript';
s.async = true;
s.src = 'http://connect.facebook.net/en_US/all.js#xfbml=1';
s1.parentNode.insertBefore(s, s1);
})();
//]]>
</script>

These two look conceptually similar in loading. They both create a new script tag and add it to the DOM which allows both to load asynchronously without dependencies on other script tags which must load in the order presented. There are minor differences in these implementations, but they look conceptually the same from the loading point of view.
The document.write() in the FB code causes some serialization with other loading events and could slow it down slightly (depends upon circumstances).

The most obvious difference is that the document.write approach writes the code into the doc exactly where it's placed, wherein the other method places the content into the container div specified.
There are some other differences too, and some other ways of loading scripts asynchronously.
Steve Souders has written an excellent article on the topic, http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/, which I recommend reading.

Related

Loading external JS file Asynchronously in document.ready()

I am trying to reference an external JS file asynchronously in document.read() event in application.
What am I currently doing to achieve this?
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://xxxx/jquery-3.2.1.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
My Question:
Is there a better way to achieve the same thing with less lines of code using any other methods?
If you are using jQuery, then you could use the .getScript function.
jQuery.getScript("https://xxxx/jquery-3.2.1.js");
I think the way you are doing it now is a good way of doing it. But if you want fewer lines of code and/or want the html you are adding to be more visible, you could do this.
var t = createElement("div");
t.innerHTML = '<script type="text/javascript" async src="https://xxxx/jquery-3.2.1.js"></script>';
var x = document.querySelector("script");
x.parentNode.insertBefore(t.childNodes, x);

Load external file variable and at the same time avoid browser external file caching

I need to append timestamp to the javaScript file in tag url to avoid caching.
This function found here in stackoverflow seems to do it:
<script type="text/javascript">
(function(){
var randomh=Math.random();
var e = document.getElementsByTagName("script")[0];
var d = document.createElement("script");
d.src = "TESTINGX.JS?x="+randomh+"";
d.type = "text/javascript";
d.async = true;
d.defer = true;
e.parentNode.insertBefore(d,e);
})();
But the problem is that inside the file testingx.js I've placed the following code:
var hello = "Hello World!";
And for some reason the variable doesn't get the global scope (or maybe the problem is other).
My html is the following:
<!DOCTYPE html>
<HTML><HEAD>
<script type="text/javascript">
(function(){
var randomh=Math.random();
var e = document.getElementsByTagName("script")[0];
var d = document.createElement("script");
d.src = "TESTINGX.JS?x="+randomh+"";
d.type = "text/javascript";
d.async = true;
d.defer = true;
e.parentNode.insertBefore(d,e);
})();
</script>
</HEAD><BODY>
<h1>WiFi Mini Web Server</h1>
<script>
document.write(hello); //hello var is contained in the TESTINGX.JS file\n\
</script>
</BODY></HTML>
If you want the external script to block everything else until it loads but still need to load it from a dynamic URL (it sounds like you do), you should be able to document.write the script element directly instead of using the DOM methods.
<!DOCTYPE html>
<html><head>
<script>
var randomh = Math.random();
document.write('<script src="TESTINGX.JS?x=' + randomh + '"></' + 'script>');
</script>
</head><body>
<h1>WiFi Mini Web Server</h1>
<script>
document.write(hello); // hello var is contained in the TESTINGX.JS file
</script>
</body></html>
http://jsbin.com/ixagud/2/edit
You code for cache busting, also happens to ensure that the script is loaded in a "lazy", "non-blocking" manner. Which means the TESTING.js is loaded after your DOM is loaded. Which also means that the document.write script is run before the js file has loaded, and added the required var to the global scope.
There can be several solutions:
1. Remove defer and async from your loading script
Ensure variables from your script are accessed on your page, only after the script is actually loaded
Better yet, don't try to handle cache busting through clever javascript (I am assuming the html is question is served using some sort of server technology like a jsp or php script). Use the server technology to add a parameter with a random number or timestamp to the usual script tag in the html
Instead of using a script to bust cache, use a normal script tag, with a parameter that is same as the timestamp in millis, as on the day when you make the build, and embed in your microcontroller / server. Treat this like a version number of the js file. So clients can use the file cached in their browser, provided the version number is the latest.
This ant task http://code.google.com/p/ant-web-tasks/wiki/CacheBusting is a good way to implement point no 4. I have had good experience with it.
Hope this helps.
try this
<script type="text/javascript">
(function(){
var randomh=Math.random();
var e = document.getElementsByTagName("script")[0];
var d = document.createElement("script");
d.src = "TESTINGX.JS?x="+randomh+"";
d.type = "text/javascript";
d.async = true;
d.defer = true;
e.parentNode.insertBefore(d,e);
document.write(hello);
})();
</script>

Javascript to load another js file

I am trying to load another JS file from a JS file.
From my JavaScript file run.js, I have the following:
document.write("<script type='text/javascript' src='my_script.js'></script>");
alert(nImages);
In side my_script.js I have the following:
<SCRIPT language="JavaScript" type="text/javascript">
<!--
nImages = 6;
//-->
</SCRIPT>
But I can't seem to get it to alert the nImages from my_script.js file.
You could do this:
var script = document.createElement('script');
script.src = 'my_script.js';
script.type = 'text/javascript';
script.onload = function () {
alert(nImages);
};
document.getElementsByTagName('head')[0].appendChild(script);
You should not use HTML inside of your script file. Your script file my_script.js should have only this in it.
nImages = 6;
Additional note: you don't need language="JavaScript" or the <!-- or //-->. Those are old conventions not needed for modern browsers (even IE6). I'd also avoid using document.write() in your JS as it has performance implications. You may want to look at a library such as RequireJS which provides a better way to load other JS files in the page.
I also have a code snippet on Github inspired by Steve Souders that loads another file via straight JS.
var theOtherScript = 'http://example.com/js/script.js';
var el = document.createElement('script');
el.async = false;
el.src = theOtherScript;
el.type = 'text/javascript';
(document.getElementsByTagName('HEAD')[0]||document.body).appendChild(el);
This will append the other script to the element (if it exists) or the of the page.
Javascript files should not have HTML in them. They should consist entirely of Javascript code, so my_script.js should contain only:
nImages = 6;
This still won't work because when you write the new script tag into the document it doesn't run immediately. It is guaranteed that run.js finishes running before my_script.js starts, so nImages is undefined when you alert it and then becomes 6 later. You'll see that this works:
document.write("<script type='text/javascript' src='my_script.js'></script>");
function call_on_load(){
alert(nImages);
}
If the contents of my_script.js are:
nImages = 6;
call_on_load();
Edit
Since you said in a comment that you can not edit my_script.js you can do this although it is not nearly as nice a solution:
// Force nImages to be undefined
var undefined;
window.nImages = undefined;
document.write("<script type='text/javascript' src='my_script.js'></script>");
(function is_loaded(cb){
if(typeof window.nImages == 'undefined')
setTimeout(function(){ is_loaded(cb); }, 100);
else
cb();
})(function(){
// This is executed after the script has loaded
alert(nImages);
});
This is not a nice solution, however, since it will continue polling indefinitely if there is an error loading the script.
EDIT
You posted in a comment the file you want to include, which has the <SCRIPT at the top. This file is useless and you can't do anything about it client side. You'd have to write a server side script to load the file as text in which case you can just parse it for the value you want.

Is it possible to use a variable for a javascript reference

Instead of:
<script src="/scripts/myJsFile.v1.js" type="text/javascript></script>
Have something like
<script src="/scripts/myJsFile." + versionVar + ".js" type="text/javascript></script>
This way when we update the js version files the user won't have to clear their cache.
Not in that way, because you're mixing HTML and JavaScript together. HTML does not have JavaScript variables available.
What you can do, however, is adding the <script> tag dynamically, i.e. through JavaScript. That way, you obviously are able to use variables:
<script>
var versionVar = "1.0";
window.addEventListener('load', function() { // on load
var scriptTag = document.createElement('script'); // create tag
scriptTag.src = "/scripts/myJsFile." + versionVar + ".js" // set src attribute
scriptTag.type = "text/javascript"; //set type attribute
document.getElementsByTagName('head')[0].appendChild(scriptTag); // append to <head>
}, false);
</script>
Check out how Google loads their Analytics. Then maybe try something similar like:
(function() {
var versionVar = 9;
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.src = 'http://www' + '.google-analytics.com/ga' + versionVar + '.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
You can't do this in your HTML file directly. But still you can do this inside an script tag if versopnVar is a JavaScript variable in your window context:
<script type="text/javascript">
var script = document.createElement('script');
script.setAttribute('src', '/scripts/myJsFile.' + versionVar + '.js');
script.setAttribute('type', 'text/javascript');
document.body.appendChild(script);
</script>
At the end, it's not a good aproach doing this. Please read this article at a list apart to get informed.
Alternative Style: Working With Alternate Style Sheets
It would probably be better to do something like
<script src="/scripts/myJsFile.js?v1" type="text/javascript></script>
Then, when you make and update:
<script src="/scripts/myJsFile.js?v2" type="text/javascript></script>
Which will cause most browsers to pull the file rather than pull from cache. This means that you won't have separate JS files. But will just be forcing the user to pull the most recent.
Also, if you want it to always pull the file you can, in a similar manner, append a random int.
You cannot do that straight out.
One way is with some server side code.
For example in php:
<?php $version = "1.0"; ?>
<script src="/scripts/myJsFile.<?php echo $version ?>.js" type="text/javascript></script>
Not exactly that way, but you can create a new script node with e.g. document.createElement and add it to the page.
var s = document.createElement("script");
s.src = ...
document.body.appendChild(s);
You can also use the document.write call to do the same...
You'd have to update your page to update the variable. Also, you'd have to update your javascript file name every time you changed it.
You can use a query string to make your JS unique.
<script src="/scripts/myJsFile.js?version=2" type="text/javascript></script>
marshall & I had the same Idea.
Also, you'd have to update your HTML file every time you updated your Javascript file.

Get surrounding element of javascript

I have code that looks like this:
<div>
<script type="text/javascript">
(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'localhost/chat/logo.js');
var t = document.getElementById('craftysyntax');
t.appendChild(s, t);
})();
</script>
<div>
I want the file that was loaded by async' to be able to do something like this (note: this is not working js code, but it is what I want to do)
parent.appendChild(abc);
which will add the child element abc in the parent div.
So, is there any way the script can reference its parent container?
Edit:
Why do I want to do this?
My website (an online store) uses a chat program with a link in the navigation bar that changes based on whether there is an operator logged in to the chat program. I am trying to convert the program to use an asynchronous loader, but the external js can not use document.write if it has been loaded asynchronously. I am trying to convert all of the document.write calls in the script to use the dom instead.
When your code is immediately executed like that, the last script element will be the current one (because of synchronous downloading and executing).
So you can get a reference to all script elements, and then get the last one using the length property minus one.
Then you can access its parent node with parentNode property.
<script type="text/javascript">
var allScripts = document.getElementsByTagName('script'),
thisScriptParent = allScripts[scripts.length - 1].parentNode;
</script>
jsFiddle.
Also, there is no need to use a ternary to check for https. Just use protocol-less (//localhost/chat/logo.js) and it will resolve to the parent site's protocol.
I don't think you can reliably get the script tag that the code is running from.
I'm not sure why your logo.js script doesn't just directly get the element by its id ("craftysyntax"), but if you are looking for a more general solution (where you could, say, add multiple such elements/scripts to the document in rapid succession, without having them mess each other up), you could do something like this:
<div>
<script type="text/javascript">
(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'localhost/chat/logo.js');
var t = document.getElementById('craftysyntax');
if (window.global_chatElems == null)
global_chatElems = [];
global_chatElems.push(t);
t.appendChild(s);
})();
</script>
<div>
then in your logo.js script:
if (window.globalChatElems) {
for (var i=0; i<globalChatElems.length; i++)
processChatElem(globalChatElems[i]);
window.globalChatElems = null;
}
Of course, if you are only allowing one of these elements per page, everything is much simpler....no need to make the global be an array, it can just be a single variable.
I ran into an old friend from school and we discussed this and figured out this solution.
I will have the inline script create its own div named based on the current time (or something like that).
Then I can pass the id of the div to the other script in the query string.
(I already have the getQueryString function)
Inline
<script type="text/javascript">
var randval = "helpbox" + Math.floor(Math.random()*10000);
document.write('<div id="' + randval + '"></div>');
(function() {
var csc = document.createElement('script');
csc.type = 'text/javascript';
csc.src = 'a1.js?divid=' + randval;
var s = document.getElementById('help');
s.appendChild(csc, s);
})();
External
var my_div = document.getElementById(getQuerystring('divid'));

Categories

Resources