Сегодня прочитал Перестаньте платить дань jQuery, хорошую статью от Sam Saffron, которая поясняет идею перемещения всех внешних скриптов в конец HTML документа, и метода который позволит использовать jQuery .ready()
везде в вашем документе, даже если вы переместили сам jQuery в конец документа. Я метод немного доработал.
Его метод, по существу, это:
- В теге
head
включите скрипт который:- Определяет коллекцию,
- Создает фальшивую функцию
$
которая добавляет аргументы в коллекцию.
- В теле документа, прямо после того как вы подключили jQuery, добавте скрипт который:
- Использует цикл из jQuery для прохождения по полекции,
- ...и вызывает реальную функцию
$
передавая ей аргумент.
Я решил поиграться немного с примерами которые дал Сем и понял что она работает только для одного варианта использования DOM ready
$(handler) // Где `handler` функция для вызова
Но jQuery также позволяет следующие форматы:
$(document).ready(handler)
$().ready(handler) // Хотя это и не рекомендуется
$(document).bind("ready", handler)
Решение
Помня об этом я попробовал создать по концепции Сема, но пришел к решению которое покрывает все четыре варианты. Вот к чему я пришел...
В теге head
, добавте:
<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>
В теге body
, прямо после подключения jQuery, добавте:
<script>(function($,d){$.each(readyQ,function(i,f){$(f)});$.each(bindReadyQ,function(i,f){$(d).bind("ready",f)})})(jQuery,document)</script>
Да, это нечитаемая смесь, но я раскрою детали (с красивыми именами переменных и функций).
После раскрытия скрипта из head
, мы получим:
(function (w, d, u) {
// Определение двоих коллекций для обработчиков
w.readyQ = [];
w.bindReadyQ = [];
// добавлет обработчик в правильную коллекцию
function pushToQ(x, y) {
if (x == "ready") {
w.bindReadyQ.push(y);
} else {
w.readyQ.push(x);
}
}
// определение псевдоним для использования позже
var alias = {
ready: pushToQ,
bind: pushToQ
}
// Определение фальшивой jQuery функции для вылова обработчиков
w.$ = w.jQuery = function (handler) {
if (handler === d || handler === u) {
// $(document).ready(handler), $().ready(handler)
// и $(document).bind("ready", handler) возвращает псевдоним с методами pushToQ
return alias;
} else {
// $(handler)
pushToQ(handler);
}
}
})(window, document);
Если вы посмотрите на документацию метода jQuery .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)
должны быть вызваны раньше, мы связываем их тоже в правильном порядке
Example
Вот простой пример использования скриптов с выводом информации в консоль.
<!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>
<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.