Supplementary - On-Demand Loaders
Three Functions for Loading JavaScript and CSS Code Dynamically
T
ypically, web pages import JavaScript code statically by means of script elements within the head element. However, code can also be loaded dynamically, either by inserting script elements explicitly into the page's DOM hierarchy, or by using XMLHTTPRequest. These approaches are far more efficient, as applications need import code only when required, thus consuming bandwidth and platform resources only when necessary, and this page presents a small function that automates the DOM-hierarchy technique.
Critically, this function can be used in conjunction with AspectJS, and this is significant because the intersection of the two technologies permits library downloads on demand but entirely transparently at the point of need. This is explored in the AspectJS Applications page entitled On-Demand Loading.
Contents
LoadLib_STH
LoadLib_STH is presented in Listing One. It retrieves JavaScript code, given an appropriate URL, and is an implementation of the 'Script-Tag Hack' (hence the 'STH' suffix).
When called it creates a <script> node dynamically, and sets its href attribute to the value of the URL supplied by the caller. It then inserts the script element into the DOM hierarchy for the page, which causes the browser to download and evaluate the corresponding JavaScript code, thus introducing that code's functionality into the execution environment.

 // Listing 1

 function LoadLib_STH (LibURL)
    {
    var ScriptElem = document.createElement ("script");

    ScriptElem.setAttribute         ("src",   LibURL);
    ScriptElem.setAttribute         ("type", "text/javascript");

    document  .getElementsByTagName ("head")[0].appendChild (ScriptElem);

    }
            
Using LoadLib_STH
As with LoadStyleSheet, using LoadLib_STH is a simple matter of calling the function, passing it the URL of the desired code as a string; and Example One illustrates this. Here, clicking on the client area of the browser window causes the OnClick event handler to execute, which calls LoadLib_STH before continuing.

 <!-- Example 1 - Using LoadLib_STH -->

 <html>
    <head>
       <script type = "text/javascript">

       function LoadLib_STH (LibURL) { ... }
       function OnClick ()
          {
          LoadLib_STH ("MyLib.js");

          ...

          }

       </script>
    </head>

    <body onclick = "OnClick ()"> ... </body>

 </html>
            
Race Conditions
Note that, while this technique allows code to be downloaded from any domain (which constitutes a security flaw in JavaScript), it is asynchronous. As with LoadStyleSheet, this means that a call to LoadLib_STH may return before the requested code has been delivered, therefore client code should not call functions or access data contained within a given library until that library has been downloaded successfully.
A proprietary mechanism may be necessary to ensure this, as JavaScript does not support a synchronisation mechanism natively, and the simplest approach is to place a method call at global scope at the end of each library that LoadLib_STH retrieves. The function that is called depends upon the nature of the application, but the essential point is that the endmost position of the call means that its execution is contingent upon the rest of the code having been evaluated first, and its global scope-level means that it is executed automatically.
However, and to extend the scenario, a call to load multiple JavaScript libraries through LoadLib_STH proffers no guarantee over the order in which the requested code will arrive. Given this, and if using the proprietary synchronisation mechanism outlined above, do be sure that the global 'end-of-file' function call does not attempt to call a method in one of the other libraries that are being loaded. The corrollary to this is that the synchronisation-onus lies with the caller(s) of LoadLib_STH.
An alternative, however to using proprietary solution is to use the CreateEventMarshaller library function provided on this site. A call to this function will return an EventMarshaller object to which 'events' can be posted such that an 'action' (a reference to a function) is associated with a given event. When the number of events posted to a given marshaller reaches a user-defined limit, the marshaller will dispatch each action (i.e. call the function on the end of each reference) in the order of any priorities that have been assigned to those events.
Example Two illustrates these concepts in action. Here two libraries are depicted that, once loaded, will each call a function in the main code-body. Instead of attempting to invoke the methods in the respective libraries, these two functions each post an event to an event marshaller that has a queue size of two.
This approach means that it is immaterial which library is loaded first, as the code that makes use of their respective objects and functions is executed only once the other has loaded. Moreover, the correct execution order - where Lib2 code must execute before Lib1 code - is guaranteed because the loading events are assigned a priority of one and zero.

 // Example 2 - Using an Event Marshaller

 function CreateEventMarshaller () { ... }

 var Marshaller = CreateEventMarshaller (2);

 function OnLib1_Loaded () { Marshaller.PostEvent (InvokeLib1Funcs, 0); }
 function OnLib2_Loaded () { Marshaller.PostEvent (InvokeLib2Funcs, 1); }

 function InvokeLib1Funcs ()
    {
    // Safe to call functions in Lib1
    // here as InvokeLib2Funcs is
    // guaranteed to have executed
    }

 function InvokeLib2Funcs ()
    {
    // Call functions in Lib2
    }

 --------------------------------------
 // Lib 1

 // Code that is depend...

 OnLib1_Loaded ();

 --------------------------------------
 // Lib 2

 // Code...

 OnLib2_Loaded ();

            
Copyright © Dodeca Technologies Ltd. 2007