Chargement dynamique de plusieurs scripts Javascript

Avec Ajax, on se retrouve rapidement avec des scripts Javascript volumineux. Pour accélérer le chargement des pages, on peut charger les scripts à la demande.

C'est assez simple à faire avec le DOM. Il suffit d'ajouter une balise script à l'entête de la page. Pour éviter de charger deux fois le même fichier, chaque URL est stockée dans un tableau. Si l'adresse du script existe déjà dans ce dernier, le chargement ne s'effectue pas.

Pour éviter des problèmes avec le cache, j'ajoute un paramètre aléatoire à l'URL.

La fonction permet également d'appeler une fonction de rappel qui sera appelée lorsque le script sera chargé. Firefox et les autres navigateurs fournissent un événement onload qui peut être utilisé. Pour Internet Explorer, on peut utiliser la propriété onReadyStateChange.

/**
 * Load one script
 * @param {String} url Url of the script
 * @param {Function} callback This function will be called when the script will be loaded.
 * @param {boolean} forceCallback If true, call the function even if the script is already loaded. Default to false.
 */
 
function loadScript(url, callback, forceCallback) {
  if (!this.loadedScript) {
    this.loadedScript = new Array();
  }
  if (this.loadedScript.indexOf(url) == -1) {
    this.loadedScript.push(url);
    var e = document.createElement("script");
    e.src = url + "?" + Math.random();
    e.type = "text/javascript";
    if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
      // IE
      e.onreadystatechange = function(){
        if (this.readyState == 'loaded') {
          callback();
        }
      }
    } else {
      // Other browsers
      e.onload = callback;
    }
    document.getElementsByTagName("head")[0].appendChild(e);
  } else {
    if (forceCallback) {
      callback();
    }
  }
}

Ça fonctionne bien, mais pour pouvoir répartir mon code sur plusieurs fichiers, j'avais besoin d'une fonction capable de charger de façon séquentielle plusieurs scripts. Des appels successifs à loadScript n'auraient pas fait l'affaire, car le chargement se fait de façon asynchrone. L'ordre des scripts n'aurait pas forcément été respecté, ce qui est génant si les fichiers sont dépendants les uns des autres.

J'ai donc écrit la fonction suivante, qui charge les fichiers les uns après les autres. Le principe est simple, mais pas évident à traduire en code : on charge le premier fichier avec une fonction de rappel qui charge le fichier suivant, et ainsi de suite. Pour la fonction createCallback, je me suis inspiré de celle existant dans ExtJS

/**
 * Load several scripts
 * @param {Array} scripts Exemple: ["script1.js", "script2js"]
 * @param {Function} callback This function will be called when the script will be loaded.
 */
 
function loadScripts(scripts, callback){
  function createCallback(method, args){
    return function() {
      return method.apply(window, args);
    };
  }
  var callbacks = new Array(createCallback(loadScript, [scripts[scripts.length - 1], callback]));
  for (var i = scripts.length - 2; i > 0; i--) {
    callbacks.unshift(createCallback(loadScript, [scripts[i], callbacks[0], true]));
  }
  loadScript(scripts[0], callbacks[0]);
}

Vous pouvez voir un exemple d'utilisation de la fonction ici.

Comments

Joli script!

Attention toutefois, l'appel d'une fonction passée en paramètre passe mal sur IE6 (pas testé sur 7).

Préférer donc : eval(callback); à callback();

Add new comment