Отложить загрузку в JavaScript, Jquery 'ready()' запускается до загрузки JQuery

Я загружаю два файла JS, один со многими плагинами, включая Jquery. Функция JQuery 'ready()' находится во втором файле.

Код:

   function downloadJSAtOnload() {

       var element = document.createElement("script");
       element.src ='<%= deferJsAll %>';
       document.body.appendChild(element);

      // second JS file load with isReady goes here
   }

   // Check for browser support of event handling capability
   if (window.addEventListener)
       window.addEventListener("load", downloadJSAtOnload, false);
   else if (window.attachEvent)
       window.attachEvent("onload", downloadJSAtOnload);
   else window.onload = downloadJSAtOnload;

You can see when I also load the second file (See comment inside the code).

Проблема в том, что я не могу контролировать порядок выполнения, и даже в этом случае 'ready()' запускается до загрузки JQuery, и я получаю сообщение об ошибке. Есть ли возможность использовать отложенную загрузку и убедиться, что сначала загружаются файлы Jquery, а затем выполняется второй (тот, что с функцией ready())?

Я использую «‹%= deferJsAll %>», потому что я решаю, какой файл обслуживать из CDN, gzip или обычный текстовый JS-файл.

Прогресс: я подумал о том, чтобы просто поставить:

var elementApp = document.createElement("script");
elementApp.src = 'js/app.js';
document.body.appendChild(elementApp);

в конце первого документа. Поэтому, когда первый документ завершит загрузку, он встроит второй зависимый код JS в тело документа. Как вы думаете, это хорошая идея?


person Idan Shechter    schedule 05.07.2012    source источник


Ответы (2)


Вот решение, которое я использовал, когда мне нужно было подождать, пока не появится какая-то переменная на странице. Он принимает имя (например, jQuery или пространство имен twttr.widgets с произвольной глубиной) и другие очевидные атрибуты. Работает в производстве на http://www.vitalmtb.com/, когда мне нужно дождаться виджетов Facebook и Twitter загружены.

// Wait till JS object is loaded. It can be a chanined variable name.
window.whenAvailable = function (name, callback, interval, max_wait) {
    interval || (interval = 50); // ms
    max_wait || (max_wait = 5000); // ms
    var timer,
        findVarByName = function(name) {
        var index = 0,
            parts = name.split('.'),
            result = window;

        index = 0;
        try {
            while (typeof result !== "undefined" && result !== null && index < parts.length) {
                result = result[parts[index++]];
            }
        }
        catch (e) {}
        if (index < parts.length || (typeof result == 'undefined' && result === undefined)) return false;
        else return true;
    };

    timer = window.setTimeout(function() {
        if (findVarByName(name)) {
            return callback();
        } else {
            window.setTimeout(arguments.callee, interval);
        }
    }, interval);

    window.setTimeout(function() {
        clearTimeout(timer);
    }, max_wait);
}

Использовать:

whenAvailable('FB', $j.spotlights.a.show_social_buttons);
whenAvailable('twttr.widgets', $j.spotlights.b.show_social_buttons);
person mvbl fst    schedule 05.07.2012
comment
Хотя я уверен, что это решение отлично подходит, когда у вас нет правильных обратных вызовов, когда что-то загружается. Это своего рода метод грубой силы, который на самом деле не нужен в примере кода OP. Поскольку он контролирует, когда и что загружается (в отличие от сумасшедшей и глючной чепухи в социальных сетях), мы можем использовать встроенные в браузер методы, такие как onload. - person Jon Jaques; 05.07.2012
comment
Хорошо, но вы можете использовать $(document).ready(...) в качестве обратного вызова. В противном случае советую использовать Require.js. - person mvbl fst; 05.07.2012
comment
+1 за требование JS. Если вы приближаетесь к моменту, когда вы асинхронно загружаете более одного файла, то я думаю, что require - это то, что вам нужно. Что касается $doc.ready, я не уверен, что это применимо здесь. Дело в том, что $doc.ready предназначен для определения завершения DOM, а не готовности jQuery. Вы не можете вызвать это до того, как существует jQuery. - person Jon Jaques; 05.07.2012

Вы можете использовать onload и onreadystatechange (для IE), чтобы определить завершение загрузки скрипта.

function loadScript(callback){
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = '<%= deferJsAll %>';
  document.body.appendChild(script);

  // exit on jQuery load.
  script.onload = function(){ callback(); };
  script.onreadystatechange = function() {
    if (this.readyState == 'complete') callback();
  }
}


loadScript(function(){
  $.getScript('<% jquery-dependent.js %>', function(){
     // jquery-dependent.js is loaded
  }
});
person Jon Jaques    schedule 05.07.2012
comment
как насчет размещения: var elementApp = document.createElement(script); elementApp.src = 'js/second_file.js'; document.body.appendChild(elementApp); - person Idan Shechter; 05.07.2012
comment
Затем вы сталкиваетесь с той же проблемой, с которой сталкивались раньше, а именно с тем, что вы не можете легко контролировать порядок выполнения в браузере. Проблема в том, что elementApp может вернуться раньше, чем jQuery, и в этот момент браузер выполнит его. Браузер всегда запускает сценарии в момент их возврата. - person Jon Jaques; 05.07.2012
comment
Чтобы уточнить, вы можете обнаружить, когда скрипт возвращается, но вы не можете контролировать, что браузер делает с ним. - person Jon Jaques; 05.07.2012