Dynamically load javascript with load completion notification

Here's a javascript function to dynamically load javascript into a page and get a notification (callback) when the script is done loading.

I used to use the good 'ol window.onload handler - but I've found that this way is better as the Web APIs we're building need to dynamically add scripts to the page that is embedding them and know when the script is done loading!

The main advantage :
This function loads the specified script and calls the javascript function you specify when the script has finished loading.
Your code can call this at any time during the life of your app - even based on user interaction! (see HTML test code below below).

This "lazy script loading" has other advantages:
- reducing the initial page / app load time
- allows for loading scripts based on user input / feature requirements


The add_script function



/* utility to add a script node to the current document
and call the callback function when it is loaded.
Should allow scripts to be loaded at any time during document
life..and have code be kicked off after they are loaded.

Browsers tested:
FF 3.6.x - WORKS
Chrome 5.x - WORKS
Safari 4.x - WORKS
MS IE 8 - WORKS
Opera 10.x - WORKS
*/

functionadd_script(scriptURL, onloadCB) {
var scriptEl = document.createElement("script");
scriptEl.type = "text/javascript";
scriptEl.src = scriptURL;

function calltheCBcmn() {
onloadCB(scriptURL);
}

if(typeof(scriptEl.addEventListener) != 'undefined') {
/* The FF, Chrome, Safari, Opera way */
scriptEl.addEventListener('load',calltheCBcmn,false);
}
else {
/* The MS IE 8+ way (may work with others - I dunno)*/
function handleIeState() {
if(scriptEl.readyState == 'loaded'){
calltheCBcmn(scriptURL);
}
}
var ret = scriptEl.attachEvent('onreadystatechange',handleIeState);
}
document.getElementsByTagName("head")[0].appendChild(scriptEl);
}





The HTML used to test loading two scripts based on user input




<!DOCTYPE HTML>
<html>
<head>
<TITLE> Script load testing</TITLE>
</head>
<body>

<div id="control_area" style="width:100%;height:100%;border:solid red 1px">
Select the script to load:
<select id="script_selector" size="1">
<option> ./gen_include2.js</option>
<option> ./gen_include.js</option>
</select>
<ul>
<li> <a href="javascript:void(0);" onclick="load_selected();"> Load selected script</a> </li>
</ul>
</div>
<div id="output_area"
style="position:relative;width:100%;height:200px;overflow:auto;
border: dotted 1px #ff0000;">
</div>

<script type="text/javascript">

/* clear the scratch area */
document.getElementById("output_area").value = "";

function load_selected() {
output_str("+load_selected");
var ssel = document.getElementById("script_selector");
var sselURL = ssel.options[parseInt(ssel.selectedIndex)].text;

function myOnloadCB(loadedScriptURL) {
output_str("myOnloadCB,script loaded: " + loadedScriptURL);
if(typeof(window.gen_include_js) != 'undefined'){
output_str("window.gen_include_js exists in DOM");
}
if(typeof(window.gen_include_js2) != 'undefined'){
output_str("window.gen_include_js2 exists in DOM");
}
}
add_script(sselURL, myOnloadCB);
}

/* utility to add a script node to the current document
and call the callback function when it is loaded.
Should allow scripts to be loaded at any time during document
life..and have code be kicked off after they are loaded.

Browsers tested:
FF 3.6.x - WORKS
Chrome 5.x - WORKS
Safari 4.x - WORKS
MS IE 8 - WORKS
Opera 10.x - WORKS
*/
function add_script(scriptURL, onloadCB) {
output_str("+add_script");
var scriptEl = document.createElement("script");
scriptEl.type = "text/javascript";
scriptEl.src = scriptURL;

function calltheCBcmn() {
onloadCB(scriptURL);
}

if(typeof(scriptEl.addEventListener) != 'undefined') {
/* FF, Chrome, Safari way */
scriptEl.addEventListener('load',calltheCBcmn,false);
}
else {
/* MS IE way */
function handleIeState() {
output_str("+handleIeState,scriptEl.readyState=" + scriptEl.readyState);
if(scriptEl.readyState == 'loaded'){
calltheCBcmn(scriptURL);
}
}
var ret = scriptEl.attachEvent('onreadystatechange',handleIeState);
output_str("attachEvent ret = " + ret);
}
document.getElementsByTagName("head")[0].appendChild(scriptEl);
}

/* function to print to the the output_area div */
function output_str(str){
var da = document.getElementById("output_area");
da.innerHTML += str + "<br/> ";
da.scrollTop = da.scrollHeight;
}
</script>

</body>
</html>






The contents of the 2 .js files dynamically loaded in the HTML above.



/* gen_include.js */
(function()
{
window.gen_include_js = {};
window.gen_include_js.loaded = true;
}
)();


/* gen_include2.js */
(function()
{
window.gen_include_js2 = {};
window.gen_include_js2.loaded = true;
}
)();

For more complete information about compiler optimizations, see our Optimization Notice.

5 comments

Top
anonymous's picture

A lot of specialists tell that personal loans aid people to live the way they want, just because they can feel free to buy necessary things. Furthermore, various banks give college loan for different classes of people.

Andy Idsinga (Intel)'s picture

Hey Tim - the loadedScriptURL is the same URL that is passed into the original call to add_script(). That would be your code's call to add_script().

Once loaded, add_script() is passing the url back to the callback from the calltheCBcmn().

In the code above, onloadCB ..inside calltheCBcmd() resolves to myOnloadCB() ...which uses the name "loadedScriptURL".

Ugg, now my head hurts :)
Andy

anonymous's picture

Hi Andy I see that it works but there is a place in the script that is making my brain hurt: Where is the value for loadedScriptURL coming from? Firefox also says it's undefined, but it still works. I can see that the call is somehow feeding back the value to the parameter of the function call, but I've never seen it done like this before and I'm not getting how it's working..

Andy Idsinga (Intel)'s picture

Hi Michael - sorry for the delayed response - I usually get an email notification when someone comments but haven't been lately.

In any case - I've had the same issues with chrome - but I usually fiddle with it and clearing the cache and then eventually it loads the newer file.

You should also make sure you that if you re-add a particular script url to the document (with time stamp) that you remove the previous node. I've done something very similar and had chrome re-request the scripts from the server as desired.

Cheers,
Andy

anonymous's picture

Hi, I have tried your code in:
Firefox 3.6.3
IE 7.0.5730.13
Opera 10.51
Chrome 5.0.375.127 [Latest at time of writting]

I Loaded the html file in each, then changed gen_include.js to include:
alert("hi");
and reloaded the scripts using your function

All worked as expected except chrome which failed to show the alert box.

I have run into this time and time again with chrome where it loads the file in once, but does not re-load the files in (uses the cache instead).

I use the time hack to force the other browsers to always fetch from server not cache:

// Generate Time for use as get latest update
var time = new Date();
var time = time.getTime();
// Generate Filename
var FileName = "Templates/Login/Template.js?"+ time;
// Load In Javascript file
document.getElementById('TemplateJavascript').src = FileName;

This again works fine (albiet without notification) in the above browsers except chrome.

I am trying to use a 'ghost' code system where only the relevant JS code / Page code is loaded in the browser at any one time with the function

FN_Template_RunMe()

run once the page is loaded in to the respective div (It is a web application so 'pages' are changed within a div so global variables are always present.

Any idea how to force Chrome to behave like the rest?

Cheers
Michael

Add a Comment

Have a technical question? Visit our forums. Have site or software product issues? Contact support.