dynamically loading external JavaScripts with AJAX

I have a lot of JavaScript in my sites. In order to allow the user to view the site without having to download hundreds of K of scripts, though, I need to split the scripts up into many different files.

The files load upon certain conditions. For example, my multiselect code is only invoked when the document contains a <select> box, and my shopping cart code is only invoked when there is an element in the page with the class shoppingcart (btw: that’s a funky shopping cart, I think you’ll agree 😉 ).

So – I needed a function that could dynamically load the scripts that I need.

Up until today, I did this by adding a script element to the head of the document, but I found that this method crapped itself when I tried it in Konqueror.

I hit on the idea of loading up the code with AJAX, then using eval() to activate the code.

Here is the function I had:

var loadedScripts=[];
function loadExternalScript(url){
  if(loadedScripts[url])return;
  loadedScripts[url]=1;
  var r=new XMLHttpRequest();
  r.open('GET',url);
  r.onreadystatechange=function(){
    if(xhr_ready(r)){
      var script=r.responseText;
      eval(script);
    }
  }
  r.send(null);
}

I tried that out, with some alert()s placed in the external scripts to prove the files were being loaded, and that was fine.

Then I tried to run functions from those scripts… I got errors that they did not exist!

It turned out that the eval() from that function was running the code from the scripts with a scope local to that function – so when the context left the function, the external scripts were scrubbed from memory.

So – a quick think later, and I came up with this amendment:

function loadExternalScript(url){
  if(loadedScripts[url])return;
  loadedScripts[url]=1;
  var r=new XMLHttpRequest();
  r.open('GET',url);
  r.onreadystatechange=function(){
    if(xhr_ready(r)){
      var script=r.responseText.replace(/function ([^\( ]*)/g,'window.$1 = function');
      eval(script);
    }
  }
  r.send(null);
}

Note that this will affect all functions in the included script, including those which were intended to be used as class methods (I don’t do classes). Note also, that global variables will need to be addressed – but you get the idea.

2 Replies to “dynamically loading external JavaScripts with AJAX”

  1. Loading scripts dynamically by creating elements is something I have been doing for a few years now. Although it is functionally equivalent to eval, the results always going into the global context.

    Even better, although most sources indicates that the src attribute must point to a “.js” file, that is incorrect. The only requirement is that the resulting data stream returned be interpretable JavaScript. This allows you to do something like:

    src=”queryByLastName.php?name=Smith”

    And have the results:

    result = { last: Smith, first: John, phone: “800-555-1212” };

    If result is pre-declared as a global, you have a two-way communications channel back to the server without page refresh, XmlHttpRequest, or ActiveX. No ugly either.

Comments are closed.