0

I have made a namespaces framework for javascript. I am loading some plugins (.js files) which are dynamically added to the HTML.

I am going to try to simplify the code.

This function is used to dinamically load a JS. The callback function is called after .js file has been loaded. Consider that the following code has already been run.

MYNAMESPACE.plugins = ["plugin1", "plugin2"];
MYNAMESPACE.getJS = {
    get: function (url, callback) {
        var script = document.createElement("script");
        var head = document.getElementsByTagName('head')[0];
        script.type = "text/javascript";
        script.src = url;
        head.insertBefore(script, head.firstChild)
        script.onload = callback;
        script.onreadystate = callback;
        return script;
        }
};

I have a init function that loads the plugins contained in MYNAMESPACE.plugins as follows:

MYNAMESPACE.init = function (callback) {
    for (index in MYNAMESPACE.plugins) {
        plugin = MYNAMESPACE.plugins[index];
        MYNAMESPACE.getJS.get(plugin + '.js', function ()
         {
              // This callback is executed when the js file is loaded
         });
    }
    // Here I want to execute callback function, but after all the callbacks in the for loop have been executed.  Something like: if (alljsloaded) callback();
}

In my HTML I have the following script tag:

<html>
    <head>
    <script type="text/javascript">
        $(document).ready(function () {
            MYNAMESPACE.init(); 

            // The following line is not executed correctlybecause init finished before the scripts are loaded and the functionOnPlugin1 is undefined.
            MYNAMESPACE.functionOnPlugin1();
        });
    </script>
    </head>
    <body>
    </body>
</html>

And I want to change it for something like this:

<html>
    <head>
    <script type="text/javascript">
        $(document).ready(function () {
            MYNAMESPACE.init(function() { MYNAMESPACE.functionOnPlugin1(); });  
        });
    </script>
    </head>
    <body>
    </body>
</html>

But I don't know how to modify the function MYNAMESPACE.init() so it executes the callback after ALL the plugin scripts are loaded.

Any ideas? Maybe using closures.

4

2 に答える 2

3
  1. Use for(;;) to loop through an array, not for(.. in ..).
  2. Create a counter in the function, increment it every time that a script has loaded. When it has reached the number of scripts, invoke the callback function.

Code:

MYNAMESPACE.init = function (callback) {
    var allPluginsLength = MYNAMESPACE.plugins.length;
    var loadedCount = 0;
    if (allPluginsLength === 0) callback(); // No plugins, invoke callback.
    else for (var index=0; index<allPluginsLength; i++) {
        plugin = MYNAMESPACE.plugins[index];
        MYNAMESPACE.getJS.get(plugin + '.js', function () {
              // This callback is executed when the js file is loaded
              if (++loadedCount == allPluginsLength) {
                  callback();
              }
         });
    }
}

Also, change your getJS.get method. Currently, callback always executes when a state changes, which is not desirable. To prevent the callback from being executed twice, the following can be used:

    script.onload = function() {
        callback && callback();
        callback = false;
    };
    // onreadystatechange instead of onreadystate, btw.
    script.onreadystatechange = function() {
        if (this.readyState == 'complete') {
            callback && callback();
            callback = false;
        }
    };
  • callback && callback() is used, to ensure that a callback exists. If it does, it's then invoked.
  • After invoking the callback, it's immediately removed (callback = null).
    This is necessary, because the onload and onreadystatechange events can fire when the script has loaded. Without my suggested code, the callback will be called multiple times for a single script.
于 2012-03-22T17:56:41.507 に答える
1

Some changes like this should work:

MYNAMESPACE.init = function (callback) {
    var remaining = MYNAMESPACE.plugins.length;
    for (index in MYNAMESPACE.plugins) {
        plugin = MYNAMESPACE.plugins[index];
        MYNAMESPACE.getJS.get(plugin + '.js', function ()
         {
              // This callback is executed when the js file is loaded
              remaining = remaining - 1;
              if (remaining === 0)
              {
                  callback();
              }
         });
    }
}
于 2012-03-22T17:59:04.517 に答える