Tuesday, October 13, 2009

Synchronously loading external javascript files

I ran in to an interesting problem recently. I needed to modify a web page but had no access to the page itself, just the javascript files that were used on the page. My solution was to use jQuery and modify the elements through javascript.

My first problem was that I needed to load jQuery javascript file, not via inline html with the

<script ... ></script>


tag, but inside from inside another javascript file. There are plenty of websites explaining how to do this with code like:

function loadjsfile(filename){
var fileref;
fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", filename)
var html_doc = document.getElementsByTagName('head')[0];
html_doc.appendChild(fileref);
}

loadjsfile('jquery.js');
loadjsfile('file1.js');
loadjsfile('file2.js');



The problem here is that these files will not load synchronously. I.e. I need file 1 to load after jQuery and file 2 to load after file 1 but using the above method, the files will load asynchronously... which will cause everything to fail. Then I stumbled on this blog post:

http://www.phpied.com/javascript-include-ready-onload/

Which showed me the trick of attaching the onreadystatechange and onload events to the file... nice work guys.

Next, I took their script and modified it a little bit to:
  1. Use an array and a little bit of recursion to load synchronously instead of simply popping up alerts
  2. Load both css and js files in one method
  3. Added fileref.readyState == 'loaded' to the ie code for IE7


// create a 2 dimentional array  - 3 files
var myArray=new Array(3)
for (i=0; i < myArray.length; i++) { 
myArray[i]=new Array(2) 

}

myArray[0][0]="somecssfile.css";
myArray[0][1]="css";

myArray[1][0]="jquery.js";
myArray[1][1]="js";

myArray[2][0]="dependsonjquery.js";
myArray[2][1]="js";

var fileref;
var index = 0;

function loadjscssfile( ) {

//if filename is a external JavaScript file    
//alert('loading = ' + myArray[index][0])
if (myArray[index][1]=="js"){   
fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", myArray[index][0])
}

//if filename is an external CSS file
else if (myArray[index][1]=="css"){ 
fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", myArray[index][0])
}

if (typeof fileref!="undefined") {  
var html_doc = document.getElementsByTagName('head')[0];
html_doc.appendChild(fileref);
}




// IE
fileref.onreadystatechange = function () {    
// complete is for ie6,                 loaded is for ie7
if (fileref.readyState == 'complete' || fileref.readyState == 'loaded') {                  
var fileName;                        
fileref.getAttribute('src') == null ? fileName = fileref.getAttribute('href') : fileName = fileref.getAttribute('src') ;  
//alert('loaded = ' + fileName)
index++;                  
if (index <  myArray.length) {  
loadjscssfile();
}

}
}

// Firefox
fileref.onload = function () {    
if (!(/MSIE (\d+\.\d+);/.test(navigator.userAgent))) { //test for MSIE x.x;
index++;                  
if (index <  myArray.length) {  
loadjscssfile();
}
}        
}
return false;
}

loadjscssfile(); 



Works like a charm... now you know :-). Any questions, please post'em.