Подтвердить что ты не робот

Альтернатива jQuery.getScript в собственном JavaScript

Я пытаюсь загрузить JS-скрипты динамически, но использование jQuery не является вариантом.

Я проверил источник jQuery, чтобы увидеть, как getScript был реализован, чтобы я мог использовать этот подход для загрузки скриптов с использованием встроенного JS. Однако getScript вызывает только jQuery.get()

и я не смог найти, где реализован метод get.

Итак, мой вопрос:

Какой надежный способ реализовать собственный метод getScript с использованием собственного JavaScript?

Спасибо!

4b9b3361

Ответ 1

Вы можете получить такие сценарии, как это:

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

Ответ 2

Вот альтернатива getScript jQuery с функциональностью обратного вызова:

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

Ответ 3

Во-первых, спасибо за ответ @Mahn. Я переписал его решение в ES6 и обещаю, что если кому-то это понадобится, я просто вставлю свой код сюда:

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

Использование:

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');
});

и код сложен.

Ответ 4

используйте этот

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

Ответ 5

Mozilla Developer Network предоставляет пример, который работает асинхронно и не использует "onreadystatechange" (из ответа @ShaneX), которого на самом деле нет в 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;
}

Пример использования:

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

Но следует учитывать комментарий @Agamemnus: скрипт может быть не полностью загружен при onloadFunction. Можно использовать таймер setTimeout(func, 0) чтобы позволить циклу событий завершить добавление скрипта в документ. Наконец, цикл обработки событий вызывает функцию, стоящую за таймером, и сценарий должен быть готов к использованию на этом этапе.

Однако, возможно, стоит подумать о возврате Promise вместо предоставления двух функций для обработки исключений и успеха, что было бы в ES6. Это также сделало бы ненужным использование таймера, поскольку обещания обрабатываются циклом событий - поскольку к моменту обработки обещания сценарий уже был завершен циклом событий.

Реализуя метод Mozilla, включая Promises, окончательный код выглядит следующим образом:

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!"); });

Ответ 6

Здесь есть несколько хороших решений, но многие устарели. Есть хороший вариант от @Mahn, но, как указано в комментарии, он не совсем заменяет $.getScript() как обратный вызов не получает данные. Я уже написал свою собственную функцию для замены $.get() и приземлился здесь, когда мне нужно, чтобы она работала для скрипта. Я смог использовать решение @Mahn и немного изменить его вместе с моей текущей $.get() и придумал что-то, что хорошо работает и просто в реализации.

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

Теперь у нас есть замена для $.get() и $.getScript() которые работают так же просто:

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;
    }
}