14. Обработка событий

События в модели DOM W3C

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

Новый обработчик удаляет ссылку на старый

Каждый новый обработчик удаляет ссылку на старый.

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

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

Регистрация обработчика события в DOM W3C

Проблема решена в модели DOM W3C. В ней обработчики событий устанавливаются не записью ссылки в свойство объекта, а с помощью специального метода addEventListener. Ничто не мешает при помощи этого метода создать несколько обработчиков одного и того же события, все они будут вызываться при наступлении события, правда, стандарт не гарантирует, что вызовы обработчиков будут происходить в порядке их регистрации.

Формат метода-регистратора обработчика события в DOM W3C:

addEventListener(eventType, listener, useCapture)

Аргументы:

Распространение события в модели DOM W3C

Как было сказано в описании аргумента useCapture, события в DOM W3C распространяются в два этапа: сначала от корня к элементу-источнику (по родственным связям), затем в обратном направлении — от элемента-источника к корню. Первый этап называется фазой захвата, второй — фазой всплытия (как и в ранней модели).

Третий аргумент метода addEventListener указывает, будет ли обработчик реагировать на событие только на фазе захвата (true) или только на фазе всплытия (false). Отметим, что сам факт вызова обработчика не прекращает распространение события. Если обработчик среагировал на событие, но в нём не был вызван метод stopPropagation объекта Event, событие продолжает распространение.

Чтобы разобраться с фазами распространения событий, воспользуемся прежним испытательным стендом, который был построен для демонстрации всплытия.

Зарегистрируем обработчики событий для каждой фазы на элементах IMG, P, BODY, HTML, образующих родственную цепочку:

Файл main.js (фрагмент)


// Назначим обработчик события объектам на фазе захвата
document.getElementById("pic").addEventListener("click",say1,true);
document.getElementById("p").addEventListener("click",say1,true);
document.getElementById("body").addEventListener("click",say1,true);
document.getElementById("html").addEventListener("click",say1,true);

// Назначим обработчик события объектам на фазе всплытия
document.getElementById("pic").addEventListener("click",say2,false);
document.getElementById("p").addEventListener("click",say2,false);
document.getElementById("body").addEventListener("click",say2,false);
document.getElementById("html").addEventListener("click",say2,false);

// Обработка на этапе захвата
function say1()
{
  var text = "Захват, id элемента: " + this.id;
  appendP(text); // Добавим абзац в документ
}
// Обработка на этапе всплытия
function say2()
{
  var text = "Всплытие, id элемента: " + this.id;
  appendP(text); // Добавим абзац в документ
}

Полные коды этого испытательного стенда приводятся на странице примеры («Пример 02. Тестируем распространение события (захват и всплытие)»).

Захват, id элемента: html
Захват, id элемента: body
Захват, id элемента: p
Захват, id элемента: pic
Всплытие, id элемента: pic
Всплытие, id элемента: p
Всплытие, id элемента: body
Всплытие, id элемента: html

Захват и всплытие события

Запустите index.htm (в любом браузере, кроме IE) и щёлкните на картинке. На странице появится протокол работы обработчиков.

Протокол наглядно демонстрирует прохождение события в обеих фазах.

Удаление обработчика

Пару с методом addEventListener образует метод removeEventListener — он удаляет, зарегистрированный ранее, обработчик события.

Удаление может потребоваться в ситуации временной регистрации обработчика. Например, в задаче о перетаскивании объектов по экрану, обработчики событий mousedown (нажатие кнопки мыши) регистрируются на весь срок работы скрипта. Когда это событие наступает, временно регистрируются два новых обработчика: mousemove (перемещение мыши) и mouseup (отпускание кнопки). Когда перетаскивание заканчивается, обработчики событий mousemove и mouseup удаляются. Действительно, нет смысла реагировать на перемещение мыши после того, как кнопка отпущена.

Формат метода removeEventListener:

removeEventListener(eventType, listener, useCapture)

Метод удаляет указанную функцию-обработчик события. Аргументы должны иметь те же значения, что и при вызове метода addEventListener. Если обработчик, соответствующий указанным значениям аргумента, не найден, метод ничего делать не будет.

Отметим, что методы addEventListener и removeEventListener принадлежат интерфейсам Element и Document.