JavaScript метод EventTarget.addEventListener()
JavaScript EventTargetОпределение и применение
JavaScript метод addEventListener() объекта EventTarget позволяет зарегистрировать обработчик событий определенного типа для конкретной цели.
В качестве цели могут выступать как такие объекты как Element, Document, Window, или любые другие объекты, которые поддерживают события, например, такой объект как XMLHttpRequest, который широко используется в асинхронных запросах AJAX (от англ. Asynchronous Javascript and XML — "асинхронный JavaScript и XML"), что позволяет конкретной странице обновлять только её часть, не нарушая при этом работу пользователя.
Обращаю Ваше внимание на то, что Вы можете использовать метод removeEventListener() для удаления обработчика событий, присоединенного с помощью метода addEventListener().
Поддержка браузерами
Метод | Chrome | Firefox | Opera | Safari | IExplorer | Edge |
---|---|---|---|---|---|---|
Базовое использование | Да | Да | Да | Да | 9.0 | Да |
Параметр options | 49.0 | 49.0 | 42.0 | 10.0 | Нет | Да |
JavaScript синтаксис:
target.addEventListener(type, listener); target.addEventListener(type, listener, options); target.addEventListener(type, listener, useCapture); type - String listener - Function options - Object useCapture - Boolean
Cпецификация
Document Object Model (DOM) Level 2 EventsЗначения параметров
Параметр | Описание |
---|---|
type | Строковое значение, представляющее тип события для прослушивания (значение чувствительно к регистру). Обязательный параметр. |
listener | Объект, который получает уведомление при возникновении события указанного типа (объект, реализующий интерфейс Event). Это должен быть объект, реализующий интерфейс EventListener, или функция JavaScript. Обязательный параметр. |
options | Объект, указывающий характеристики прослушивателя событий (является необязательным параметром). Доступные опции:
|
useCapture | Логическое значение, которое определяет будут ли события этого типа отправлены зарегистрированному прослушивателю (обработчику события) перед отправкой любому EventTarget, расположенному под ним в дереве DOM. Если передать логическое значение true, то функция будет зарегистрирована как перехватывающий обработчик и будет вызываться в фазе перехвата (capture phase). Значение по умолчанию false - обработчик события будет срабатывать в фазе всплытия (bubbling phase). Необязательный параметр. Параметр useCapture не всегда был необязательным, в идеале, вы должны включить его для более широкой браузерной поддержки. |
Пример использования
Базовое использование метода
<!DOCTYPE html> <html> <head> <title>Использование JavaScript метода addEventListener()</title> </head> <body> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> const ul = document.querySelector("ul"), // выбираем первый элемент ul в документе listElements = document.querySelectorAll("li"); // выбираем все элементы li в документе const myFunc = function(event) { if(event.target.tagName === "LI") { // если объект, который отправил событие имеет значение свойства tagName равное LI event.target.style.color = "green"; // то задаем цвет текста зеленый } }; ul.addEventListener("click", myFunc); // устанавливаем элементу ul обработчик события click listElements.forEach(function(elem) { // проходим все элементы коллекции elem.addEventListener("dblclick", function(){ // устанавливаем каждому элементу li обработчик события dblclick this.style.color = "#000"; // задаем цвет текста черный }) }) </script> </body> </html>
В этом примере мы разместили маркированный список (<ul>), внутри которого мы разместили пять элементов списка (<li>). С помощью метода querySelector() мы выбрали первый элемент <ul> в документе, а с помощью метода querySelectorAll() все элементы <li> в документе, и инициализировали переменные этими значениями.
С помощью метода addEventListener() мы установили для элемента <ul> обработчик события "click" (нажатие левой кнопкой мыши на элементе), который с помощью свойства target объекта Event проверяет имя тега элемента, и если это значение соответствует значению "LI", то изменяет стиль цвета текста на зеленый при срабатывании. Обратите внимание, что зачастую удобнее установить один обработчик на родительский элемент, а не для каждого элемента по отдельности, особенно это актуально при динамическом изменении количества элементов, в этом случае отсутствует необходимость обновлять обработчики для элементов.
Далее с помощью метода forEach() объекта Array мы проходим по каждому элементу коллекции, и с помощью метода addEventListener() устанавливаем каждому элементу <li> обработчик события "dblclick" (двойное нажатие левой кнопки мыши), который изменяет стиль цвета текста элемента на черный при срабатывании.
Результат нашего примера:
Обращаю Ваше внимание на то, что допускается устанавливать элементу несколько обработчиков как одного, так и разных типов событий, например:
document.body.addEventListener("mousedown", () => console.log("event 1")); document.body.addEventListener("mousedown", () => console.log("event 2")); document.body.addEventListener("mousedown", () => console.log("event 3")); document.body.addEventListener("mouseup", () => console.log("event 4")); document.body.addEventListener("mouseup", () => console.log("event 5")); // при нажатии кнопки мыши на элементе body произойдет вызов трёх обработчиков // event 1 // event 2 // event 3 // при отпускании кнопки мыши на элементе body произойдет вызов еще двух обработчиков // event 4 // event 5
В этом примере с помощью метода addEventListener() для элемента <body> мы установили пять обработчиков событий - три обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на элементе, и еще два сработают в тот момент, когда кнопка будет отпущена (курсор должен находиться при этом на элементе <body>). Обратите внимание, что в качестве обработчиков событий были использованы анонимные стрелочные функции.
В следующем примере мы рассмотрим, что произойдет, если для одного типа событий мы будем использовать именованные функции, а не анонимные:
const myFunc = () => console.log("my event") ; document.body.addEventListener("mousedown", myFunc); document.body.addEventListener("mousedown", myFunc); document.body.addEventListener("mousedown", myFunc); // при нажатии кнопки мыши на элементе body произойдет вызов только одного обработчика // my event
В этом примере с помощью метода addEventListener() для элемента <body> мы установили три обработчика событий "mousedown". Обратите внимание на важный момент, если в одном EventTarget (в нашем случае элемент <body>) с одинаковыми параметрами зарегистрировано несколько одинаковых прослушивателей (обработчиков) событий, повторяющиеся экземпляры в этом случае будут отброшены. Они не вызывают повторный вызов обработчиков событий, и их не нужно удалять вручную с помощью метода removeEventListener().
Использование параметра useCapture
Ранее некоторые браузеры требовали передачи методу addEventListener() третьего аргумента, поэтому при регистрации обычного, неперехватывающего обработчика, который срабатывает в фазе всплытия (bubbling phase) в третьем аргументе следует передавать значение false для поддержки старых версий браузеров.
Если передать логическое значение true, то функция будет зарегистрирована как перехватывающий обработчик и будет вызываться в фазе перехвата (capture phase).
Давайте рассмотрим следующий пример:
document.body.addEventListener("mousedown", () => console.log("event 1"), false); document.body.addEventListener("mousedown", () => console.log("event 2"), true); document.body.addEventListener("mousedown", () => console.log("event 3"), false); document.body.addEventListener("mousedown", () => console.log("event 4"), true); // при нажатии кнопки мыши на элементе body произойдет вызов четырех обработчиков // event 2 // event 4 // event 1 // event 3
В этом примере с помощью метода addEventListener() для элемента <body> мы установили четыре обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на элементе. Обратите внимание, что первыми будут вызваны те обработчики, для которых значение параметра useCapture будет соответствовать значению true - функции будут зарегистрированы как перехватывающий обработчик, и будут вызываться в фазе перехвата (capture phase). После вызова этих функций вызываются уже два неперехватывающих обработчика события, которые срабатывают в фазе всплытия (bubbling phase).
На изображении ниже представлено как будет распостраняться событие до элемента <body> с указанием всех фаз (стадий) - фаза перехвата, фаза цели и фаза всплытия:
Использование параметра options
По аналогии с параметром useCapture, если передать объект с ключом capture, значение которого соответствует логическому значению true, то функция будет зарегистрирована как перехватывающий обработчик и будет вызываться в фазе перехвата (capture phase), логическое значение false определяет неперехватывающий обработчик, который срабатывает в фазе всплытия (bubbling phase) - это значение по умолчанию для всех функций обработчиков в современных браузерах.
Давайте рассмотрим следующий пример:
document.body.addEventListener("mousedown", () => console.log("event 1"), {capture: false}); document.body.addEventListener("mousedown", () => console.log("event 2"), {capture: true}); document.body.addEventListener("mousedown", () => console.log("event 3"), {capture: false}); document.body.addEventListener("mousedown", () => console.log("event 4"), {capture: true}); // при нажатии кнопки мыши на элементе body произойдет вызов четырех обработчиков // event 2 // event 4 // event 1 // event 3
В этом примере с помощью метода addEventListener() для элемента <body> мы установили четыре обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на элементе. Обратите внимание, что первыми будут вызваны те обработчики, для которых значение ключа capture параметра options будет соответствовать значению true - функции будут зарегистрированы как перехватывающий обработчик, и будут вызываться в фазе перехвата (capture phase). После вызова этих функций вызываются уже два неперехватывающих обработчика события, которые срабатывают в фазе всплытия (bubbling phase).
Следующий ключ объекта options, который мы рассмотрим это ключ once, он позволяет указать логическое значение, которое будет указывать на то, должен ли быть вызван обработчик события не более одного раза после добавления, или нет. Если указано логическое значение true, то прослушиватель будет автоматически удален после первого вызова. Давайте рассмотрим следующий пример:
document.body.addEventListener("mousedown", () => console.log("event 1"), {once: true}); document.body.addEventListener("mousedown", () => console.log("event 2"), {once: false}); // при нажатии кнопки мыши на элементе body произойдет вызов двух обработчиков // event 1 // event 2 // при повторном нажатии кнопки мыши на элементе body произойдет вызов уже только одного обработчика // event 2
В этом примере с помощью метода addEventListener() для элемента <body> мы установили два обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на элементе. Обратите внимание, что при первом нажатии будут вызваны оба обработчика, а во втором случае, только второй, это связано с тем, что для первого обработчика был установлен ключ once объекта options в значение true, в результате чего обработчик события был удален сразу же после первого вызова. Это удобно, если Вам необходимо вызвать обработчик только единожды и не беспокоиться о его удалении в дальнейшем.
Заключительный ключ объекта options, который мы рассмотрим это ключ passive, он позволяет указать логическое значение, которое будет указывать на то, может ли функция обработчик отменить действие события по умолчанию (вызвать метод preventDefault() объекта Event).
Если указано логическое значение true, то функция, указанная прослушивателем, никогда не отменит действие события по умолчанию (вызовет метод preventDefault()). Если обработчик события всё же вызовет метод preventDefault(), то браузер пользователя проигнорирует его, и создаст при этом предупреждение в консоли. Логическое значение false свидетельствует о том, что функция обработчик может отменить действие события по умолчанию (может вызвать метод preventDefault()).
Согласно спецификации, значение по умолчанию для параметра passive всегда равно false. Однако это дает возможность прослушивателям событий, обрабатывающим определенные события касания блокировать основной поток браузера при попытке обработки прокрутки, что может привести к значительному снижению производительности во время обработки прокрутки.
Чтобы предотвратить эту проблему, некоторые браузеры (в частности, Chrome и Firefox) изменили значение по умолчанию параметра passive на true для событий touchstart и touchmove в узлах уровня документа Window, Document и Document.body. Это предотвращает вызов прослушивателя событий, поэтому он не может блокировать отрисовку страницы во время прокрутки.
Вам не нужно беспокоиться о значении passive для базового события прокрутки (scroll), так как его нельзя отменить, прослушиватели событий в любом случае не смогут блокировать показ страницы.
Нюансы использования this в обработчике события
Как правило необходимо передавать элемент, на котором сработал обработчик события, особенно это актуально при использовании обобщённого обработчика событий для элементов одного типа. Если добавить обычную функцию обработчик события к элементу с помощью метода addEventListener(), то значением this внутри такого обработчика будет являться ссылка на элемент. Значение this будет совпадать со значением свойства currentTarget аргумента события объекта Event, передаваемого обработчику события, например:
elem.addEventListener("click", function(e) { console.log(this.className); // выведет в консоль значение свойства className элемента elem console.log(e.currentTarget === this); // true })
Обратите внимание на важный момент, значение this может меняться, например, стрелочные функции не имеют собственного контекста this и в этом случае this не будет ссылаться на элемент:
elem.addEventListener("click", (e) => { console.log(this.className); // this не будет ссылаться на элемент elem console.log(e.currentTarget === this); // false }
Давайте разберем еще ситуацию, при которой обработчик события указан как атрибут события на HTML элементе. Код JavaScript в значении атрибута эффективно упаковывается в функцию обработчика, которая связывает значение this способом, соответствующим методу addEventListener() (this в коде представляет ссылку на элемент):
<input id = "btn" type = "button" onclick = "console.log(this.id)"> // выведет в консоль значение свойства id элемента
При нажатии на элемент <button> в консоль будет выведена информация о значении глобального атрибута id элемента, так как значение this внутри функции, вызываемой кодом в значении атрибута ведет себя согласно стандартным правилам, и ссылается в данном случае на элемент, который инициировал событие. Давайте рассмотрим следующий пример:
<script> const myFunc = function() { console.log(this.id) // this не будет ссылаться на элемент, который инициировал событие }; </script> <input id = "btn" type = "button" onclick = "myFunc()"> <-- добавляем атрибут событий onclick -->
Обратите внимание на важный момент, значение this внутри функции myFunc() будет являться ссылкой на глобальный объект Window, или будет соответствовать значению undefined в том случае, если указан строгий режим (strict mode).
Для того, чтобы передать необходимый контекст, Вы можете использовать метод call() объекта Function, который позволяет вызывать (выполнять) функцию как метод объекта, устанавливая ее контекст исполнения (this) в указанное значение, передавая при этом необходимые аргументы:
<script> const myFunc = function() { console.log(this.id) // this будет ссылаться на элемент, который инициировал событие }; </script> <input id = "btn" type = "button" onclick = "myFunc.call(this)"> <-- добавляем атрибут событий onclick -->
В этом случае при срабатывании события в консоль будет выводиться значение глобального атрибута id элемента, который вызвал событие.
JavaScript EventTarget