How to Auto Include a Javascript File

By Mark Kahn

Many developers have a large library of JavaScript code at their fingertips that they developed, their collegues developed, or that they've pieced together from scripts all over the Internet. Have you ever thought that it would be nice to not have to search through all those files just to find that one function? This article will show you how dynamically include any JavaScript file, at runtime, by simply calling a function in that file!

Here's an example: You have a function foo() in file bar.js. In your code, you know that foo() might be called, but it probably won't be because most people do not use its functionality. You don't want to force the user to download bar.js unless it's going to be used because it's a fairly large file. Here you'll learn how to make a fake foo() function that actually loads bar.js on the fly and then calls the real foo() function.
Dynamically Loading a Script

As many developers know, there are at least two different ways to dynamically load a script at runtime. The first is to create a script object and append it to the document. The second is to use an XMLHTTP request to grab the source code, and then eval() it.

It is this second method that we're going to use, and we're going to exploit the fact that an XMLHTTP request has the capability to completely stall any script activity.

First, some basics: how to create an XMLHTTP Object. There are as many different functions to return a cross-browser XMLHTTP Object as there are developers that work with AJAX. I happen to have my own as well, and here's a simplified example of that:

Js代码 复制代码 收藏代码
  1. function getXMLHttpObj(){   
  2.         if(typeof(XMLHttpRequest)!='undefined')   
  3.             return new XMLHttpRequest();   
  4.   
  5.         var axO=['Msxml2.XMLHTTP.6.0''Msxml2.XMLHTTP.4.0',   
  6.             'Msxml2.XMLHTTP.3.0''Msxml2.XMLHTTP''Microsoft.XMLHTTP'], i;   
  7.         for(i=0;i<axO.length;i++)   
  8.             try{   
  9.                 return new ActiveXObject(axO[i]);   
  10.             }catch(e){}   
  11.         return null;   
  12.      }  

 

 

Most browsers other than Internet Explorer 5 or 6 have a built-in XMLHttpRequest object. Internet Explorer 7, when it's released, will also have this object natively. The first thing we do is check to see if this object exists. If it does, we create an instance of it and that's it. If the object doesn't exist, we attempt to create one of several ActiveX Objects. We don't know what objects our users have installed, so we attempt to create several different XMLHTTP objects, starting with the newest ones.

Now in order to dynamically load functions, we first need to define them. We could do this one function at a time, but instead of hard-coding dozens of functions, we can choose to just make an object or array with all the file names and the functions you want to have auto-included:

Js代码 复制代码 收藏代码
  1. var autoIncludeFunctions = {   
  2.     'scripts/file1.js': ['function1''function2''function3'],   
  3.     'scripts/file2.js': ['function4''function5''function6'],   
  4.     'scripts/file3.js': ['function7''function8''function9']   
  5. }  

 

 


Our autoIncludeFunctions object should contain a list of JavaScript files, as well as a list of functions in those files. Here we are using shorthand JavaScript notation to create both the object and the arrays, but the same thing could be accomplished in many different ways.

These .js files can contain any code you have available, such as JavaScript menus, animations, etc. The simplest example would be a file titled "file1.js" that only contained "function function1(){ alert('Hello, World!'); }".

Note that if any of these files contain functions with the same name as another file, only the last function listed will be used.

To make things a bit easier, we're going to make a function that will pull a JavaScript file down and execute it. It's very important, in our case, that the third paramater sent to the XMLHTTP object be false. This forces the script to wait for the response to download as opposed to continuing on with other code.

 

Js代码 复制代码 收藏代码
  1. function loadScript(scriptpath, functions){   
  2.     var oXML = getXMLHttpObj();   
  3.     oXML.open('GET', scriptpath, false);   
  4.     oXML.send('');   
  5.     eval(oXML.responseText);   
  6.     for(var i=0; i<functions.length; i++)   
  7.         window[functions[i]] = eval(functions[i]);   
  8. }  

 

 



    




The loadScript function expects two arguments: scriptpath and functions. "scriptpath" should contain the URL to your JavaScript file, and "functions" is the array of functions that exist in this JavaScript file.

As you can see, the code to pull and execute a script is straightforward. The browser first downloads, and then interprets the JavaScript file. If you've read any other articles on AJAX development, you might remember that in most cases the third argument sent to the open() function of an XMLHTTP object is usually "true." In our case we have it set to "false." This argument controls the state of the XMLHTTP object. If set to true, the object runs asynchrounously, meaning that all other JavaScript code continues while the object is loading. While this is a good thing in many circumstances, if we implemented it here our code would return before our file was done loading. Since we want our code to wait until this file is complete, we set this third argument to false, thus pausing our JavaScript execution until we are ready to continue.

When the code is evaluated from the responseText, it's executed in the limited scope of the loadScript function and because of this, none of these functions will be available outside of the loadScript function. In order do get around this, the for loop adds each function to the window object, thus making it globally available.

It's important to note that only scripts on the same server as the current page can be called in this manner. This is inherent to the XMLHTTP Object and is a necessary measure to increase general browser security.

 

Now that we have everything we need, we'll piece it together. We're going to create a "fake" function for each function that we want to auto-load. When these "fake" functions are called, they will load the real file and then re-execute the real function.

 

Js代码 复制代码 收藏代码
  1. for(var fileName in autoIncludeFunctions){   
  2.         var functionNames = autoIncludeFunctions[fileName];   
  3.         for(var i=0; i<functionNames.length; i++){   
  4.             var functionName = functionNames[i];   
  5.   
  6.             window[functionName] = function(){   
  7.                 loadScript(fileName, functionNames);   
  8.                 var execCode = functionName+'( ';   
  9.                 for(var i=0; i<arguments.length; i++) execCode+='arguments['+i+'],';   
  10.                 execCode     = execCode.substr(0, execCode.length-1)+')';   
  11.                 return eval(execCode);   
  12.             }   
  13.         }   
  14.     }  

 

 

 
The first part is relatively simple. We are looping through our list of files, one at a time, and then looping through each function in those files. Let's break down the inner code, so that it's a bit easier to understand:

Js代码 复制代码 收藏代码
  1. window[functionName] = function(){   
  2.     loadScript(fileName, functionNames);  

 

 


Here, we're using a global setting for our new "fake" function. We use the window object so that this function will be available throught the code. Inside of this function, we're calling the loadScript function that we previously created. After this is called, our fake global function is replaced with the real one, and we're halfway home. We now have to call the real function with all of the parameters passed to the fake one.

 

Js代码 复制代码 收藏代码
  1. var execCode = functionName+'( ';   
  2. for(var i=0; i<arguments.length; i++) execCode+='arguments['+i+'],';   
  3. execCode     = execCode.substr(0, execCode.length-1)+');';  

 

 


We now generate a string with which to call our real function. If you didn't already know, the arguments object contains all of the parameters passed into a function. We loop through the arguments object so that we can pass each and every parameter sent to this fake function on to the real one. At the end, this string will look something like "functionName(arguments[0], arguments[1], arguments[2]);".

      return eval(execCode);

Now that we have a string containing the proper call to our real function, we just have to execute it and we're done! Our real function has now replaced the fake one so that we don't have to go through all of this again, and it's been called. Any value that the real function would return gets sent to the original calling code as it should.
Things to Look Out For

If you are implementing this code, you might want to take the following into account:

   1. As briefly mentioned, any scripts that are loaded must be on the same server. Using this method, you can not load a script from www.some_other_site.com from www.your_site.com.
   2. When the auto load code is initiated, there will be a pause for the end user. Depending on the size of the file being downloaded, the speed of the user's connection and other variables, this pause can be anywhere from a few ms to a few seconds or more. During this time, the page will appear to be locked to your user. If you included this script the normal way, you would have the same delay, but it would occur when the page was loading.
   3. Any functions in a script file that are not defined in the autoIncludeFunctions variable will not be globally accessible. So if your file 'file1.js' has 20 functions in it, in the above example only three of them will be available for use. One solution to this is to define all of your functions in the javascript as "window.functionName = function(){" instead of "function functionName(){".
   4. This code has the potential to replace existing functions with the same names. You might consider adding a check for this by checking to see if window[functionName] before replacing it. If you don't do a function check, it's suggested that you run this code before including other js files, in case a function might be overridden.

If you want to see this concept in more detail, take a look at my general.js file. It's not implemented exactly as portrayed here, but it's fairly close, and is a bit more robust.
Conclusion

You now have the ability to access any JavaScript file you've ever created, all from one script. The examples in this article are straightforward, but it shouldn't be hard to modify this code to add validation to see if the file exists, prevent the code from overriding other functions, and any other features you may need.
About the Author

Mark Kahn is a Web Developer and DBA. Mark can be contacted through his website, http://www.jslibrary.org.