Обработка кода, который опирается на jQuery перед загрузкой jQuery - PullRequest
31 голосов
/ 28 ноября 2011

Я хотел бы следовать общему руководству по размещению всего JavaScript в самом низу страницы, чтобы ускорить время загрузки, а также позаботиться о некоторых неприятных проблемах с конфликтующими версиями jQuery в веб-приложении (Django).

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

Мне интересно, есть ли простой способ, чтобы закодировать это так, чтобы, хотя jQuery еще не определен, код работает, когда определен jQuery.Не знаю другого способа сделать это:

function run_my_code($) {
    // jquery-dependent code here
    $("#foo").data('bar', true);
}
var t = null;
function jquery_ready() {
    if (window.jQuery && window.jQuery.ui) {
        run_my_code(window.jQuery);
    } else {
        t = window.setTimeout(jquery_ready, 100);
    }
}
t = window.setTimeout(jquery_ready, 100);

На самом деле, мне может понадобиться использовать код на странице более одного раза, код, который не знает о другом коде, так что даже это, вероятно, победит 'это не сработает, если я не переименую каждый jquery_ready во что-то вроде jquery_ready_ guid , jquery_ready_ otherguid и т. д.

Разъяснение

Просто так,Я добавляю код JavaScript (<script type="text/javascript" src="jquery.min.js" />) в в самом низу страницы, прямо перед </body>.Поэтому я не могу использовать $.

Ответы [ 10 ]

17 голосов
/ 03 сентября 2014

Лучший способ, который я нашел, - это написать код в функции и вызвать функцию после загрузки jquery:

function RunAfterjQ(){
// Codes that uses jQuery
}

<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
    RunAfterjQ();
</script>

Обновление: Для главных страниц вы можете определитьмассив для добавления функций в начало главной страницы:

var afterJQ = [];

, затем в нижней части главной страницы запускаются все функции, помещенные в этот массив:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
    for(var i = 0; i < afterJQ.length; i++) afterJQ[i]();
</script>

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

afterJQ.push( function() { 
    // this code will execute after jQuery is loaded.
 });
17 голосов
/ 28 ноября 2011

Ваш путь - единственный известный мне путь, хотя я бы позаботился о том, чтобы область видимости была немного теснее:

(function() {
  var runMyCode = function($) {
    // jquery-dependent code here
    $("#foo").data('bar', true);
  };

  var timer = function() {
    if (window.jQuery && window.jQuery.ui) {
      runMyCode(window.jQuery);
    } else {
      window.setTimeout(timer, 100);
    }
  };
  timer();
})();

Обновление

Вотмаленький отложенный загрузчик, который я собрал вместе:

var Namespace = Namespace || { };
Namespace.Deferred = function () {
  var functions = [];
  var timer = function() {
    if (window.jQuery && window.jQuery.ui) {
        while (functions.length) {
            functions.shift()(window.jQuery);
        }
    } else {
        window.setTimeout(timer, 250);
    }
  };
  timer();
  return {
    execute: function(onJQueryReady) {
        if (window.jQuery && window.jQuery.ui) {
            onJQueryReady(window.jQuery);
        } else {
            functions.push(onJQueryReady);
        }
    }
  };
}();

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

Namespace.Deferred.execute(runMyCode);
16 голосов
/ 14 апреля 2017

Простая в использовании чистая версия JavaScript $(document).ready();:

document.addEventListener("DOMContentLoaded", function(event) { 
    //you can use jQuery there
});
10 голосов
/ 23 марта 2015

Вот способ написать внедренный код, который будет запускаться только после загрузки jQuery (синхронно или асинхронно).

<script>
if ( ! window.deferAfterjQueryLoaded ) {
    window.deferAfterjQueryLoaded = [];
    Object.defineProperty(window, "$", {
        set: function(value) {
            window.setTimeout(function() {
                $.each(window.deferAfterjQueryLoaded, function(index, fn) {
                    fn();
                });
            }, 0);
            Object.defineProperty(window, "$", { value: value });
        },

        configurable: true
    });
}

window.deferAfterjQueryLoaded.push(function() {
    //... some code that needs to be run
});
</script>

Что это делает:

  1. Определяет deferAfterjQueryLoaded лениво, поэтому вам не нужно вводить это в head.
  2. Определяет setter для window.$. Когда jQuery загружается, одно из последних назначений - глобальная переменная $. Это позволяет вам запускать функцию, когда это происходит.
  3. Запланирует запуск отложенных функций как можно скорее после завершения работы скрипта jQuery (setTimeout(..., 0);).
  4. Может ли сеттер удалить себя.

Для полной чистоты вы можете также удалить функцию по расписанию deferAfterjQueryLoaded.

7 голосов
/ 18 марта 2015

Как насчет:

<script>
    window.deferAfterjQueryLoaded = [];
    window.deferAfterjQueryLoaded.push(function() {
        //... some code that needs to be run
    });

    // ... further down in the page

    window.deferAfterjQueryLoaded.push(function() {
        //... some other code to run
    });
</script>

<script src="jquery.js" />
<script>
    $.each(window.deferAfterjQueryLoaded, function(index, fn) {
        fn();
    });
</script>

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

Если вы также хотите, чтобы DOMReady запускал, вы можете вложить $(function() {}) в одну из ваших функций deferAfterjQueryLoaded, например:

window.deferAfterjQueryLoaded.push(function() {
    $(function() {
        console.log('jquery loaded and the DOM is ready');
    });
    console.log('jquery loaded');
});

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

4 голосов
/ 28 ноября 2011

У меня такая же проблема, но с кучей файлов я использую headjs для управления загрузкой, это всего лишь 2 КБ, так что в любом случае для меня на самом деле не проблема вставитьзаголовок.Ваш код становится

head.ready(function(){
  $...
});

, а внизу страницы

head.js('/jquery.min.js');
1 голос
/ 21 апреля 2017

Вы можете отложить все вызовы, такие как jQuery (function () {...}) без цикла setTimeout: https://jsfiddle.net/rL1f451q/3/

Он собирает каждый вызов jQuery (...) в массив до тех пор, пока jQuery не будет определен, затем второй код выполняет их, когда jQuery доступен.

Поместите это в голову или начало тела:

<!-- jQuery defer code body: deferring jQuery calls until jQuery is loaded -->
<script>
  window.jQueryQ = window.jQueryQ || [];
  window.$ = window.jQuery = function(){
    window.jQueryQ.push(arguments);
  }
</script>
<!-- end: jQuery defer code body -->

И это в самом конце тела, после скрипта jQuery:

<!-- jQuery deferring code footer: add this to the end of body and after the jQuery code -->
<script>
  jQuery(function(){
    jQuery.each(window.jQueryQ||[],function(i,a){
      // to understand why setTimeout 0 is useful, see: https://www.youtube.com/watch?v=8aGhZQkoFbQ, tldr: having a lot of calls wont freeze the website
      setTimeout(function(){
        jQuery.apply(this,a);
      },0);
    });
  });
</script>
<!-- end: jQuery deferring code footer -->
1 голос
/ 24 марта 2015
function jQueryGodot(code)
{
    if (window.jQuery)
    {
        code(window.jQuery);        
    }
    else
    {
        if (!window.$)
        {
            window.$ = { codes: [] };
            window.watch('$', function(p, defered, jQuery) {
                jQuery.each(defered.codes, function(i, code) {
                    code(jQuery);
                });
                return jQuery;
            });
        }

        window.$.codes.push(code);
    }
}

jQueryGodot(function($) {
    $('div').html('Will always work!');
})

Рабочий пример для JSFiddle .

Код, переданный в функцию jQueryGodot, всегда будет выполняться независимо от того, вызывается ли он до или после загрузки jQuery.

Решение опирается на Object.watch, который требует этого полифилла (минимизировано 660 байт) в большинстве браузеров: https://gist.github.com/adriengibrat/b0ee333dc1b058a22b66

1 голос
/ 28 ноября 2011

Вы должны быть в состоянии сделать это для события document ready.

0 голосов
/ 07 июня 2019

Я написал простой js-код для обработки таких случаев: https://github.com/Yorkii/wait-for

Затем вы можете использовать его вот так

waitFor('jQuery', function () {
   //jQuery is loaded
   jQuery('body').addClass('done');
});

Вы можете даже подождать несколько библиотек

waitFor(['jQuery', 'MyAppClass'], function () {
   //Both libs are loaded
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...