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

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

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

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

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

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

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

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
176
0
166 795
28
Перейти к ответу Данный вопрос помечен как решенный

Ответы 28

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

  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.howtocr‌​eate.co.uk/operaStu‌‌​​ff/userjs/aagmfunct‌​ions.js)

user2284570 14.09.2014 06:32

все основные библиотеки 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 03.08.2017 18:59

Я сделал в основном то же самое, что и вы, Адам, но с небольшой модификацией, чтобы убедиться, что я добавляю тег заголовка, чтобы выполнить работу. Я просто создал функцию включения (код ниже) для обработки файлов сценария и 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 );
}

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

palehorse 12.02.2010 18:30

@palehorse, Майк и Мухд, правы, это может сработать в вашем проекте, но это потому, что переменные "includedFile" и "Array" должны быть определены в другом месте вашего проекта, этот код сам по себе не будет работать, он может быть лучше отредактировать его, чтобы он мог работать вне контекста вашего проекта, или, по крайней мере, добавить комментарий, объясняющий, что это за неопределенные переменные (тип и т. д.)

user280109 23.10.2014 14:21

does anyone have a better way?

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

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.howtocr‌​eate.co.uk/operaStu‌‌​​ff/userjs/aagmfunct‌​ions.js)

user2284570 14.09.2014 06:34

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

Я использовал гораздо менее сложная версия в последнее время с 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>

Это здорово ... если вы не пытаетесь загрузить jquery.

Mike Bethany 12.02.2010 11:58

Похоже, jQuery собирается включить плагин Require в jQuery Core для будущего выпуска: plugins.jquery.com/project/require

Adam 10.02.2011 19:47

Еще лучший способ использовать jQuery - использовать $.getScript. Смотрите мой ответ.

Muhd 14.02.2012 00:39

Modernizr (yepnope.js) или lab.js - подходящие решения для этого. использование тяжелой библиотеки сценариев (которая должна загружаться первой) - не самый подходящий ответ для мобильных устройств или многих других ситуаций.

1nfiniti 14.02.2012 18:53

хороший ответ, но зачем писать цикл for на одной строке ?!

JonnyRaa 12.02.2014 15:17

@JonnyLeeds хорошее замечание, я был слишком компактен, я обновлю.

travis 12.02.2014 20:19

Старые браузеры и валидаторы HTML @MaulikGangani интерпретируют это как токен для завершения скрипта.

travis 15.04.2017 21:55

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

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

/** include - including .js files from JS - [email protected] - 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.howtocr‌​eate.co.uk/operaStu‌‌​​ff/userjs/aagmfunct‌​ions.js)

user2284570 14.09.2014 06:33
Ответ принят как подходящий

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

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

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

user2284570 14.09.2014 06:28

@ user2284570 CORS stackoverflow.com/questions/5750696/…

Ciasto piekarz 21.01.2017 13:29

@Ciastopiekarz: я не контролирую web.archive.org.

user2284570 21.01.2017 22:31

Затем вам нужно очистить данные, к которым вы хотите получить доступ, с помощью какой-либо другой программы и предоставить их себе.

David Schumann 03.12.2017 16:21

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

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

Пример:

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.

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

Muhd 14.02.2012 00:06

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>

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

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

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

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

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

еще один отличный ответ

$.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

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

user2284570 14.09.2014 06:32

В 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 event для запуска функции обратного вызова при загрузке скрипта. Таким образом, вы можете поместить весь код с помощью удаленной библиотеки в функцию обратного вызова. Напр .:

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

Это работает. Я надеюсь, что обмен своим опытом сэкономит ваше время, потому что я потратил на это много часов. Я использую Angular 6 и применяемый шаблон (html, css, jquery). Проблема в том, что в шаблоне есть файл js, который загружается после загрузки элементов html для присоединения событий слушателей. Этот js-файл было трудно выполнить после загрузки приложения angular. Добавление его в тег скриптов angular app (angular.json) объединит их, но не выполнит этот файл js после загрузки. Слишком много кода для переписывания в машинописном тексте, так что это было большим подспорьем. Следующий комментарий Я помещу пример из-за длины комментария

Nour Lababidi 23.09.2018 23:19

Я просто использовал этот код следующим образом: ngAfterViewInit () {debugger; $ .getScript ("/ assets / js / jquery.app.js", function () {alert ("Скрипт загружен и выполнен."); // здесь вы можете использовать все, что вы определили в загруженном скрипте}); }

Nour Lababidi 23.09.2018 23:20

Для '$' в angular я сделал следующее: stackoverflow.com/questions/32050645/…

Nour Lababidi 23.09.2018 23:21

Части этого сообщения кажутся плагиатом с stackoverflow.com/a/950146/6243352

ggorlen 17.12.2020 01:50

Я написал простой модуль, который автоматизирует импорт / включение скриптов модуля в 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
});

Если вы хотите загрузить скрипт СИНХРОНИЗАЦИЯ, вам нужно добавить текст скрипта непосредственно в тег 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()); });

Сделайте его красивым, коротким, простым и удобным в обслуживании! :]

// 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.info("library loaded: ", url);
                    break;
                default:
                    console.info("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]);
    }
}

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

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

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");

можно посмотреть по ссылке: отвечать

asmmahmud 03.08.2017 18:59

Здесь - простой с обратным вызовом и поддержкой 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.info('jquery loaded');

});

Я знаю, что мой ответ на этот вопрос немного запоздал, но вот отличная статья в 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 Я понятия не имею, что это за «асинхронность», но так уж получилось, что я выполняю скрипты, добавленные через JS, в том порядке, в котором они были добавлены.

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

IE Не знаю об «async», но есть обходной путь с использованием «onreadystatechange».

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

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

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"
])

Я использую вышеуказанный подход, он отлично работает в Chrome и Firefox, но сталкивается с проблемами в браузере IE

Mr. Roshan 25.09.2019 09:33

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

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

Вот простой пример функции для загрузки файлов 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");

Предлагается новый стандарт ECMA под названием динамический импорт, недавно включенный в Chrome и Safari.

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

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule

Абсурдный однострочник для тех, кто считает, что загрузка 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(...).

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

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

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

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.info('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.info("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.goldenHour.getMinutes() + ". Take your pics!")
  })
})()

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

Я изменил некоторые из приведенных выше сообщений с помощью рабочего примера. Здесь мы также можем указать 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"]);
});

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

  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.

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

import('./myscript.js');

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

Access to script at 'http://..../myscript.js' from origin 'http://127.0.0.1' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

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

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());

Есть ли причина использовать конструктор Function вместо eval? Из документации MDN Function просто создает функцию и принимает имена параметров, а затем тело функции в качестве аргументов. В то время как eval просто принимает строку кода, что и есть в файле .js.

Michael Hoffmann 09.12.2020 20:02

Другие вопросы по теме