Supplementary

Three Functions for Loading Functions, Object Definitions, and CSS Rules Dynamically

loadJS_STH is a small function that retrieves JavaScript code, given an appropriate URL, and is an implementation of the technique known as 'On-Demand JavaScript' or 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 (although it does not use the eval function).

Do note that, while convenient, this technique represents a potential security weakness in that allows the download of code from any domain. Given that all code runs in the browser with the same privileges, including access to cookies, downloading from a non-originating domain is secure only if the trustworthiness of the downloaded code is guaranteed.

Using loadJS_STH
Race Conditions
Using an EventMarshaller

Product Version: 1.1
Tutorial Version: 1.2
API Doc. Version: 2.0

Using loadJS_STH

Using loadJS_STH is a simple matter of calling the function, passing it the URL of the desired code as a string; and Example 1 (a somewhat contrived demonstration) illustrates this.

Here, clicking on a button causes the onClick event handler to execute, which calls loadJS_STH. That function then downloads one of the three .js files shown. Once the file has been retrieved the function it contains is executed, and this displays a quote from a film or book.


 <!-- Example 1 -->

 <html>
    <head>
       <script type = "text/javascript" src = "loadJS_STH.js"></script>
       <script type = "text/javascript">

       // Contents of the three .JS files shown separately

       function onClick (FileName)
          {
          loadJS_STH (FileName + ".js");
          }

       </script>

       <title></title>

    </head>

    <body>

       <h3>loadJS_STH Tutorial - Example 01</h3>

       <p>Click a Button to Display a Quote</p>

       <input type = "button" value = "Alien"            onclick = "onClick ('Alien'         )"/>
       <input type = "button" value = "Spinal Tap"       onclick = "onClick ('Spinal_Tap'    )"/>
       <input type = "button" value = "Childhood's End"  onclick = "onClick ('Childhoods_End')"/>

    </body>
 </html>
            

Race Conditions

Note that on-demand JavaScript operates asynchronously. This means that a call to loadJS_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 loadJS_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 the fact that it resides at global scope means that executes automatically.

This approach is illustrated in the three .js files that Example 1 uses, the contents of which are given in the listing. Each defines a function, and then makes a call to that function.


 //- Contents of Alien.js -----------------------------------------------

 function Display_Alien_Quote ()
    {
    alert ("I admire its purity, its sense of survival; "
         + "unclouded by conscience, remorse, or "
         + "delusions of morality");
    }

 Display_Alien_Quote ();


 //- Contents of Spinal_Tap.js ------------------------------------------

 function Display_SpinalTap_Quote ()
    {
    alert ("I do not, for one, think that the problem was that the band was down."
        +  "I think that the problem may have been that there was a Stonehenge "
        +  "monument on the stage that was in danger of being crushed by a dwarf.");
    }

 Display_SpinalTap_Quote ();


 //- Contents of Childhoods_End.js --------------------------------------

 function Display_ChildhoodsEnd_Quote ()
    {
    alert ("Jan had always been a good pianist "
         + "- and now he was the finest in the world");
    }

 Display_ChildhoodsEnd_Quote ();
            

Using an Event Marshaller

While this approach works with the retrieval of single libraries, extending the scenario to load multiple code-bodies using loadJS_STH proffers no guarantee over the order in which the requested code will arrive. In other words, and unlike loadJS_XHR, loadJS_STH operates asynchronously.

Given this, and if using the proprietary synchronisation mechanism outlined above, do be sure that a global 'end-of-file' function-call in one library does not attempt to call a method in another library that has been requested at essentially the same time - in essence, the synchronisation-onus lies with the caller(s) of loadJS_STH.

This may require complex logic to resolve, therefore an alternative is to use the Event Marshaller library that is also available from this site, and which has its own overview, tutorial and API documentation. Using this library, a call to the createEventMarshaller function will return an EventMarshaller object - a prioritised queueing mechanism, to which 'events' can be posted.

Each event is added to the marshaller's queue as a reference to a 'response' function, and when the number of events posted reaches a user-defined limit, the marshaller will dispatch each response (i.e. call the function on the end of each reference) according to the priority (if any) that has been assigned to each event.

In the context of loadLib_STH, Example 2 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 EventMarshaller 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 respectively.


 // Example 2

 // Assume EventMarshaller.js is imported using a script tag

 var Marshaller = createEventMarshaller (2);

 function LoadLibs ()
    {
    loadJS_STH ("AmericanPsycho_2.js"); // Second lib is loaded first deliberately
    loadJS_STH ("AmericanPsycho_1.js");
    }

 --------------------------------------

 // AmericanPsycho_1.js

 function DisplayFirstPart ()
    {
    alert ("Patrick Bateman: 'When I see a pretty girl walking down the street, "
         + "I think two things. One part wants me to be real nice and sweet and "
         + "treat her right.'");

    alert ("David Van Patten: 'And what does the other part think?'");

    }

 Marshaller.postEvent (DisplayFirstPart, 1);

 --------------------------------------

 // AmericanPsycho_2.js

 function DisplaySecondPart ()
    {
    alert ("Patrick Bateman: 'What her head would look like on a stick...'");
    }

 Marshaller.postEvent (DisplaySecondPart, 0);

 --------------------------------------

 Output:

 Patrick Bateman: 'When I see a pretty girl walking down the street,
 I think two things. One part wants me to be real nice and sweet and
 treat her right.'

 David Van Patten: 'And what does the other part think?'

 Patrick Bateman: 'What her head would look like on a stick...'