Динамически загружать файл JavaScript

168

Как вы можете надежно и динамически загрузить файл JavaScript? Это может быть использовано для реализации модуля или компонента, который при «инициализации» будет динамически загружать все необходимые скрипты библиотеки JavaScript по требованию.

Клиент, использующий компонент, не обязан загружать все файлы сценариев библиотеки (и вручную вставлять <script>теги на свою веб-страницу), которые реализуют этот компонент, - только файл сценария основного компонента.

Как основные библиотеки JavaScript выполняют это (Prototype, jQuery и т. Д.)? Объединяют ли эти инструменты несколько файлов JavaScript в единую распространяемую версию файла сценария для сборки? Или они выполняют динамическую загрузку вспомогательных «библиотечных» скриптов?

Дополнение к этому вопросу: есть ли способ обработать событие после загрузки динамически включенного файла JavaScript? Прототип document.observeдля событий всего документа. Пример:

document.observe("dom:loaded", function() {
  // initially hide all containers for tab content
  $$('div.tabcontent').invoke('hide');
});

Каковы доступные события для элемента скрипта?

Адам
источник

Ответы:

84

Вы можете написать динамические теги сценария (используя Prototype ):

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});

Проблема в том, что мы не знаем, когда внешний файл скрипта будет полностью загружен.

Мы часто хотим, чтобы наш зависимый код был в следующей строке, и нам нравится писать что-то вроде:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}

Существует умный способ внедрить зависимости скрипта без необходимости обратных вызовов. Вам просто нужно вытащить скрипт через синхронный AJAX-запрос и проверить скрипт на глобальном уровне.

Если вы используете Prototype, метод Script.load выглядит так:

var Script = {
    _loadedScripts: [],
    include: function(script) {
        // include script only once
        if (this._loadedScripts.include(script)) {
            return false;
        }
        // request file synchronous
        var code = new Ajax.Request(script, {
            asynchronous: false,
            method: "GET",
            evalJS: false,
            evalJSON: false
        }).transport.responseText;
        // eval code on global level
        if (Prototype.Browser.IE) {
            window.execScript(code);
        } else if (Prototype.Browser.WebKit) {
            $$("head").first().insert(Object.extend(
                new Element("script", {
                    type: "text/javascript"
                }), {
                    text: code
                }
            ));
        } else {
            window.eval(code);
        }
        // remember included script
        this._loadedScripts.push(script);
    }
};
aemkei
источник
Что я могу сделать, чтобы это работало для междоменного домена? (загрузка скрипта из http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
@ user2284570 CORS stackoverflow.com/questions/5750696/…
Ciasto piekarz
@ Ciastopiekarz: я не контролирую web.archive.org.
user2284570
Затем вам нужно очистить данные, к которым вы хотите получить доступ, с помощью другой программы и предоставить их себе
Дэвид Шуманн
что такое Ajax.Request ??
блюджайке
72

В javascript нет импорта / включения / запроса, но есть два основных способа достичь желаемого:

1 - Вы можете загрузить его с помощью вызова AJAX, а затем использовать eval.

Это самый простой способ, но он ограничен вашим доменом из-за настроек безопасности Javascript, а использование eval открывает путь к ошибкам и взломам.

2 - Добавьте тег сценария с URL-адресом сценария в HTML.

Определенно лучший путь. Вы можете загрузить скрипт даже с чужого сервера, и он чист, поскольку вы используете анализатор браузера для оценки кода. Вы можете поместить тег в заголовок веб-страницы или внизу тела.

Оба эти решения обсуждаются и иллюстрируются здесь.

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

Это означает, что если вы будете использовать эти приемы напрямую, вы не сможете использовать свой недавно загруженный код на следующей строке после того, как вы попросили его загрузить, потому что он все еще будет загружаться.

Например: my_lovely_script.js содержит MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Затем вы перезагрузите страницу, нажав F5. И это работает! Смешение ...

Так что с этим делать?

Ну, вы можете использовать взлом, который предлагает автор, по ссылке, которую я вам дал. Таким образом, для спешащих людей он использует событие en для запуска функции обратного вызова при загрузке скрипта. Таким образом, вы можете поместить весь код с помощью удаленной библиотеки в функцию обратного вызова. EG:

function loadScript(url, callback)
{
    // adding the script tag to the head as suggested before
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = url;

   // then bind the event to the callback function 
   // there are several events for cross browser compatibility
   script.onreadystatechange = callback;
   script.onload = callback;

   // fire the loading
   head.appendChild(script);
}

Затем вы пишете код, который хотите использовать ПОСЛЕ того, как скрипт загружается в лямбда-функцию:

var myPrettyCode = function() {
    // here, do what ever you want
};

Затем вы запускаете все это:

loadScript("my_lovely_script.js", myPrettyCode);

Ладно, я понял. Но писать все эти вещи - боль.

Ну, в этом случае вы можете использовать как всегда фантастический бесплатный фреймворк jQuery, который позволяет вам делать то же самое в одной строке:

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});
Давал Шах
источник
11
не могу поверить, насколько недооценен этот ответ. Спасибо.
Нафталимич
2
Бьюсь об заклад, это потому, что люди не любят читать за первой строкой, если только они не обещают им, что они будут богаты, если они «просто следуют этим трем секретным шагам к успеху».
Коста
Это работает. Я надеюсь поделиться своим опытом здесь сэкономит ваше время, потому что я потратил много часов на это. Я использую Angular 6 и применяемый шаблон (html, css, jquery). Проблема в том, что шаблон имеет файл js, который загружается после загрузки html-элементов для прикрепления событий слушателей. Этот js-файл было трудно выполнить после загрузки углового приложения. Добавление его в тег сценариев angular app (angular.json) свяжет их, но не выполнит этот файл js после загрузки. Это слишком много кода, чтобы переписать в машинописи, так что это было очень полезно. Следующий комментарий Я
приведу
Я просто использовал этот код следующим образом: ngAfterViewInit () {debugger; $ .getScript ("/ assets / js / jquery.app.js", function () {alert ("Скрипт загружен и выполнен."); // здесь вы можете использовать все, что вы определили в загруженном скрипте}); }
Нур Лабабиди
За угловым
знаком
28

Недавно я использовал гораздо менее сложную версию с jQuery :

<script src="scripts/jquery.js"></script>
<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  var $head = $("head");
  for (var i = 0; i < js.length; i++) {
    $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

Он отлично работал в каждом браузере, в котором я его тестировал: IE6 / 7, Firefox, Safari, Opera.

Обновление: версия без jQuery:

<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  for (var i = 0, l = js.length; i < l; i++) {
    document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>
Трэвис
источник
25
Это здорово ... если вы не пытаетесь загрузить jquery.
1
Похоже, что jQuery собирается добавить плагин Require в jQuery Core для будущего выпуска: plugins.jquery.com/project/require
Адам,
1
Еще лучше использовать jQuery $.getScript. Смотри мой ответ.
августа,
1
Modernizr (yepnope.js) или lab.js являются подходящими решениями для этого. Использование тяжелой библиотеки сценариев (которая должна загружаться первой) не самый подходящий ответ для мобильных устройств или многих других ситуаций.
1nfiniti
2
@MaulikGangani старые браузеры и HTML-валидаторы интерпретируют это как маркер для завершения скрипта.
Трэвис
20

Я сделал в основном то же самое, что вы сделали Адам, но с небольшим изменением, чтобы убедиться, что я добавляю тег head, чтобы выполнить работу. Я просто создал функцию включения (код ниже) для обработки как скриптов, так и CSS-файлов.

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

function include( url, type ){
    // First make sure it hasn't been loaded by something else.
    if( Array.contains( includedFile, url ) )
        return;

    // Determine the MIME-type
    var jsExpr = new RegExp( "js$", "i" );
    var cssExpr = new RegExp( "css$", "i" );
    if( type == null )
        if( jsExpr.test( url ) )
            type = 'text/javascript';
        else if( cssExpr.test( url ) )
            type = 'text/css';

    // Create the appropriate element.
    var tag = null;
    switch( type ){
        case 'text/javascript' :
            tag = document.createElement( 'script' );
            tag.type = type;
            tag.src = url;
            break;
        case 'text/css' :
            tag = document.createElement( 'link' );
            tag.rel = 'stylesheet';
            tag.type = type;
            tag.href = url;
            break;
    }

    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( tag );
    Array.add( includedFile, url );
}
бледная лошадь
источник
Боюсь, у меня нет контекста, кроме этого Пикл, у меня нет никаких предложений. Приведенный выше код работает как есть, он был извлечен непосредственно из работающего проекта.
бледная лошадь
5
@palehorse, Mike и Muhd, правы, это может сработать в вашем проекте, но это потому, что переменные "includesFile" и "Array" должны быть определены в другом месте вашего проекта, этот код сам по себе не будет работать, это может быть лучше отредактировать его, чтобы он мог работать вне контекста вашего проекта, или хотя бы добавить комментарий, объясняющий, что это за неопределенные переменные (тип и т. д.)
user280109
14

еще один потрясающий ответ

$.getScript("my_lovely_script.js", function(){


   alert("Script loaded and executed.");
  // here you can use anything you defined in the loaded script

 });

https://stackoverflow.com/a/950146/671046

Навид
источник
2
Что я могу сделать, чтобы это работало для междоменного домена? (загрузка скрипта из http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
9

Вот пример кода, который я нашел ... у кого-нибудь есть лучший способ?

  function include(url)
  {
    var s = document.createElement("script");
    s.setAttribute("type", "text/javascript");
    s.setAttribute("src", url);
    var nodes = document.getElementsByTagName("*");
    var node = nodes[nodes.length -1].parentNode;
    node.appendChild(s);
  }
Адам
источник
Что я могу сделать, чтобы это работало для междоменного домена? (загрузка скрипта из http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
6

Если вы уже загрузили jQuery, вы должны использовать $ .getScript .

Это имеет преимущество перед другими ответами здесь в том, что у вас есть встроенная функция обратного вызова (чтобы гарантировать, что скрипт загружается до запуска зависимого кода), и вы можете контролировать кэширование.

Muhd
источник
4

Если вы хотите загрузить скрипт SYNC , вам нужно добавить текст скрипта непосредственно в тег HTML HEAD. Добавление его как вызовет загрузку ASYNC . Чтобы синхронно загрузить текст скрипта из внешнего файла, используйте XHR. Ниже приведен краткий пример (он использует части других ответов в этом и других постах):

/*sample requires an additional method for array prototype:*/

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/*define object that will wrap our logic*/
var ScriptLoader = {
LoadedFiles: [],

LoadFile: function (url) {
    var self = this;
    if (this.LoadedFiles.contains(url)) return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                self.LoadedFiles.push(url);
                self.AddScript(xhr.responseText);
            } else {
                if (console) console.error(xhr.statusText);
            }
        }
    };
    xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
    xhr.send(null);
},

AddScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}
};

/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/

ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function () { alert($('#tb1').val()); });
Sielu
источник
3

у кого-нибудь есть способ получше?

Я думаю, что просто добавить скрипт в тело было бы проще, чем добавить его в последний узел на странице. Как насчет этого:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}
Джозеф Пекораро
источник
Что я могу сделать, чтобы это работало для междоменного домена? (загрузка скрипта из http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

Я использовал еще одно решение, которое я нашел в сети ... Это находится под CreativeCommons, и он проверяет, был ли источник включен до вызова функции ...

Вы можете найти файл здесь: include.js

/** include - including .js files from JS - bfults@gmail.com - 2005-02-09
 ** Code licensed under Creative Commons Attribution-ShareAlike License 
 ** http://creativecommons.org/licenses/by-sa/2.0/
 **/              
var hIncludes = null;
function include(sURI)
{   
  if (document.getElementsByTagName)
  {   
    if (!hIncludes)
    {
      hIncludes = {}; 
      var cScripts = document.getElementsByTagName("script");
      for (var i=0,len=cScripts.length; i < len; i++)
        if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
    }
    if (!hIncludes[sURI])
    {
      var oNew = document.createElement("script");
      oNew.type = "text/javascript";
      oNew.src = sURI;
      hIncludes[sURI]=true;
      document.getElementsByTagName("head")[0].appendChild(oNew);
    }
  }   
} 
Пьер Спринг
источник
Что я могу сделать, чтобы это работало для междоменного домена? (загрузка скрипта из http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
user2284570
3

Только что узнал о замечательной функции в YUI 3 (на момент написания доступна в предварительной версии). Вы можете легко вставлять зависимости в библиотеки YUI и «внешние» модули (то, что вы ищете) без слишком большого количества кода: YUI Loader .

Он также отвечает на ваш второй вопрос о вызываемой функции, как только загружается внешний модуль.

Пример:

YUI({
    modules: {
        'simple': {
            fullpath: "http://example.com/public/js/simple.js"
        },
        'complicated': {
            fullpath: "http://example.com/public/js/complicated.js"
            requires: ['simple']  // <-- dependency to 'simple' module
        }
    },
    timeout: 10000
}).use('complicated', function(Y, result) {
    // called as soon as 'complicated' is loaded
    if (!result.success) {
        // loading failed, or timeout
        handleError(result.msg);
    } else {
        // call a function that needs 'complicated'
        doSomethingComplicated(...);
    }
});

Работал отлично для меня и имеет преимущество управления зависимостями. Обратитесь к документации YUI за примером с календарем YUI 2 .

Kariem
источник
Это, вероятно, идеально, за исключением того, что YUI непристойно велик только для этой функциональности.
Muhd
3

Существует новый предложенный стандарт ECMA под названием динамический импорт , недавно включенный в Chrome и Safari.

const moduleSpecifier = './dir/someModule.js';

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
Алистер
источник
2

Техника, которую мы используем на работе, состоит в том, чтобы запросить файл javascript с помощью AJAX-запроса, а затем eval (). Если вы используете библиотеку прототипов, они поддерживают эту функцию в своем вызове Ajax.Request.

17 из 26
источник
2

jquery разрешил это для меня с помощью функции .append () - использовал это для загрузки полного пакета jquery ui

/*
 * FILENAME : project.library.js
 * USAGE    : loads any javascript library
 */
    var dirPath = "../js/";
    var library = ["functions.js","swfobject.js","jquery.jeditable.mini.js","jquery-ui-1.8.8.custom.min.js","ui/jquery.ui.core.min.js","ui/jquery.ui.widget.min.js","ui/jquery.ui.position.min.js","ui/jquery.ui.button.min.js","ui/jquery.ui.mouse.min.js","ui/jquery.ui.dialog.min.js","ui/jquery.effects.core.min.js","ui/jquery.effects.blind.min.js","ui/jquery.effects.fade.min.js","ui/jquery.effects.slide.min.js","ui/jquery.effects.transfer.min.js"];

    for(var script in library){
        $('head').append('<script type="text/javascript" src="' + dirPath + library[script] + '"></script>');
    }

Использовать - в заголовке вашего html / php / etc после импорта jquery.js вы просто включите этот файл, чтобы загрузить всю библиотеку и добавить ее в заголовок ...

<script type="text/javascript" src="project.library.js"></script>
JM Design
источник
2

Делайте это красиво, коротко, просто и легко! :]

// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library 
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)
{
    var ajax = new XMLHttpRequest();
    ajax.open('GET', url, false);
    ajax.onreadystatechange = function ()
    {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4)
        {
            switch(ajax.status)
            {
                case 200:
                    eval.apply( window, [script] );
                    console.log("library loaded: ", url);
                    break;
                default:
                    console.log("ERROR: library not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

 // initialize a single load 
load('plugins/script.js');

// initialize a full load of scripts
if (s.length > 0)
{
    for (i = 0; i < s.length; i++)
    {
        load(s[i]);
    }
}

Этот код является просто кратким функциональным примером, который может потребовать дополнительных функциональных возможностей для полной поддержки на любой (или данной) платформе.

TFont
источник
2

Динамический модуль импорта появился в Firefox 67+ .

(async () => {
   await import('./synth/BubbleSynth.js')
})()

С обработкой ошибок:

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()

Это также работает для любого вида немодульных библиотек, в этом случае библиотека доступна на объекте окна, по-старому, но только по требованию, что приятно.

Например, используя suncalc.js , на сервере должна быть включена поддержка CORS !

(async () => {
 await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js')
 .then(function(){
   let times = SunCalc.getTimes(new Date(), 51.5,-0.1);
   console.log("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.sunrise.getMinutes() + ". Take your pics!")
  })
})()

https://caniuse.com/#feat=es6-module-dynamic-import

NVRM
источник
1

Есть сценарии, которые предназначены специально для этой цели.

yepnope.js встроен в Modernizr, а lab.js - более оптимизированная (но менее удобная для пользователя версия).

Я бы не советовал делать это с большой библиотекой, такой как jquery или прототип - потому что одно из основных преимуществ загрузчика скриптов - это возможность загружать скрипты рано - вам не нужно ждать, пока jquery и все ваши элементы dom загрузятся, прежде чем запустить проверку, чтобы увидеть, хотите ли вы динамически загрузить скрипт.

1nfiniti
источник
1

Я написал простой модуль, который автоматизирует работу по импорту / включению скриптов модуля в JavaScript. Попробуйте и оставьте отзыв! :) Для подробного объяснения кода обратитесь к этому сообщению в блоге: http://stamat.wordpress.com/2013/04/12/javascript-require-import-include-modules/

var _rmod = _rmod || {}; //require module namespace
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = {};
_rmod.loading = {
    scripts: {},
    length: 0
};

_rmod.findScriptPath = function(script_name) {
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) {
        if (script_elems[i].src.endsWith(script_name)) {
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        }
    }
    return '';
};

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark the root directory of your library, any library


_rmod.injectScript = function(script_name, uri, callback, prepare) {

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() {
            callback(script_name, uri);
        };

    document.getElementsByTagName('head')[0].appendChild(script_elem);
};

_rmod.requirePrepare = function(script_name, uri) {
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
};

_rmod.requireCallback = function(script_name, uri) {
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
};

_rmod.onReady = function() {
    if (!_rmod.LOADED) {
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){
            _rmod.on_ready_fn_stack[i]();
        });
        _rmod.LOADED = true;
    }
};

//you can rename based on your liking. I chose require, but it can be called include or anything else that is easy for you to remember or write, except import because it is reserved for future use.
var require = function(script_name) {
    var np = script_name.split('.');
    if (np[np.length-1] === '*') {
        np.pop();
        np.push('_all');
    }

    script_name = np.join('.');
    var uri = _rmod.libpath + np.join('/')+'.js';
    if (!_rmod.loading.scripts.hasOwnProperty(script_name) 
     && !_rmod.imported.hasOwnProperty(script_name)) {
        _rmod.injectScript(script_name, uri, 
            _rmod.requireCallback, 
                _rmod.requirePrepare);
    }
};

var ready = function(fn) {
    _rmod.on_ready_fn_stack.push(fn);
};

// ----- USAGE -----

require('ivar.util.array');
require('ivar.util.string');
require('ivar.net.*');

ready(function(){
    //do something when required scripts are loaded
});
Стамат
источник
1

Я потерян во всех этих примерах, но сегодня мне нужно было загрузить внешний .js из моего основного .js, и я сделал это:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");
adrianTNT
источник
Вы можете взглянуть на ссылку: ответ
asmmahmud
1

Вот простой с обратным вызовом и поддержкой IE:

function loadScript(url, callback) {

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState) { //IE
        script.onreadystatechange = function () {
            if (script.readyState == "loaded" || script.readyState == "complete") {
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //Others
        script.onload = function () {
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {

     //jQuery loaded
     console.log('jquery loaded');

});
Иаков
источник
1

Я знаю, что мой ответ немного запоздал на этот вопрос, но вот отличная статья на www.html5rocks.com - Глубокое погружение в мутные воды загрузки скриптов .

В этой статье делается вывод о том, что в отношении поддержки браузера лучший способ динамической загрузки файла JavaScript без блокировки отображения контента заключается в следующем:

Учитывая, что вы назвали четыре сценария, script1.js, script2.js, script3.js, script4.jsвы можете сделать это с помощью async = false :

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Теперь, Spec говорит : скачать вместе, выполнить по порядку, как только все загрузки.

Firefox <3.6, Opera говорит: я понятия не имею, что это за «асинхронная» вещь, но так получилось, что я выполняю сценарии, добавленные через JS, в порядке их добавления.

Safari 5.0 говорит: « Я понимаю« асинхронный », но не понимаю, как установить« ложный »с JS. Я выполню ваши сценарии, как только они приземлятся, в любом порядке.

IE <10 говорит: нет понятия об «асинхронности», но есть обходной путь, использующий «onreadystatechange».

Все остальное говорит: я твой друг, мы собираемся сделать это по книге.

Теперь полный код с IE <10:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

Несколько трюков и минимизация позже, это 362 байта

!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
  "//other-domain.com/1.js",
  "2.js"
])
asmmahmud
источник
Я использую описанный выше подход, он отлично работает в Chrome и Firefox, но сталкивается с проблемами в браузере IE
г-н Рошан,
1

Что-то вроде этого...

<script>
     $(document).ready(function() {
          $('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
     });
</script>
Джеймс Арнольд
источник
1

Вот простой пример для функции загрузки файлов JS. Соответствующие пункты:

  • вам не нужен jQuery, поэтому вы можете использовать его изначально для загрузки также файла jQuery.js
  • это асинхронный с обратным вызовом
  • он гарантирует, что он загружается только один раз, так как он хранит вложение с записью загруженных URL, тем самым избегая использования сети
  • в отличие от jQuery $.ajaxили $.getScriptвы можете использовать одноразовые номера, решая, таким образом, проблемы с CSP unsafe-inline. Просто используйте собственностьscript.nonce
var getScriptOnce = function() {

    var scriptArray = []; //array of urls (closure)

    //function to defer loading of script
    return function (url, callback){
        //the array doesn't have such url
        if (scriptArray.indexOf(url) === -1){

            var script=document.createElement('script');
            script.src=url;
            var head=document.getElementsByTagName('head')[0],
                done=false;

            script.onload=script.onreadystatechange = function(){
                if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
                    done=true;
                    if (typeof callback === 'function') {
                        callback();
                    }
                    script.onload = script.onreadystatechange = null;
                    head.removeChild(script);

                    scriptArray.push(url);
                }
            };

            head.appendChild(script);
        }
    };
}();

Теперь вы используете его просто

getScriptOnce("url_of_your_JS_file.js");
Жоао Пиментел Феррейра
источник
1

Абсурдная строчка, для тех, кто считает, что загрузка библиотеки js не должна занимать больше одной строки кода: P

await new Promise((resolve, reject) => {let js = document.createElement("script"); js.src="mylibrary.js"; js.onload=resolve; js.onerror=reject; document.body.appendChild(js)});

Очевидно, что если скрипт, который вы хотите импортировать, является модулем, вы можете использовать import(...)функцию.


источник
1

С Promises вы можете упростить это, как это. Функция загрузчика:

  const loadCDN = src =>
    new Promise((resolve, reject) => {
      if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
      const script = document.createElement("script")
      script.src = src
      script.async = true
      document.head.appendChild(script)
      script.onload = resolve
      script.onerror = reject
    })

Использование (async / await):

await loadCDN("https://.../script.js")

Использование (обещание):

loadCDN("https://.../script.js").then(res => {}).catch(err => {})

ПРИМЕЧАНИЕ: было одно подобное решение, но оно не проверяет, загружен ли уже скрипт, и загружает скрипт каждый раз. Этот проверяет свойство src.

radulle
источник
0

все основные библиотеки javascript, такие как jscript, prototype, YUI, поддерживают загрузку файлов сценариев. Например, в YUI после загрузки ядра вы можете сделать следующее, чтобы загрузить элемент управления календаря.

var loader = new YAHOO.util.YUILoader({

    require: ['calendar'], // what components?

    base: '../../build/',//where do they live?

    //filter: "DEBUG",  //use debug versions (or apply some
                        //some other filter?

    //loadOptional: true, //load all optional dependencies?

    //onSuccess is the function that YUI Loader
    //should call when all components are successfully loaded.
    onSuccess: function() {
        //Once the YUI Calendar Control and dependencies are on
        //the page, we'll verify that our target container is 
        //available in the DOM and then instantiate a default
        //calendar into it:
        YAHOO.util.Event.onAvailable("calendar_container", function() {
            var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container");
            myCal.render();
        })
     },

    // should a failure occur, the onFailure function will be executed
    onFailure: function(o) {
        alert("error: " + YAHOO.lang.dump(o));
    }

 });

// Calculate the dependency and insert the required scripts and css resources
// into the document
loader.insert();
Даррен Копп
источник
Вы можете взглянуть на ссылку: ответ
asmmahmud
0

Я подправил некоторые из вышеприведенного поста с рабочим примером. Здесь мы можем дать CSS и JS в том же массиве также.

$(document).ready(function(){

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/* define object that will wrap our logic */
var jsScriptCssLoader = {

jsExpr : new RegExp( "js$", "i" ),
cssExpr : new RegExp( "css$", "i" ),
loadedFiles: [],

loadFile: function (cssJsFileArray) {
    var self = this;
    // remove duplicates with in array
    cssJsFileArray.filter((item,index)=>cssJsFileArray.indexOf(item)==index)
    var loadedFileArray = this.loadedFiles;
    $.each(cssJsFileArray, function( index, url ) {
            // if multiple arrays are loaded the check the uniqueness
            if (loadedFileArray.contains(url)) return;
            if( self.jsExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addScript(data);
                });

            }else if( self.cssExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addCss(data);
                });
            }

            self.loadedFiles.push(url);
    });

    // don't load twice accross different arrays

},
addScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
},
addCss: function (code) {
    var oNew = document.createElement("style");
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}

};


//jsScriptCssLoader.loadFile(["css/1.css","css/2.css","css/3.css"]);
jsScriptCssLoader.loadFile(["js/common/1.js","js/2.js","js/common/file/fileReader.js"]);
});
VG P
источник
0

Для тех из вас, кто любит однострочники:

import('./myscript.js');

Скорее всего, вы можете получить ошибку, например:

Доступ к сценарию в http: //..../myscript.js из источника « http://127.0.0.1 » был заблокирован политикой CORS: заголовок «Access-Control-Allow-Origin» отсутствует запрашиваемый ресурс.

В этом случае вы можете вернуться к:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Людмила Тинькова
источник