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.

Portrait de castrogne

Joli script! Attention

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();



Portrait de Loi Scellier 2011

Merci pour ce script. Je l'ai

Merci pour ce script.

Je l'ai modifié pour qu'il tourne sous IE6, et j'ai ajouté un patch à cause d'une erreur. Voir le code ci-dessous.

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/) {
    var len = this.length;var from = Number(arguments[1]) || 0;from=(from < 0)?Math.ceil(from):Math.floor(from);if (from < 0) from += len;
    for (; from < len; from++) {if (from in this && this[from] === elt) return from; } return -1;
  };
}
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' || this.readyState == 'complete') {
	  if(typeof(callback)=='function') eval('callback();'); // Patch: le dernier callback est de type 'Nan' ce qui cause une erreur
        }
      }
    } else {
      // Other browsers
      if(typeof(callback)=='function') { // Patch: cf ci-dessus
        e.onload = callback;
      }
    }
    document.getElementsByTagName("head")[0].appendChild(e);
  } else {
    if (forceCallback) {
      eval(callback); 
    }
  }
}
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]);
}

Poster un nouveau commentaire

Le contenu de ce champ ne sera pas montré publiquement. If you have a Gravatar account associated with the e-mail address you provide, it will be used to display your avatar.
  • Les adresses de pages web et de messagerie électronique sont transformées en liens automatiquement.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • You can enable syntax highlighting of source code with the following tags: <code>, <php>.

Plus d'informations sur les options de formatage

CAPTCHA
La vérification ne tient pas compte des minuscules ou des majuscules.
Image CAPTCHA
Enter the characters shown in the image.