Full of lovely web standards information and a large helping of PHP

Dynamically loading JS libraries and detecting when they're loaded

1 year, 2 days ago

Sometimes it makes sense to dynamically load JavaScript library code only at the point at which it's required. If functionality which uses that library is used rarely then it makes little sense incur the overhead of an additional HTTP request or data transfer on page load.

We can dynamically load any JavaScript and add it to the document with the following function.

Making a dynamic request

  1. function loadScript(sScriptSrc) {
  2. var oHead = document.getElementById('head')[0];
  3. var oScript = document.createElement('script');
  4. oScript.type = 'text/javascript';
  5. oScript.src = sScriptSrc;
  6. oHead.appendChild(oScript);
  7. }

This approach isn't without problems though. The request will load asynchronously which means that any JavaScript which depends on code returned by the request may continue to run before the request completes resulting in errors.

Callback event handlers

As described in far greater detail in JavaScript Madness: Dynamic Script Loading browsers provide the ability to specify a callback function to run once the script load completes. In most it's as simple as assigning a callback function to the script.onload event handler.

  1. oScript.onload = callback;

Internet Explorer, as in so many cases, does things differently. It supports a onreadystatechange event handler to which we can assign a callback function. It's slightly more complicated than that used by other browsers as one has to check a return status before running the callback.

  1. oScript.onreadystatechange = function() {
  2. if (this.readyState == 'complete') {
  3. callback();
  4. }
  5. }

Combining the parts gives us:

  1. function loadScript(sScriptSrc, oCallback) {
  2. var oHead = document.getElementById('head')[0];
  3. var oScript = document.createElement('script');
  4. oScript.type = 'text/javascript';
  5. oScript.src = sScriptSrc;
  6. // most browsers
  7. oScript.onload = oCallback;
  8. // IE 6 & 7
  9. oScript.onreadystatechange = function() {
  10. if (this.readyState == 'complete') {
  11. oCallback();
  12. }
  13. }
  14. oHead.appendChild(oScript);
  15. }

Detecting script load with timeouts

Reading a number of sources however it seems that there is a lack of trust in the cross browser robustness of these event handlers. One could instead use timeouts to poll for the existence of required functions returned by the script request and run our callback once detected.

I wrote the following function to achieve just this recently. The function takes two main parameters, a string representing the function to poll for and a reference to the callback function to run once it's found.

  1. function onFunctionAvailable(sMethod, oCallback, oObject, bScope) {
  2. if (typeof(eval(sMethod)) === 'function') {
  3. bScope ? oCallback.call(oObject) : oCallback(oObject);
  4. } else {
  5. setTimeout(function () {
  6. onFunctionAvailable(sMethod, oCallback, oObject, bScope);
  7. }), 50
  8. }
  9. }

Two additional parameters allow one to define the scope of "this" within the callback function.

The hardest part of this function was working out how to detect the existence of a function from a string. Fortunately there's no shortage of people I can turn to with JavaScript skills better than my own. Thanks to Christian, Stuart and Andrew for their input.

Further Reading

Comments

  • Hey Ed - I was trying to implement a version of onFunctionAvailable but having some trouble getting it to work in Safari. Perhaps my edits are causing the problem. I was a little confused on what the parameter oObject and bScope represent and removed them. Any chance you'd give an example of how to call your function? Thanks in advance, I found your blog helpful. btw - here is my cutdown version: --- function onFunctionAvailable(sMethod) { var a = sMethod.split('('); if( a.length < 1 ) { return; } var funcName = a[0];

    if( typeof(eval(funcName)) === 'function' ) { eval(sMethod); } else { setTimeout( function () { onFunctionAvailable(sMethod ); } ), 1000 }

    }

    Jordan Catalano - 2nd November 2007

  • ejeliot,

    you saved me lots of heartache. much appreciated.

    danny - 12th November 2007

  • An example will be appeciated too :)

    NASDAK - 2nd December 2007

  • Nice! I wasn't aware of the onload/readystatechange properties of the script object. BTW from what I've read eval is considered a security risk and you are going ot want to use square bracket syntaxt to detect that function - if( typeof( window[funcName] )

    Ward - 2nd December 2007

  • Hi, have you tried to detect if an error ocurred while loading the script?

    I wrote the following script, works in Firefox but not in IE:

    var resourceElement = document.createElement('script'); [... etc etc ...]

    resourceElement.onerror = function(message) { alert(message); };

    For IE, the only thing I tried is using window.onerror , but then I can't identify which script failed. Anyway it works if you just want to know if *any* of the required scripts failed, but with a really poor level of accuracy.

    I'd appreciate any help or thoughts you may share. Better if you write an article about handling dynamic JS loading errors. :)

    Thanks,

    LK

    lufublico - 19th December 2007

  • Thanks for the sum up. Very good information in here.

    One question though: Wouldn't it be sufficient if you simply call the callback function from your (dynamically loaded) JavaScript file? This should work at least as long as you have control over that file.

    Till - 8th January 2008

  • it worked very well I think this: var oHead = document.getElementById('head')[0];

    to be replaced as var oHead = document.getElementsByTagName('head')[0];

    anyway fine.

    Web Tips in Javascript

    Raja - 13th June 2008

  • In IE 7:

    this.readyState

    was 'loading' subsequently 'loaded'.

    I changed:

    if (this.readyState == 'complete')

    to:

    if (this.readyState == 'loaded' || this.readyState == 'complete')

    Maarten Maarten

    Maarten - 26th June 2008

Help make this post better

Notes: Standard BBCode for links, bold, italic and code are supported. rel="nofollow" is added to all links. Your email address will never be displayed on the site.

Back to index