web-dev-qa-db-fra.com

Comment inclure plusieurs fichiers js à l'aide de la méthode jQuery $ .getScript ()

J'essaie d'inclure dynamiquement des fichiers javascript dans mon fichier js. J'ai fait des recherches à ce sujet et trouver la méthode jQuery $ .getScript () serait un moyen souhaitable.

// jQuery
$.getScript('/path/to/imported/script.js', function()
{
    // script is now loaded and executed.
    // put your dependent JS here.
    // what if the JS code is dependent on multiple JS files? 
});

Mais je me demande si cette méthode peut charger plusieurs scripts à la fois? La raison pour laquelle je pose cette question est que mon fichier javascript dépend parfois de plusieurs fichiers js. 

Merci d'avance. 

111
sozhen

La réponse est

Vous pouvez utiliser les promesses avec getScript() et attendre que tous les scripts soient chargés, quelque chose comme:

$.when(
    $.getScript( "/mypath/myscript1.js" ),
    $.getScript( "/mypath/myscript2.js" ),
    $.getScript( "/mypath/myscript3.js" ),
    $.Deferred(function( deferred ){
        $( deferred.resolve );
    })
).done(function(){

    //place your code here, the scripts are all loaded

});

FIDDLE

UN AUTRE FIDDLE

Dans le code ci-dessus, ajouter un différé et le résoudre dans $() revient à placer toute autre fonction dans un appel jQuery, comme $(func), c'est la même chose que 

$(function() { func(); });

c'est-à-dire qu'il attend que le DOM soit prêt. Ainsi, dans l'exemple ci-dessus, $.when attend que tous les scripts soient chargés et que le DOM soit prêt en raison de l'appel $.Deferred qui se résout dans le rappel prêt DOM.


Pour une utilisation plus générique, une fonction pratique

Une fonction utilitaire acceptant n'importe quel tableau de scripts pourrait être créée comme ceci:

$.getMultiScripts = function(arr, path) {
    var _arr = $.map(arr, function(scr) {
        return $.getScript( (path||"") + scr );
    });

    _arr.Push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));

    return $.when.apply($, _arr);
}

qui peut être utilisé comme ça

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});

où le chemin sera préfixé à tous les scripts, et est également facultatif, ce qui signifie que si le tableau contient l'URL complète, on peut également le faire, et laisser le chemin en même temps.

$.getMultiScripts(script_arr).done(function() { ...

Arguments, erreurs, etc.

De plus, notez que le callback done contiendra un nombre d'arguments correspondant aux scripts passés dans les scripts, chaque argument représentant un tableau contenant la réponse.

$.getMultiScripts(script_arr).done(function(response1, response2, response3) { ...

où chaque tableau contiendra quelque chose comme [content_of_file_loaded, status, xhr_object]. Nous n'avons généralement pas besoin d'accéder à ces arguments car les scripts seront chargés automatiquement de toute façon, et la plupart du temps, le callback done est tout ce que nous cherchons vraiment à savoir. les scripts ont été chargés, je les rajoute par souci d'exhaustivité, et dans les rares cas où il est nécessaire d'accéder au texte du fichier chargé ou lorsqu'il est nécessaire d'accéder à chaque objet XHR ou à un élément similaire.

De plus, si l'un des scripts ne se charge pas, le gestionnaire d'échec sera appelé et les scripts suivants ne seront pas chargés.

$.getMultiScripts(script_arr).done(function() {
     // all done
}).fail(function(error) {
     // one or more scripts failed to load
}).always(function() {
     // always called, both on success and error
});
284
adeneo

J'ai implémenté une fonction simple pour charger plusieurs scripts en parallèle:

Une fonction

function getScripts(scripts, callback) {
    var progress = 0;
    scripts.forEach(function(script) { 
        $.getScript(script, function () {
            if (++progress == scripts.length) callback();
        }); 
    });
}

Usage

getScripts(["script1.js", "script2.js"], function () {
    // do something...
});
23
Andrei

Chargez le script nécessaire suivant dans le rappel du précédent comme:

$.getScript('scripta.js', function()
{
   $.getScript('scriptb.js', function()
   {
       // run script that depends on scripta.js and scriptb.js
   });
});
9
Frankey

Parfois, il est nécessaire de charger les scripts dans un ordre spécifique. Par exemple, jQuery doit être chargé avant l'interface utilisateur jQuery. La plupart des exemples de cette page chargent des scripts en parallèle (de manière asynchrone), ce qui signifie que l'ordre d'exécution n'est pas garanti. Sans commande, le script y qui dépend de x pourrait se rompre si les deux sont chargés avec succès mais dans un ordre incorrect.

Je propose une approche hybride permettant le chargement séquentiel de scripts dépendants + chargement parallèle optionnel + objets différés :

/*
 * loads scripts one-by-one using recursion
 * returns jQuery.Deferred
 */
function loadScripts(scripts) {
  var deferred = jQuery.Deferred();

  function loadScript(i) {
    if (i < scripts.length) {
      jQuery.ajax({
        url: scripts[i],
        dataType: "script",
        cache: true,
        success: function() {
          loadScript(i + 1);
        }
      });
    } else {
      deferred.resolve();
    }
  }
  loadScript(0);

  return deferred;
}

/*
 * example using serial and parallel download together
 */

// queue #1 - jquery ui and jquery ui i18n files
var d1 = loadScripts([
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js",
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/i18n/jquery-ui-i18n.min.js"
]).done(function() {
  jQuery("#datepicker1").datepicker(jQuery.datepicker.regional.fr);
});

// queue #2 - jquery cycle2 plugin and tile effect plugin
var d2 = loadScripts([
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/jquery.cycle2.min.js",
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/plugin/jquery.cycle2.tile.min.js"

]).done(function() {
  jQuery("#slideshow1").cycle({
    fx: "tileBlind",
    log: false
  });
});

// trigger a callback when all queues are complete
jQuery.when(d1, d2).done(function() {
  console.log("All scripts loaded");
});
@import url("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/blitzer/jquery-ui.min.css");

#slideshow1 {
  position: relative;
  z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<p><input id="datepicker1"></p>

<div id="slideshow1">
  <img src="https://dummyimage.com/300x100/FC0/000">
  <img src="https://dummyimage.com/300x100/0CF/000">
  <img src="https://dummyimage.com/300x100/CF0/000">
</div>

Les scripts des deux files d'attente seront téléchargés en parallèle. Cependant, les scripts de chaque file d'attente seront téléchargés dans l'ordre, assurant ainsi l'exécution ordonnée. Carte cascade:

 waterfall chart of scripts

8
Salman A

Utilisez yepnope.js ou Modernizr (qui inclut yepnope.js en tant que Modernizr.load).

METTRE &AGRAVE; JOUR

Pour faire un suivi, voici un bon équivalent de ce que vous avez actuellement avec yepnope, montrant les dépendances de plusieurs scripts:

yepnope({
  load: ['script1.js', 'script2.js', 'script3.js'],
  complete: function () {
      // all the scripts have loaded, do whatever you want here
  }
});
4
Chris Pratt

J'ai rencontré un certain nombre de problèmes avec le chargement de plusieurs scripts, dont un (avec Chrome), le chargement à chaud du même domaine de scripts ne fonctionnant pas après le chargement réussi par Ajax où Cross Domain fonctionne parfaitement! :(

La réponse sélectionnée à la question initiale ne fonctionne pas de manière fiable.

Après de nombreuses itérations, voici ma réponse finale à getScript (s) et au chargement asynchrone de plusieurs scripts dans un ordre strict spécifique avec une option de rappel chargée par script et un rappel global à la fin. Testé dans jQuery 2.1+ et les versions modernes de Chrome, Firefox plus la abandonné Internet Explorer.

Mon cas de test consistait à charger des fichiers pour un rendu web THREE.JS puis à lancer le script de rendu lorsque THREE global est devenu disponible à l'aide d'une vérification d'intervalle passée à un appel de fonction anonyme à onComplete.

La fonction Prototype (getScripts)

function getScripts( scripts, onScript, onComplete )
{
    this.async = true;
    this.cache = false;
    this.data = null;
    this.complete = function () { $.scriptHandler.loaded(); };
    this.scripts = scripts;
    this.onScript = onScript;
    this.onComplete = onComplete;
    this.total = scripts.length;
    this.progress = 0;
};

getScripts.prototype.fetch = function() {
    $.scriptHandler = this;
    var src = this.scripts[ this.progress ];
    console.log('%cFetching %s','color:#ffbc2e;', src);

    $.ajax({
        crossDomain:true,
        async:this.async,
        cache:this.cache,
        type:'GET',
        url: src,
        data:this.data,
        statusCode: {
            200: this.complete
        },
        dataType:'script'
    });
};

getScripts.prototype.loaded = function () {
    this.progress++;
    if( this.progress >= this.total ) {
        if(this.onComplete) this.onComplete();
    } else {
        this.fetch();
    };
    if(this.onScript) this.onScript();
};

Comment utiliser

var scripts = new getScripts(
    ['script1.js','script2.js','script.js'],
    function() {
        /* Optional - Executed each time a script has loaded (Use for Progress updates?) */
    },
    function () {
        /* Optional - Executed when the entire list of scripts has been loaded */
    }
);
scripts.fetch();

La fonction est la même que celle que j'ai trouvée en utilisant Deferred (Deprecated now?), When, Success & Complete dans mes essais pour ne PAS être fiable à 100%!?, D'où cette fonction et l'utilisation de statusCode par exemple.

Vous voudrez peut-être ajouter un comportement de gestion des erreurs/échecs si vous le souhaitez.

4
Marc Newton

Super réponse, adeneo.

Il m'a fallu un peu de temps pour comprendre comment rendre votre réponse plus générique (afin de pouvoir charger un tableau de scripts définis par le code). Le rappel est appelé lorsque tous les scripts ont été chargés et exécutés. Voici ma solution:

    function loadMultipleScripts(scripts, callback){
        var array = [];

        scripts.forEach(function(script){
            array.Push($.getScript( script ))
        });

        array.Push($.Deferred(function( deferred ){
                    $( deferred.resolve );
                }));

        $.when.apply($, array).done(function(){
                if (callback){
                    callback();
                }
            });
    }
2
RoccoB

Vous pouvez utiliser la méthode $.when- en essayant la fonction suivante:

function loadScripts(scripts) {
  scripts.forEach(function (item, i) {
    item = $.getScript(item);
  });
  return $.when.apply($, scripts);
}

Cette fonction serait utilisée comme ceci:

loadScripts(['path/to/script-a.js', 'path/to/script-b.js']).done(function (respA, respB) {
    // both scripts are loaded; do something funny
});

C'est la façon d'utiliser les promesses et d'avoir un minimum de frais généraux.

2
Emanuel Kluge

Ce que vous recherchez est un chargeur compatible AMD (comme require.js).

http://requirejs.org/

http://requirejs.org/docs/whyamd.html

Il y a beaucoup de bons logiciels open source si vous le regardez. En gros, cela vous permet de définir un module de code, et s'il dépend d'autres modules de code, il attendra que le téléchargement de ces modules soit terminé avant de s'exécuter. De cette façon, vous pouvez charger 10 modules de manière asynchrone et il ne devrait y avoir aucun problème, même si l’un dépend de quelques-uns des autres à exécuter.

1
dqhendricks

Cette fonction s'assurera qu'un fichier est chargé après le chargement complet du fichier de dépendance. Il vous suffit de fournir les fichiers dans un ordre en gardant à l’esprit les dépendances d’autres fichiers.

function loadFiles(files, fn) {
    if (!files.length) {
        files = [];
    }
    var head = document.head || document.getElementsByTagName('head')[0];

    function loadFile(index) {
        if (files.length > index) {
            var fileref = document.createElement('script');
            fileref.setAttribute("type", "text/javascript");
            fileref.setAttribute("src", files[index]);
            head.appendChild(fileref);
            index = index + 1;
            // Used to call a callback function
            fileref.onload = function () {
                loadFile(index);
            }
        } else if(fn){
            fn();
        }
    }
    loadFile(0);
}
1
Mr_Green

Cela fonctionne pour moi:

function getScripts(scripts) {
    var prArr = [];
    scripts.forEach(function(script) { 
        (function(script){
            prArr .Push(new Promise(function(resolve){
                $.getScript(script, function () {
                    resolve();
                });
            }));
        })(script);
    });
    return Promise.all(prArr, function(){
        return true;
    });
}

Et utilisez-le:

var jsarr = ['script1.js','script2.js'];
getScripts(jsarr).then(function(){
...
});
1
user2020001

Voici la réponse utilisant celle de Maciej Sawicki et implémentant Promise comme rappel:

function loadScripts(urls, path) {
    return new Promise(function(resolve) {
        urls.forEach(function(src, i) {

            let script = document.createElement('script');        
            script.type = 'text/javascript';
            script.src = (path || "") + src;
            script.async = false;

            // If last script, bind the callback event to resolve
            if(i == urls.length-1) {                    
                // Multiple binding for browser compatibility
                script.onreadystatechange = resolve;
                script.onload = resolve;
            }

            // Fire the loading
            document.body.appendChild(script);
        });
    });
}

Utilisation:

let JSDependencies = ["jquery.js",
                      "LibraryNeedingJquery.js",
                      "ParametersNeedingLibrary.js"];

loadScripts(JSDependencies,'JavaScript/').then(taskNeedingParameters);

Tous les fichiers Javascript sont téléchargés dès que possible et exécutés dans l'ordre indiqué. Alors taskNeedingParameters est appelé.

1
H4dr1en

Il existe un plugin qui étend la méthode getScript de jQuery. Permet le chargement asynchrone et synchrone et utilise le mécanisme de mise en cache de jQuery. La divulgation complète, j'ai écrit ceci. N'hésitez pas à contribuer si vous trouvez une meilleure méthode.

https://github.com/hudsonfoo/jquery-getscripts

0
user3455212

sur la base de la réponse de @adeneo ci-dessus:

des suggestions d'amélioration ??

// Usage
//$.getMultiResources(['script-1.js','style-1.css'], 'assets/somePath/')
//  .done(function () {})
//  .fail(function (error) {})
//  .always(function () {});

(function ($) {
  $.getMultiResources = function (arr, pathOptional, cache) {
    cache = (typeof cache === 'undefined') ? true : cache;
    var _arr = $.map(arr, function (src) {
      var srcpath = (pathOptional || '') + src;
      if (/.css$/i.test(srcpath)) {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'text',
          cache: cache,
          success: function () {
            $('<link>', {
              rel: 'stylesheet',
              type: 'text/css',
              'href': srcpath
            }).appendTo('head');
          }
        });

      } else {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'script',
          cache: cache
        });
      }
    });
    //
    _arr.Push($.Deferred(function (deferred) {
      $(deferred.resolve);
    }));
    //
    return $.when.apply($, _arr);
  };
})(jQuery);
0
django

Vous pouvez essayer ceci en utilisant la récursivité. Cela les téléchargera de manière synchronisée, l'un après l'autre, jusqu'à ce que le téléchargement de la liste complète soit terminé.

var queue = ['url/links/go/here'];

ProcessScripts(function() { // All done do what ever you want

}, 0);

function ProcessScripts(cb, index) {
    getScript(queue[index], function() {
        index++;
        if (index === queue.length) { // Reached the end
            cb();
        } else {
            return ProcessScripts(cb, index);
        }
    });
}

function getScript(script, callback) {
    $.getScript(script, function() {
        callback();
    });
}
0
Rick

Version abrégée de la réponse complète d'Andrew Marc Newton ci-dessus. Celui-ci ne vérifie pas si le code d'état a réussi, ce que vous devez faire pour éviter tout comportement indéfini de l'interface utilisateur. 

Celui-ci était destiné à un système ennuyeux dans lequel je pouvais garantir jQuery mais pas d'autre, alors je voulais une technique suffisamment courte pour ne pas être transformée en script externe si elle y était forcée. (Vous pouvez le rendre encore plus court en passant l'indice 0 au premier appel "récursif" mais la force des habitudes de style m'a fait ajouter le sucre).

J'attribue également la liste des dépendances à un nom de module. Ce bloc peut donc être inclus partout où vous avez besoin de "module1". Les scripts et l'initialisation dépendante ne seront inclus/exécutés qu'une seule fois (vous pouvez vous connecter à index dans le rappel et voir un seul message. ensemble ordonné de AJAX demandes en cours d'exécution)

if(typeof(__loaders) == 'undefined') __loaders = {};

if(typeof(__loaders.module1) == 'undefined')
{
    __loaders.module1 = false;

    var dependencies = [];

    dependencies.Push('/scripts/loadmefirst.js');
    dependencies.Push('/scripts/loadmenext.js');
    dependencies.Push('/scripts/loadmelast.js');

    var getScriptChain  = function(chain, index)        
    {
        if(typeof(index) == 'undefined')
            index = 0;

        $.getScript(chain[index], 
            function()
            {
                if(index == chain.length - 1)
                {
                    __loaders.module1 = true;

                    /* !!!
                        Do your initialization of dependent stuff here 
                    !!! */
                }
                else 
                    getScriptChain(chain, index + 1);
            }
        );
    };

    getScriptChain(dependencies);       
}
0
Whelkaholism

Ajouter des scripts avec async = false

Voici une approche différente mais super simple . Pour charger plusieurs scripts, vous pouvez simplement les ajouter au corps. 

  • Les charge de manière asynchrone, car c’est ainsi que les navigateurs optimisent le chargement.
  • Exécute les scripts dans l’ordre, car c’est ainsi que les navigateurs analysent les balises HTML
  • Pas besoin de rappel, car les scripts sont exécutés dans l'ordre. Ajoutez simplement un autre script, et il sera exécuté après les autres scripts

Plus d'infos ici: https://www.html5rocks.com/en/tutorials/speed/script-loading/

var scriptsToLoad = [
   "script1.js", 
   "script2.js",
   "script3.js",
]; 

scriptsToLoad.forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.body.appendChild(script);
});
0
Maciej Sawicki

Charges n scripts un à un (utile si, par exemple, le 2ème fichier a besoin du 1er):

(function self(a,cb,i){
    i = i || 0; 
    cb = cb || function(){};    
    if(i==a.length)return cb();
    $.getScript(a[i++],self.bind(0,a,cb,i));                    
})(['list','of','script','urls'],function(){console.log('done')});
0
user669677