Шаг 42.
Библиотека jQuery.
Изменение движения события. Делегирование событий

    На этом шаге мы рассмотрим пример делегирования событий.

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


   Примечание. В версии jQuery 1.3 появилась пара новых методов, .live() и .die(). Они решают те же задачи, что и методы .bind() и .unbind(), но для получения преимуществ, описываемых на данном шаге, они используют прием делегирования событий. Описание этих методов можно найти по адресу: http://docs.jquery.com/Events/live

    В нашем примере имеются всего три элемента <div class="button">, к которым подключаются обработчики события click. Но как быть, если элементов намного больше? Такая ситуация встречается намного чаще, чем может показаться. Представим, например, большую таблицу, где каждая строка содержит интерактивный элемент, для взаимодействия с которым требуется определить обработчик события click. Механизм неявной итерации позволяет легко присоединить обработчики событий ко всем таким элементам, но производительность при этом может пострадать, потому что внутренняя реализация библиотеки jQuery все равно выполняет цикл и для поддержки всех обработчиков потребуется большой объем памяти.

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

    Для примера применим эту методику к нашему переключателю стилей (невзирая на то, что малое число элементов не требует этого). Как отмечалось выше, для определения, какой элемент находился под указателем мыши в момент щелчка, можно использовать свойство event.target.

 $(document).ready(function() {
   $('#switcher').click(function(event) {
     if ($(event.target).is('.button')) {
       $('body').removeClass();
       if (event.target.id == 'switcher-narrow') {
         $('body').addClass('narrow');
       }
       else if (event.target.id == 'switcher-large') {
         $('body').addClass('large');
       }
     
       $('#switcher .button').removeClass('selected');
       $(event.target).addClass('selected');
       event.stopPropagation();
     }
   });
   $('#switcher').click(function() {
       $('#switcher .button').toggleClass('hidden');
   });
 });

Рис.1. Пример делегирования события

Полный текст этого примера можно взять здесь.

    Здесь мы использовали новый метод с именем .is(). Он принимает селекторные выражения, которые мы рассматривали в предыдущих шагах, и проверяет соответствие селектору текущего объекта jQuery. Если хотя бы один элемент множества соответствует селектору, метод .is() возвращает значение true. В данном случае выражение $(event.target). is('button') проверяет, имеет ли элемент, на котором был выполнен щелчок, класс button. Если проверка дает положительный результат, выполняется тот же программный код, что и прежде, за одним важным исключением: теперь ключевое слово this ссылается на элемент <div id="switcher">, поэтому всякий раз, когда требуется выяснить, на какой кнопке произошел щелчок, необходимо использовать свойство event.target.


   Примечание. Мы могли бы проверить наличие класса у элемента также с помощью метода .hasClass(). Однако метод .is() обеспечивает большую гибкость и может выполнять проверку на соответствие любому селектору.

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

 $(document).ready(function() {
   $('#switcher').click(function(event) {
     if (!$(event.target).is('.button')) {
       $('#switcher .button').toggleClass('hidden');
     }
   });
 });

 $(document).ready(function() {
   $('#switcher').click(function(event) {
     if ($(event.target).is('.button')) {
       $('body').removeClass();
       if (event.target.id == 'switcher-narrow') {
         $('body').addClass('narrow');
       }
       else if (event.target.id == 'switcher-large') {
         $('body').addClass('large');
       }
     
       $('#switcher .button').removeClass('selected');
       $(event.target).addClass('selected');
     }
   });
 });

Рис.2. Исправленный пример делегирования события

Полный текст этого примера можно взять здесь.

    Для данного случая этот пример слишком сложен, но с ростом числа элементов, к которым требуется подключить обработчики событий, делегирование событий становится более предпочтительным приемом.


   Примечание. Делегирование событий с успехом может применяться и в других ситуациях, с которыми мы познакомимся позднее, например при добавлении новых элементов методами манипулирования деревом DOM или процедурами АJАХ.

    На следующем шаге мы рассмотрим удаление обработчика события.




Предыдущий шаг Содержание Следующий шаг