On Demand Script Loading

This code will return true while it’s waiting to load the external script – and if called again (i.e. at a later date or if you’ve got an excited user) will know that the script has already been loaded.

It allows for scripts to be loaded on demand without the use of an external library such as Dojo to allow for imports.

It also caches the function that requested the script to be loaded, to then execute it again when it’s ready.

Note: the code does support the calling function to have parameters – it uses the arguments.callee.caller.arguments to pass the args back in.

/**
* Only returns true when the external script has been
* loaded in to the DOM.  It uses arguments.callee.caller
* to work out which function is the callback.
*
* @param url {String} URL of external script
* @param obj {String} The name of a function or variable
* within the external script to test for.
* @license: Creative Commons License - 
* ShareAlike http://creativecommons.org/licenses/by-sa/3.0/
* @author Remy Sharp / leftlogic.com
*/
function waitingForScript(url, obj) {
  // doesn't work in Opera
  var callback = arguments.callee.caller;
  var args = arguments.callee.caller.arguments;
  var s, ok, timer, doc = document;

  // if the object/function doesn't exist and we've not tried to load it
  // then pull it in and fire the calling function once complete
  if ((typeof window[obj] == 'undefined') && !window['loading' + obj]) {
    window['loading' + obj] = true;

    if (!doc.getElementById('_' + obj)) {
      s = doc.createElement('script');
      s.src = url;
      s.id = '_' + obj;
      doc.body.appendChild(s);
    }

    timer = setInterval(function () {
      ok = false;
      try { 
        ok = (typeof window[obj] != 'undefined');
      } catch (e) {}

      if (ok) {
        clearInterval(timer);
        callback.apply(this);
      }
    }, 10);

    // we're loading in the script now, so we're currently waiting
    return true;
  } else if (typeof window[obj] == 'undefined') {
    // object not defined yet, so we're still waiting
    return true;
  } else {
    // it's already loaded
    return false;
  }
}

Usage

function MyFunction() {
  console.log('Testing whether jQuery is loaded (' + !!(typeof jQuery == 'function') + ')');
  if (waitingForScript('http://jquery.com/src/jquery-latest.js', 'jQuery')) return;
  console.log('Do some action with jQuery');
}

MyFunction("arg1", { "arg" : "two" });

9 Responses to “On Demand Script Loading”

  1. Hi,

    this is a nice concept you developed.

    Coincidentally I developed something similar about two weeks ago. Though it works slightly different in that it is supposed to load a number of scripts based on dependancy without having to care about urls.

    I’d also argue that your code is a bit hard to read, especially the last if else block.

    Other than that, great work and interesting concept.

  2. Dynamically loading external Javscript Files

    Some weeks ago I read a short tutorial over at JavaScript Kit about how to load JavaScript files into your document on demand. This was a thought that I already pursued for some time as I had seen something similar already in the Scriptaculous Library….

  3. This isn’t the same technique as the link your provided.

    What you read at JavaScript Kit was how to inject a script in to the DOM. This is simple stuff.

    What the code above does, that’s fundamentally different is that it doesn’t proceed with anything until that script (or module) is in place.

    I know that Dojo have some kind of ‘import’ method for script, and I suspect this is done using synchronous Ajax requests (i.e. ones that block) (I could well be wrong) – but this script does that job over any domain.

  4. True, I should have pointed out the difference in your approach a little bit more. Especially the callback part.

    But if you read it you might have noticed that I also stated

    I stumbled upon a great concept today that takes the initial idea a bit farther.

    Though writing that I was working on the same idea was poor wording on my end.

    Long story short…
    This is great concept and I’d like to create a synergy between the two approaches and to improve upon the idea.

    Keep up the good work here.

  5. Remy, how did you manage to format the code in this post that way? (i mean non-pre one)

  6. @Yansky – I’ve putting together a short post that explains a bunch of different ways to get this done. Should be the next few days.

  7. Hi,

    this post saved me some time – good approach and done nicely. Thanks

  8. Not sure why you would write this function in such a complex way, but I see essentially it tests for the existence of an object. What if the script if partially loaded, and the object is defined, yet the script has not finished loading? Is this possible?

  9. @Peru – it’s probably more complicated than you expect because we have to find a way to block in JavaScript until the object is loaded. The way I’m doing this is with a timeout, but the single executed thread continues – so we’re capturing the calling function and arguments to recall it later on.

    What if the script if partially loaded, and the object is defined, yet the script has not finished loading? Is this possible?

    Since JavaScript is single threaded, this won’t happen. Once the object starts loading, it won’t release resource to run some other script until it’s finished. So, depending on how the object loading script is written (i.e. it doesn’t contain timeouts to load data later on), the object will always be completely loaded.

Leave a Reply
Not required

CODE: Please escape code and wrap in <pre><code>, doing so will automatically syntax highlight