Марк Аврелий, «Медитации»
Некоторые программы работают с вводом пользователя, мышью и клавиатурой. Время возникновения такого ввода и последовательность данных нельзя предсказать заранее. Это требует иного подхода к контролю над порядком выполнения программы, чем уже привычный нам.
Представьте интерфейс, в котором единственным способом узнать, нажали ли на кнопку клавиатуры, было бы считывание текущего состояния кнопки. Чтобы реагировать на нажатия, вам пришлось бы постоянно считывать состояния кнопок, чтобы вы могли поймать это состояние, пока кнопка не отжалась. Было бы опасно проводить другие подсчёты, отнимающие процессорное время, так как можно было бы пропустить момент нажатия.
Таким образом ввод обрабатывался на примитивных устройствах. Шагом вперёд было бы, если железо или операционка замечали бы нажатие кнопки и передавали бы его в очередь. Затем программа периодически могла бы проверять очередь на новые события и реагировать на то, что находится в очереди.
Разумеется, она должна помнить о проверке, и делать это достаточно часто, потому что наличие длительного промежутка времени между нажатием кнопки и тем, когда программа замечает и реагирует на это, ведёт к восприятию этой программы как медленно работающей. Такой подход используется достаточно редко.
Вариант получше – некая промежуточная система, которая позволяет коду реагировать на события в момент их возникновения. Браузеры позволяют это делать путём регистрации функций как обработчиков заданных событий.
Щёлкните по документу для запуска обработчика.
addEventListener("click", function() {
console.log("Щёлк!");
});
Функция addEventListener
регистрирует свой второй аргумент как функцию, которая вызывается, когда описанное в первом аргументе событие случается.
Каждый обработчик событий браузера зарегистрирован в контексте. Когда вы вызываете addEventListener
, вы вызываете её как метод целого окна, потому что в браузере глобальная область видимости – это объект window
. У каждого элемента DOM есть свой метод addEventListener
, позволяющий слушать события от этого элемента.
Нажми меня нежно.
А здесь нет обработчиков.
var button = document.querySelector("button");
button.addEventListener("click", function() {
console.log("Кнопка нажата.");
});
Пример назначает обработчик на DOM-узел кнопки. Нажатия на кнопку запускают обработчик, а нажатия на другие части документа – не запускают.
Присвоение узлу атрибута onclick
работает похоже. Но у узла есть только один атрибут onclick
, значит таким способом вы можете зарегистрировать только один обработчик. Метод addEventListener
позволяет добавлять любое количество обработчиков, так что вы не замените случайно уже назначенный ранее обработчик.
Метод removeEventListener
, вызванный с такими же аргументами, как addEventListener
, удаляет обработчик.
Act-once button
var button = document.querySelector("button");
function once() {
console.log("Done.");
button.removeEventListener("click", once);
}
button.addEventListener("click", once);
Чтобы это провернуть, мы даём функции имя (в данном случае, once
), чтобы её можно было передать и в addEventListener
, и в removeEventListener
.
В примерах мы проигнорировали тот факт, что функциям-обработчикам передаётся аргумент – объект события. В нём хранится дополнительная информация о событии. К примеру, если надо узнать, какая кнопка мыши была нажата, мы можем обратиться к свойству which
этого объекта.
Жми меня, чем хочешь!
var button = document.querySelector("button");
button.addEventListener("mousedown", function(event) {
if (event.which == 1)
console.log("Левая");
else if (event.which == 2)
console.log("Средняя");
else if (event.which == 3)
console.log("Правая");
});
Хранящаяся в объекте информация – разная для каждого типа событий. Мы обсудим эти типы позже. Свойство объекта type
всегда содержит строку, описывающую событие (например, "click"
или "mousedown"
).
Распространение (propagation)
События, зарегистрированные на узлах, имеющих дочерние узлы, получат и некоторые события, случившиеся с их детьми. Если кликнуть на кнопку внутри параграфа, обработчики событий параграфа получат событие click
.
Если и у параграфа и у кнопки есть обработчики, то первым запустится более конкретный – то есть, обработчик кнопки. Событие как бы распространяется наружу, от узла, где оно случилось, до его родительского и далее до корня документа. После отработки всех обработчиков всех промежуточных узлов, очередь среагировать на событие доходит и до самого окна.
Читать дальше