Использование .ready() перед включением jQuery
В этой заметке предлагается метод, который позволяет использовать метод jQuery.ready() в любом месте документа, даже если сам jQuery подключается в самом низу. Отложенная инициалзация jQuery.
Идея в том, чтобы переместить JavaScript код в конец HTML-документа, используя для этого сам JavaScript. Идея была взята от сюда Stop paying your jQuery tax.
Метод заключается в следующем:
-
В
headвключается сценарий, который:- Создает массив.
- Создает поддельную функцию $, которая перемещает аргументы вызова в этот массив.
- В
body, после включения jQuery, ставим скрипт, который:- Использует jQuery для перебора содержимого нашего массива.
- ...и вызывает настоящую функцию $, передавая сохраненные аргументы (функции).
Реализация для WordPress
Для WordPress эта идея реализовывается таким PHP кодом в файле functions.php:
/**
* Ability to use jQuery.ready() before jQuery is enabled
* 0 priority is important in some cases.
* The connection splits into two parts: in head and on DOMContentLoaded.
*
* @see https://wp-kama.ru/note/bezopasnoe-ispolzovanie-ready-pered-vklyucheniem-jquery
*/
add_action( 'wp_head', 'jquery_ready_catch', 1 );
function jquery_ready_catch(){
?>
<script id="jquery_ready_catch">
(function(w,d,u){w.readyQ=[];w.bindReadyQ=[];function p(x,y){if(x==='ready'){w.bindReadyQ.push(y);}
else{w.readyQ.push(x);}};var a={ready:p,bind:p};w.$=w.jQuery=function(f){if(f===d||f===u){return a}
else{p(f)}}})(window,document);
document.addEventListener( 'DOMContentLoaded', function(){
jQuery.each(readyQ,(i,f)=>{ jQuery(f) });
jQuery.each(bindReadyQ,function(i,f){ jQuery(document).bind('ready',f)} );
} )
</script>
<?php
}
Пояснения
Рассмотрим минифицированный JS код выше в нормальном виде:
(function (w, d, u) {
// Define two queues for handlers
w.readyQ = [];
w.bindReadyQ = [];
// Push a handler into the correct queue
function pushToQ(x, y) {
if (x == "ready") {
w.bindReadyQ.push(y);
} else {
w.readyQ.push(x);
}
}
// Define an alias object (for use later)
var alias = {
ready: pushToQ,
bind: pushToQ
}
// Define the fake jQuery function to capture handlers
w.$ = w.jQuery = function (handler) {
if (handler === d || handler === u) {
// Queue $(document).ready(handler), $().ready(handler)
// and $(document).bind("ready", handler) by returning
// an object with alias methods for pushToQ
return alias;
} else {
// Queue $(handler)
pushToQ(handler);
}
}
})(window, document);
Если вы посмотрите документацию по методу jQuery.ready(), там объясняется, что если обработчики привязаны к DOM ready с помощью функции .bind(), то на самом деле они срабатывают после срабатывания всех других обработчиков. Именно поэтому у нас есть две очереди - для того, чтобы соблюсти это поведение.
Развернув сценарий body (сразу после jQuery), мы получаем:
(function ($, doc) {
$.each(readyQ, function (index, handler) {
$(handler);
});
$.each(bindReadyQ, function (index, handler) {
$(doc).bind("ready", handler);
});
})(jQuery, document);
Точно так же, как в примере Сэма, мы используем метод jQuery.each(), чтобы правильно связать все наши обработчики очереди с DOM ready, но поскольку $(document).bind("ready", handler) мог быть вызван раньше, мы свяжем их тоже правильным образом.
Пример
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script>
(function(w,d,u){w.readyQ=[];w.bindReadyQ=[];
function p(x,y){if(x=="ready"){w.bindReadyQ.push(y);}else{w.readyQ.push(x);}};
var a={ready:p,bind:p};w.$=w.jQuery=function(f){if(f===d||f===u){
return a}else{p(f)}}}
)(window,document)
</script>
</head>
<body>
<script>
$(document).bind("ready", function () {
console.log("Example D: $(document).bind(\"ready\", handler)");
});
$(document).ready(function () {
console.log("Example A: $(document).ready(handler)");
});
$().ready(function () {
console.log("Example B: $().ready(handler)");
});
$(function(){
console.log("Example C: $(handler)");
});
</script>
<!-- HTML КОД СТРАНИЦЫ -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
(function($,d){$.each(readyQ,function(i,f){$(f)});
$.each(bindReadyQ,function(i,f){$(d).bind("ready",f)})})(jQuery,document)
</script>
</body>
</html>
Вывод:
Example A: $(document).ready(handler)
Example B: $().ready(handler)
Example C: $(handler)
Example D: $(document).bind("ready", handler)
Обратите внимание, что хотя Example D первый, в нем используется $(document).bind("ready", handler), поэтому он ставится в очередь и выполняется после трех других примеров. Он ведет себя именно так, как задумано jQuery.
--