web-dev-qa-db-fra.com

Alternative à jQuery.getScript en JavaScript natif

J'essaie de charger dynamiquement les scripts JS, mais l'utilisation de jQuery n'est pas une option.

J'ai vérifié la source jQuery pour voir comment getScript a été implémenté afin que je puisse utiliser cette approche pour charger des scripts à l'aide de JS natif. Cependant, getScript n'appelle que jQuery.get ()

et je n'ai pas pu trouver où la méthode get est implémentée.

Donc ma question est,

Quel est un moyen fiable d'implémenter ma propre méthode getScript à l'aide de JavaScript natif?

Merci!

43
ILikeTacos

Vous pouvez récupérer des scripts comme celui-ci:

(function(document, tag) {
    var scriptTag = document.createElement(tag), // create a script tag
        firstScriptTag = document.getElementsByTagName(tag)[0]; // find the first script tag in the document
    scriptTag.src = 'your-script.js'; // set the source of the script to your script
    firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag); // append the script to the DOM
}(document, 'script'));
29
Mathletics

Voici une alternative jQuery getScript avec fonctionnalité de rappel:

function getScript(source, callback) {
    var script = document.createElement('script');
    var prior = document.getElementsByTagName('script')[0];
    script.async = 1;

    script.onload = script.onreadystatechange = function( _, isAbort ) {
        if(isAbort || !script.readyState || /loaded|complete/.test(script.readyState) ) {
            script.onload = script.onreadystatechange = null;
            script = undefined;

            if(!isAbort && callback) setTimeout(callback, 0);
        }
    };

    script.src = source;
    prior.parentNode.insertBefore(script, prior);
}
82
Mahn

Tout d'abord, merci pour la réponse de @ Mahn. J'ai réécrit sa solution dans ES6 et promets, au cas où quelqu'un en aurait besoin, je vais juste coller mon code ici:

const loadScript = (source, beforeEl, async = true, defer = true) => {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script');
    const prior = beforeEl || document.getElementsByTagName('script')[0];

    script.async = async;
    script.defer = defer;

    function onloadHander(_, isAbort) {
      if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
        script.onload = null;
        script.onreadystatechange = null;
        script = undefined;

        if (isAbort) { reject(); } else { resolve(); }
      }
    }

    script.onload = onloadHander;
    script.onreadystatechange = onloadHander;

    script.src = source;
    prior.parentNode.insertBefore(script, prior);
  });
}

Usage:

const scriptUrl = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoad&render=explicit';
loadScript(scriptUrl).then(() => {
  console.log('script loaded');
}, () => {
  console.log('fail to load script');
});

et le code est effacé.

11
AXE

utilisez ceci

var js_script = document.createElement('script');
js_script.type = "text/javascript";
js_script.src = "http://www.example.com/script.js";
js_script.async = true;
document.getElementsByTagName('head')[0].appendChild(js_script);
8
Rohit Agrawal

Mozilla Developer Network fournit un exemple qui fonctionne de manière asynchrone et n'utilise pas 'onreadystatechange' (de la réponse de @ ShaneX) qui n'est pas vraiment présent dans un HTMLScriptTag:

function loadError(oError) {
  throw new URIError("The script " + oError.target.src + " didn't load correctly.");
}

function prefixScript(url, onloadFunction) {
  var newScript = document.createElement("script");
  newScript.onerror = loadError;
  if (onloadFunction) { newScript.onload = onloadFunction; }
  document.currentScript.parentNode.insertBefore(newScript, document.currentScript);
  newScript.src = url;
}

Exemple d'utilisation:

prefixScript("myScript1.js");
prefixScript("myScript2.js", function () { alert("The script \"myScript2.js\" has been correctly loaded."); });

Mais le commentaire de @Agamemnus doit être pris en compte: le script peut ne pas être complètement chargé lorsque onloadFunction est appelé. Un temporisateur peut être utilisé setTimeout(func, 0) pour laisser la boucle d'événement finaliser le script ajouté au document. La boucle d'événement appelle enfin la fonction derrière la minuterie et le script devrait être prêt à être utilisé à ce stade.

Cependant, on devrait peut-être envisager de retourner une promesse au lieu de fournir deux fonctions pour la gestion des exceptions et des succès, ce serait la manière ES6. Cela rendrait également inutile un temporisateur, car les promesses sont gérées par la boucle d'événement - car au moment où la promesse est gérée, le script était déjà finalisé par la boucle d'événement.

Implémentant la méthode de Mozilla, y compris Promises, le code final ressemble à ceci:

function loadScript(url)
{
  return new Promise(function(resolve, reject)
  {
    let newScript = document.createElement("script");
    newScript.onerror = reject;
    newScript.onload = resolve;
    document.currentScript.parentNode.insertBefore(newScript, document.currentScript);
    newScript.src = url;
  });
}

loadScript("test.js").then(() => { FunctionFromExportedScript(); }).catch(() => { console.log("rejected!"); });
1
m.w.

Il existe de bonnes solutions ici, mais beaucoup sont obsolètes. Il y a un bon par @Mahn mais comme indiqué dans un commentaire ce n'est pas exactement un remplacement pour $.getScript() car le rappel ne reçoit pas de données. J'avais déjà écrit ma propre fonction pour remplacer $.get() et j'ai atterri ici quand j'en ai besoin pour travailler sur un script. J'ai pu utiliser la solution de @ Mahn et la modifier un peu avec mon remplacement actuel de $.get() et trouver quelque chose qui fonctionne bien et qui est simple à implémenter.

function pullScript(url, callback){
    pull(url, function loadReturn(data, status, xhr){
        //If call returned with a good status
        if(status == 200){
            var script = document.createElement('script');
            //Instead of setting .src set .innerHTML
            script.innerHTML = data;
            document.querySelector('head').appendChild(script);
        }
        if(typeof callback != 'undefined'){
            //If callback was given skip an execution frame and run callback passing relevant arguments
            setTimeout(function runCallback(){callback(data, status, xhr)}, 0);
        }
    });
}

function pull(url, callback, method = 'GET', async = true) {
    //Make sure we have a good method to run
    method = method.toUpperCase();
    if(!(method === 'GET'   ||   method === 'POST'   ||  method === 'HEAD')){
        throw new Error('method must either be GET, POST, or HEAD');
    }
    //Setup our request
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {   // XMLHttpRequest.DONE == 4
            //Once the request has completed fire the callback with relevant arguments
            //you should handle in your callback if it was successful or not
            callback(xhr.responseText, xhr.status, xhr);
        }
    };
    //Open and send request
    xhr.open(method, url, async);
    xhr.send();
}

Nous avons maintenant un remplacement pour $.get() et $.getScript() qui fonctionne tout aussi simplement:

pullScript(file1, function(data, status, xhr){
    console.log(data);
    console.log(status);
    console.log(xhr);
});

pullScript(file2);

pull(file3, function loadReturn(data, status){
    if(status == 200){
        document.querySelector('#content').innerHTML = data;
    }
}
0
Xandor