Search code examples
javascriptjquerysinemacula

Delay a script from running until all assets have loaded


A Brief Explanation

This is not as basic as it sounds, so please do not skip to answering before you have read and understood what I am trying to do :-).

I have an object called SineMacula which contains a few base functions like so (ignore the ready function for the moment):

(function(global, $){

    // MOST CODE STRIPT OUT HERE */

    /**
     * Sine Macula Run
     * Makes it easy to write plugins for the Sine Macula library
     *
     * @param function callback
     */
    SM.run = run;
    function run(callback){
        // Call the function with the Sine Macula
        // and jQuery objects
        callback(SM, $);
    }

    /**
     * Sine Macula Ready
     * Executes code once the Sine Macula and jQuery
     * libraries are ready
     *
     * @param function callback
     */
    SM.ready = ready;
    function ready(callback){
        // Call the function with the Sine Macula
        // and jQuery objects
        jQuery(function($) {
            callback(SM, $);
        });
    }

    /**
     * Sine Macula Load
     * Load the Sine Macula Libraries and Plugins
     * into the current document
     *
     * The options:
     * - package: the package of libraries to load
     * - packageURL: a remote source to load the package details from
     * - libraries: any additional libraries to load
     *
     * @param object parameter The options for the Sine Macula load
     */
    SM.load = load;
    function load(options){
        // BUILD A QUERY HERE
        // ...
        // Complete the url by appending the query
        url = '//libraries.sinemaculammviii.com/'+query;        
        // Append the script tag to the end of the document
        script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url;
        $('head')[0].appendChild(script);       
    }
})(this, this.jQuery);

The load() function simply loads in all relevant plugins/libraries for the page by appending a relevant script tag to the head of the page.

All the scripts in the loaded pages are run as a callback to the function called run. This ensures that both jQuery and SineMacula are passed through to the plugins.

The Problem

This is where the issue lies, because the libraries are being loaded into the SineMacula object, there is no way to detect whether they have been loaded or not.

For example, if one of the libraries contains a function plugin called setDate(), and I run:

SM.setDate()

This will not necessarily work because the setDate() function may not have been loaded into the SineMacula object yet and therefore it will return an 'Uncaught TypeError...'.

Can anybody advise of a good adaptation to the SineMacula.ready() function to detect whether or not the libraries are present?

Please do not make suggestions to do with the jQuery.load() function, I am well aware of it.

I do not want the solution to be a callback of the function load(), unless absolutely necessary.

I hope this makes sense, if not let me know and I will post more info. Wanted to keep it as brief as possible.

Thanks in Advance

UPDATE

Forgot to mention that I have a testing page here where you can see the error I am getting and get a better understanding of what I am doing :-)


Solution

  • If I understand correctly, its the user that invokes SM.run() from within their script, which then calls SM.load(), which loads other parts of the library.

    So if that's the case, then no matter what, their script will finish executing before any other parts of the library can be loaded.

    I think what you'd need to do is have a requirement that the user have their .run() in a separate script from the rest of their code. Then you can use document.write to load the other scripts. This will cause them to load and block the next script, which would contain the rest of the user's code:

    function load(options){
        // BUILD A QUERY HERE
        // ...
        // Complete the url by appending the query
        document.write('<scr' + 'ipt type="text/javascript" ',
                                ' src="//libraries.sinemaculammviii.com/' + query,
                                '"><\/scr' + 'ipt>');      
    }
    

    <script type="text/javascript" src="/path/to/your/lib.js"></script>
    <script type="text/javascript">
        SineMacula.load('all');
    </script>
    
    <!-- The document.write will write the new script here, and it will be loaded
             syncronously, so it will block. -->
    
    <script type="text/javascript">
        // code that uses the loaded library parts
    </script>