Динамически включать JavaScript и ждать - PullRequest
5 голосов
/ 18 октября 2010

То, что я пытаюсь сделать, это динамически включать один или несколько файлов js из Javascript.Я знаю, что есть множество методов для этого, но я ищу решение, которое следует этим правилам:

1) Не используйте какие-либо фреймворки или библиотеки JS (jQuery, Prototype и т. Д.).

2) Сценарий должен остановить выполнение, пока внешний js-файл не будет полностью загружен и проанализирован браузером.

Рациональным основанием # 2 является то, что внешний js-файл будет включать определения функций, которые необходимо использовать немедленнопосле того, как сценарий включен.Большую часть времени браузер не успел проанализировать файл js, прежде чем я начну вызывать эти функции.

Я не против использовать функцию обратного вызова, чтобы узнать, когда скрипт завершит загрузку, но:

1) Я не знаю заранее, сколько сценариев будет включено динамически, поэтому я не хочу и не могу написать несколько вложенных обратных вызовов.Мне просто нужно знать, когда они все загрузятся.

2) Я формулирую, что попытка использовать какое-то событие «загрузки» для запуска обратного вызова может не работать, если браузер кэшировал JavaScript.

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

Пользователи будут определять, какие плагины они хотят загрузить в массив.

plugins = [ 'FooPlugin', 'BarPlugin' ];

Затем я буду циклически проходить по массиву и загружать сценарий js для каждого плагина:

for(var i = 0; i < plugins.length; i++) {
    loadScript('plugins/' + plugins[i] + '.js');
}

Каждый плагин помещает себя в массивloaded_plugins (это пример FooPlugin.js)

load_plugins.push({
    name: 'FooPlugin',
    // Other plugin methods and properties here
});

Ответы [ 3 ]

3 голосов
/ 18 октября 2010

Работает кросс-браузер, начиная с IE6.

document.loadScript = function (src, callback) {
    function script(src, onload) {
        var scriptTag = document.createElement('script');
        if (onload) scriptTag.onload = onload;
        scriptTag.src = src;
        scriptTag.type = 'text/javascript';
        return scriptTag;
    }
    function outer(tag) { 
        var d = document.createElement('div');
        d.appendChild(tag);
        return d.innerHTML;
    }
    if (!(src instanceof Array)) src = [src];
    var i, scr, 
        callbackId = 'dynCall_'+Math.floor(Math.random()*89999+10000);
        counter = src.length, 
        call = function () { if (--counter == 0) callback(); };
    if (!document.body) {
        window[callbackId] = function () {
            delete window[callbackId];
            if (callback instanceof Function) callback();
        };
        for (i=0; i<src.length; i++) document.write(outer(script(src[i])));
        document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>');
        return;
    }
    for (i=0; i<src.length; i++) document.body.appendChild(script(src[i], call));
};

Минимизировано / запутано:

(function(){document.loadScript=function(src,callback){function script(src,onload){var scriptTag=document.createElement('script');if(onload)scriptTag.onload=onload;scriptTag.src=src;scriptTag.type='text/javascript';return scriptTag}function outer(tag){var d=document.createElement('div');d.appendChild(tag);return d.innerHTML}if(!(src instanceof Array))src=[src];var i,scr,callbackId='dynCall_'+Math.floor(Math.random()*89999+10000);counter=src.length,call=function(){if(--counter==0)callback()};if(!document.body){window[callbackId]=function(){delete window[callbackId];if(callback instanceof Function)callback()};for(i=0;i<src.length;i++)document.write(outer(script(src[i])));document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>');return}for(i=0;i<src.length;i++)document.body.appendChild(script(src[i],call))};document.loadScript.toString=function(){return'function loadScript() { [obfuscated] }'}})();

Краткое объяснение веток:

Если тег скрипта, вызывающий loadScript, находится внутри заголовка или тела документа, document.body будет неопределенным, так как DOM еще не включен. Таким образом, мы не можем использовать стандартный метод добавления тега, и мы должны использовать doc write. Это имеет то преимущество, что теги, которые мы пишем, будут происходить в обязательно последовательном порядке, но недостатком является то, что у нас должна быть функция обратного вызова глобальной области видимости.

В то же время, если у нас есть document.body, мы отлично понимаем, что делаем правильно (-ish - мы приносим жертвы, когда вокруг нет библиотек, чтобы помочь сделать это правильно, поэтому .onload (). Тем не менее, это не то, чтобы мы собирались выбросить много событий в тег скрипта, так что, вероятно, все будет в порядке.) Недостатком (может быть?) является то, что все скрипты загружаются асинхронно, поэтому мы должны запустить обратный отсчет во время .

2 голосов
/ 18 октября 2010

Самое простое (и самое надежное) решение, которое я знаю, - это просто использовать document.write для вставки нового тега скрипта в документ (или, конечно, столько, сколько вам нужно):

<script type="text/javascript">document.write('<' + 'script src="/assets/js/file.js" type="text/javascript"><\/script>');</script>

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

РЕДАКТИРОВАТЬ : Вот (намного) более сложныйподход , который в основе использует "elem = document.createElement ('script')" с последующим прослушиванием elem.onreadystatechange (для IE) и elem.onload.Будет загружать всю партию сценариев параллельно, а затем запускать обратный вызов, когда все они есть.Использовали это в прошлом, когда браузеры все еще в основном загружали скрипты по одному для ускорения загрузки (он основан на скрипте с аналогичными целями где-то там).Пример использования:

Loader.script( [
    './js/jquery/jquery.jmap.js',
    './js/jquery/jquery.getUrlParam.js',
    './js/jquery/jquery.form.js',
    './js/jquery/jquery.gettext.js',
    './js/jquery/jquery.scrollTo.js',
    './js/jquery/jquery.prettydate.js'
  ], { complete: function() {
    // do your thing
  }
})
0 голосов
/ 18 октября 2010

Несколько возможных подходов с макушки головы. Я собираюсь написать шаги, а не код.

Вариант 1

  1. Загрузите файл JS, поместив его в элемент с тегом.
  2. Сразу после запуска setTimout () для проверки переменной в загружаемом вами файле JS.
  3. Повторяйте # 2, пока не появится переменная или пока не будет достигнут maxCounter (maxCounter = 10), чтобы вы не сидели там вечно.
  4. Как только переменная появится, перейдите к следующему файлу JS и снова начните с шага 1.

Вариант 2

  1. Используйте подход Ajax.
  2. Вызовите файл JS с помощью Ajax.
  3. Добавьте результаты к инструкциям по использованию в статье ниже.
  4. Функция setTimeout () устанавливается на несколько секунд и обрабатывает сценарий ИЛИ проверяет переменную, аналогичную опции # 1, и повторяет тайм-аут до появления переменной.

ссылка на эту статью для обоих подходов: http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS

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

Я бы предпочел такой подход:

1 . Создайте тег запроса Javascript ajax для сервера, который может обрабатывать динамическую логику (php, java, .NET и т. Д.).

2 . Передайте функцию обратного вызова, которая уже существует на странице и знает, сколько дополнительных файлов JS вам нужно знать. Даже включите параметр индекса в обратный вызов (что-то вроде myCallback (5)), чтобы вы знали, где вы находитесь.

1-2a . Вы можете просто добавить функцию обратного вызова ко всем вашим JS-файлам вручную и позволить этой функции управлять очередью JS без каких-либо знаний о файлах JS, кроме функции обратного вызова.

3 . Пусть сервер добавляет обратный вызов к возвращаемому файлу JS.

4 . Функция обратного вызова будет активирована после обработки JS, и она будет управлять загрузкой дополнительных файлов JS.

Как насчет этого?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...