15. Введение в jQuery

Обработчик готовности документа

Если скрипт работает с объектами DOM, необходимо чтобы DOM был построен. Это ясно.

Во всех примерах, которые приводились ранее, мы погружали такие коды внутрь обработчика события load, записанного в виде функционального литерала:


// Обработчик события load.
// Функциональный литерал будет  
// вызван после полной загрузки 
// документа и построения его  
// объектной модели.
window.onload = function ()
{
  код, который работает с 
  объектами DOM
};

Кроме того, такой подход позволял обойтись вообще без глобальных имён — все имена являются локальными переменными безымянного обработчика.

Пример 03 содержит модификацию приложения «Как устроена jQuery». Работа с DOM при помощи $ в файле main.js закодирована без ожидания события load. Скрипт не работает, браузеры выдают сообщения об ошибке (если в их настройках разрешено выдавать сообщения об ошибках). Проверьте!

Однако событие load браузер генерирует не сразу после посроения DOM, а только тогда, когда все элементы страницы полностью загружены.

Вот браузер загрузил код страницы, построил DOM, и грузит по сети картинки. Картинки могут грузиться долго, могут вообще не загрузиться (браузер прекращает неудачную загрузку через какое-то время). Пока загрузка продолжается, пользователь смотрит на «сырой» экран, который web-разработчик предполагал обработать своими кодами и превратить в «шоколадку». «Шоколадка» возникнет на экране только после события «load», возможно даже испугав пользователя, давно приступившего к работе с «сырой» страницей.

Было бы хорошо, если бы скрипты начинали работу не после события load, а после события makeDOM (построен DOM)! Но такого события не предусмотрено.

Однако разработчики jQuery утверждают, что в их библиотеке есть способ запуска программного кода сразу после построения дерева DOM, но до загрузки изображений. Вот этот способ:



// Обработчик готовности документа 
$(function ()
{
  код, который работает с объектами DOM
});

Синонимом приведенного выше кода в jQuery является следующая конструкция:


// Обработчик готовности документа 
$(document).ready(
function ()
{
  код, который работает с объектами DOM
});

Но первый вариант, в силу его лаконичности, используется чаще.

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

Чтобы разгадать секрет этого обработчика, смоделируем обработчик готовности документа в нашей собственной демонстрационной библиотеке:


window.$ = jQuery;  // Определяем синоним "$" для "jQuery"
function jQuery(selector)
{
  // Если аргумент selector есть функция, подождать пока  
  // будет готов DOM и выполнить эту функцию
  if (typeof(selector) == "function") 
  {                 
    // Флаг готовности DOM jQuery.isReady определён ниже
    if (jQuery.isReady) selector();  // Если DOM готов, 
                                     // выполнить функцию немедленно
    else // Иначе запустить проверку готовности DOM
    {    // и запомнить функцию, чтобы выполнить её позже
      jQuery.f = selector; // Запомнить ссылку на функцию
      jQuery.ready();      // Запустить проверку готовности DOM
    }
    return;                // Закончить работу
  }
  // Здесь остальная часть функции
  ...
}

// Проверка готовности документа
// -----------------------------
jQuery.isReady = false;  // Флаг готовности DOM (изначально false)
jQuery.ready = function() 
{
  if (!jQuery.isReady) // DOM ещё не построен
  {
    // Ждем, пока появится объект document.body
    if (!document.body) setTimeout(jQuery.ready, 13);
    else 
    { // Если document.body появился, установить флаг готовности в true
      // и запустим отложенную функцию 
      jQuery.isReady = true;   // Флаг готовности в true
      if(jQuery.f) jQuery.f(); // Запустить отложенную функцию
    }
  }  
}

Работает это так. Если аргумент вызова jQuery есть функция, проверяем, построен ли DOM. Если DOM построен, выполняем функцию-аргумент незамедлительно. Если нет, сохраняем ссылку на функцию, запускаем проверку готовности DOM и заканчиваем работу.

Проверку готовности DOM выполняет функция jQuery.ready, определённая как метод объекта jQuery.

Работа функции jQuery.ready начинается с проверки существования объекта document.body. Если объект существует, значит, DOM построен, можно установить флаг готовности в true и запустить отложенную функцию. Если объекта document.body еще нет, запускаем таймер, который будет вызывать jQuery.ready через каждые 13 миллисекунд для новой проверки существования объекта document.body.

Автор библиотеки jQuery выбрал длительность интервала, через который запускается проверка, равную 13 миллисекундам. Почему именно 13, а, например, не 10, я не знаю. Возможно, на этот счет были какие-то соображения.

Заметим, что вариант проверки в обычном цикле (или рекурсии) без таймера не подошел бы. Такая функция либо «подвесит» браузер, либо выдаст панель с сообщением о недопустимом времени исполнении скрипта:


jQuery.ready = function() 
{
  if (!jQuery.isReady) // DOM ещё не построен
  {
    while (!document.body) ;  // Такая проверка не пройдет!
    jQuery.isReady = true;    // Флаг готовности в true
    if(jQuery.f) jQuery.f();  // Запустить отложенную функцию
  }  
}

Дело в том, что браузер не завершит построение DOM, пока не выполнит все «начальные» скрипты, те, выполнение которых не связано с событиями и таймерами.

Следовательно, цикл

while (!document.body) ;

обречен работать «вечно» или до тех пор, пока в браузере не сработает защита от зацикливания (что приведет к выдачи на экран соответствующего диалога).

Вы можете заменить приведенным выше вариантом определение функции jQuery.ready и посмотреть, что получится.

Вариант с таймером будет работать нормально. Строка

if (!document.body) setTimeout(jQuery.ready, 13);

проинициализирует таймер и функция jQuery.ready завершит работу. Затем она будет вызываться (уже таймером) через каждые 13 миллисекунд, и это не помешает браузеру завершить построение DOM.

Пример 04 содержит модификацию приложения «Как устроена jQuery» с использованием проверки готовности документа, код которого был описан выше. Скрипт нормально работает во всех браузерах.