Dynamically load a JavaScript file

You may create a script element dynamically, using Prototypes:

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});

The problem here is that we do not know when the external script file is fully loaded.

We often want our dependant code on the very next line and like to write something like:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}

There is a smart way to inject script dependencies without the need of callbacks. You simply have to pull the script via a synchronous AJAX request and eval the script on global level.

If you use Prototype the Script.load method looks like this:

var Script = {
    _loadedScripts: [],
    include: function(script) {
        // include script only once
        if (this._loadedScripts.include(script)) {
            return false;
        }
        // request file synchronous
        var code = new Ajax.Request(script, {
            asynchronous: false,
            method: "GET",
            evalJS: false,
            evalJSON: false
        }).transport.responseText;
        // eval code on global level
        if (Prototype.Browser.IE) {
            window.execScript(code);
        } else if (Prototype.Browser.WebKit) {
            $$("head").first().insert(Object.extend(
                new Element("script", {
                    type: "text/javascript"
                }), {
                    text: code
                }
            ));
        } else {
            window.eval(code);
        }
        // remember included script
        this._loadedScripts.push(script);
    }
};

I did basically the same thing that you did Adam, but with a slight modification to make sure I was appending to the head element to get the job done. I simply created an include function (code below) to handle both script and CSS files.

This function also checks to make sure that the script or CSS file hasn't already been loaded dynamically. It does not check for hand coded values and there may have been a better way to do that, but it served the purpose.

function include( url, type ){
    // First make sure it hasn't been loaded by something else.
    if( Array.contains( includedFile, url ) )
        return;
     
    // Determine the MIME type.
    var jsExpr = new RegExp( "js$", "i" );
    var cssExpr = new RegExp( "css$", "i" );
    if( type == null )
        if( jsExpr.test( url ) )
            type = 'text/javascript';
        else if( cssExpr.test( url ) )
            type = 'text/css';
            
    // Create the appropriate element.
    var element = null;
    switch( type ){
        case 'text/javascript' :
            element = document.createElement( 'script' );
            element.type = type;
            element.src = url;
            break;
        case 'text/css' :
            element = document.createElement( 'link' );
            element.rel = 'stylesheet';
            element.type = type;
            element.href = url;
            break;
    }
    
    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( element );
    Array.add( includedFile, url );
}

There is no import / include / require in javascript, but there are two main ways to achieve what you want:

1 - You can load it with an AJAX call then use eval.

This is the most straightforward way but it's limited to your domain because of the Javascript safety settings, and using eval is opening the door to bugs and hacks.

2 - Add a script element with the script URL in the HTML.

Definitely the best way to go. You can load the script even from a foreign server, and it's clean as you use the browser parser to evaluate the code. You can put the script element in the head element of the web page, or at the bottom of the body.

Both of these solutions are discussed and illustrated here.

Now, there is a big issue you must know about. Doing that implies that you remotely load the code. Modern web browsers will load the file and keep executing your current script because they load everything asynchronously to improve performances.

It means that if you use these tricks directly, you won't be able to use your newly loaded code the next line after you asked it to be loaded, because it will be still loading.

E.G : my_lovely_script.js contains MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Then you reload the page hitting F5. And it works! Confusing...

So what to do about it ?

Well, you can use the hack the author suggests in the link I gave you. In summary, for people in a hurry, he uses en event to run a callback function when the script is loaded. So you can put all the code using the remote library in the callback function. E.G :

function loadScript(url, callback)
{
    // adding the script element to the head as suggested before
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = url;

   // then bind the event to the callback function 
   // there are several events for cross browser compatibility
   script.onreadystatechange = callback;
   script.onload = callback;

   // fire the loading
   head.appendChild(script);
}

Then you write the code you want to use AFTER the script is loaded in a lambda function :

var myPrettyCode = function() {
    // here, do what ever you want
};

Then you run all that :

loadScript("my_lovely_script.js", myPrettyCode);

Ok, I got it. But it's a pain to write all this stuff.

Well, in that case, you can use as always the fantastic free jQuery framework, which let you do the very same thing in one line :

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});

I used a much less complicated version recently with jQuery:

<script src="scripts/jquery.js"></script>
<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  var $head = $("head");
  for (var i = 0; i < js.length; i++) {
    $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

It worked great in every browser I tested it in: IE6/7, Firefox, Safari, Opera.

Update: jQuery-less version:

<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  for (var i = 0, l = js.length; i < l; i++) {
    document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>