14. Обработка событий
События в модели DOM W3C
В ранней модели за одним элементом нельзя закрепить несколько обработчиков одного события.
Каждый новый обработчик удаляет ссылку на старый. |
Ссылки на обработчики событий в ранней модели DOM хранятся в событийных свойствах объектов DOM (даже если они задаются событийными атрибутами в тегах). Отсюда следует, что за элементом нельзя закрепить более одного обработчика на каждое событие. Это является серьезным недостатком, и вот почему. Когда к странице подключается несколько скриптов, а так бывает часто, каждый из них может устанавливать на одни и те же элементы свои собственные обработчики событий. В рамках ранней модели ссылки на обработчики записываются в соответствующие свойства объекта DOM, следовательно, каждый подключаемый к странице скрипт может присвоить этому свойству свое собственное значение, удалив тем самым, ссылку, записанную ранее другим скриптом. |
Регистрация обработчика события в DOM W3C
Проблема решена в модели DOM W3C. В ней обработчики событий устанавливаются не записью ссылки в свойство объекта, а с помощью специального метода addEventListener. Ничто не мешает при помощи этого метода создать несколько обработчиков одного и того же события, все они будут вызываться при наступлении события, правда, стандарт не гарантирует, что вызовы обработчиков будут происходить в порядке их регистрации.
Формат метода-регистратора обработчика события в DOM W3C:
addEventListener(eventType, listener, useCapture)
Аргументы:
-
eventType — строка, тип события, для обработки которого будет вызываться обработчик. Тип события совпадает с именем событийного свойства или атрибута ранней модели DOM, но без префикса «on». Например, "click", "mouseover", "mouseout".
-
listener — ссылка на функцию-обработчик. Функции передается один аргумент — объект с интерфейсом Event, относящийся к произошедшему событию. Обработчик вызывается как метод элемента, в котором он зарегистрирован. Это означает, что ключевое слово this в обработчике будет относиться именно к этому элементу (и не всегда совпадать с тем элементом, в котором событие возникло).
Такое поведение реализуют все современные браузеры (кроме IE, но тут разговор отдельный). Однако такое поведение не прописано в стандарте. Стандарт предлагает для связи с источником события и с объектом, в котором зарегистрирован обработчик, два свойства интерфейса Event: target — ссылка на источник события и currentTarget — ссылка на объект, который среагировал на событие (к которому привязан обработчик).
-
useCapture — логическое значение. Если false, то обработчик будет работать так же, как в ранней модели, то есть будет реагировать на событие либо в момент его возникновения, либо на этапе всплытия. Если этот аргумент равен true, обработчик будет работать только на фазе захвата. Дело в том, что распространение события в DOM W3C происходит в два этапа. Сначала событие передаётся от корня к элементу-источнику (фаза захвата), затем обратно, от элемента-источника к корню (фаза всплытия).
Распространение события в модели 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
|
Запустите index.htm (в любом браузере, кроме IE) и щёлкните на картинке. На странице появится протокол работы обработчиков. Протокол наглядно демонстрирует прохождение события в обеих фазах. |
Удаление обработчика
Пару с методом addEventListener образует метод removeEventListener — он удаляет, зарегистрированный ранее, обработчик события.
Удаление может потребоваться в ситуации временной регистрации обработчика. Например, в задаче о перетаскивании объектов по экрану, обработчики событий mousedown (нажатие кнопки мыши) регистрируются на весь срок работы скрипта. Когда это событие наступает, временно регистрируются два новых обработчика: mousemove (перемещение мыши) и mouseup (отпускание кнопки). Когда перетаскивание заканчивается, обработчики событий mousemove и mouseup удаляются. Действительно, нет смысла реагировать на перемещение мыши после того, как кнопка отпущена.
Формат метода removeEventListener:
removeEventListener(eventType, listener, useCapture)
Метод удаляет указанную функцию-обработчик события. Аргументы должны иметь те же значения, что и при вызове метода addEventListener. Если обработчик, соответствующий указанным значениям аргумента, не найден, метод ничего делать не будет.
Отметим, что методы addEventListener и removeEventListener принадлежат интерфейсам Element и Document.