JavaScript метод EventTarget.addEventListener()
Определение и применение
JavaScript метод addEventListener() объекта
EventTarget
позволяет зарегистрировать обработчик событий определенного типа для конкретной цели.
В качестве цели могут выступать как такие объекты как Element,
Document, Window, или любые другие объекты, которые поддерживают события, например, такой объект как
XMLHttpRequest, который широко используется в асинхронных запросах AJAX (от англ.
Asynchronous Javascript and XML — "асинхронный JavaScript и XML"), что позволяет конкретной
странице обновлять только её часть, не нарушая при этом работу пользователя.
Опция signal принимает AbortSignal: вызов abort() соответствующего
AbortController удаляет обработчик. Уже отменённый сигнал предотвращает регистрацию. Повторная
регистрация того же обработчика с одинаковыми type и capture не создаёт дубликат.
listener может быть функцией или объектом с методом handleEvent();
null игнорируется.
Посмотрите пример удаления обработчика через сигнал.
Поддержка браузерами
Спецификация: dom.spec.whatwg
Поддерживается во всех основных браузерах с 2015 года.
Отдельные значения (1) поддерживаются не везде — смотрите таблицу.
| Возможность | Компьютеры | Мобильные | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
addEventListener | 1 Версия 1 выпущена в 2008 г. Примечание MDN (англ.) Before Chrome 49, the | 12 | 1 | 7 | 1 | 18 Версия 18 выпущена в 2012 г. Примечание MDN (англ.) Before Chrome Android 49, the | 4 | 10.1 | 1 | 1.0 Версия 1.0 выпущена в 2013 г. Примечание MDN (англ.) Before Samsung Browser 5.0, the | 1 Версия 1 выпущена в 2008 г. Примечание MDN (англ.) Before Chrome 49, the | 1 |
| Form with options object supported (third parameter can be either options or a Boolean, for backwards compatibility) | 49 | 16 | 49 | 36 | 10 | 49 | 49 | 36 | 10 | 5.0 | 49 | 10 |
| options.capture parameter | 49 | 16 | 49 | 36 | 10 | 49 | 49 | 36 | 10 | 5.0 | 49 | 10 |
| options.once parameter | 55 | 16 | 50 | 42 | 10 | 55 | 50 | 42 | 10 | 6.0 | 55 | 10 |
| options.passive parameter | 51 | 16 | 49 | 38 | 10 | 51 | 49 | 41 | 10 | 5.0 | 51 | 10 |
| options.passive parameter defaults to true for touchstart and touchmove events | 55 | 79 | 61 | 42 | 11.1 | 55 | 61 | 42 | 11.3 | 6.0 | 55 | 11.3 |
| options.passive parameter defaults to true for wheel and mousewheel events | 73 | 79 | 84 | 60 | 73 | 84 | 52 | 11.0 | 73 | |||
| options.signal parameter | 90 | 90 | 86 | 76 | 15 | 90 | 86 | 64 | 15 | 15.0 | 90 | 15 |
| useCapture parameter is optional | 1 | 12 | 6 | 11.6 | 1 | 18 | 6 | 12 | 1 | 1.0 | 4.4 | 1 |
поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31
JavaScript синтаксис
target.addEventListener(type, listener);
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
Спецификация
Значения параметров
| Параметр | Описание |
|---|---|
type |
Строковое значение, представляющее тип события для прослушивания (значение чувствительно к регистру). Обязательный параметр. |
listener |
Объект, который получает уведомление при возникновении события указанного типа. Это должен быть объект,
реализующий интерфейс EventListener, или функция JavaScript.
Обязательный параметр.
|
options |
Объект, указывающий характеристики прослушивателя событий (является
необязательным параметром). Доступные опции:
|
useCapture |
Логический вариант третьего аргумента, эквивалентный { capture: useCapture }. Значение по
умолчанию — false. У предков true означает перехват, false — всплытие;
на цели оба варианта выполняются в фазе AT_TARGET.
|
На самой цели события оба вида обработчиков — с capture: true и capture: false — выполняются
при eventPhase === Event.AT_TARGET (2), сначала перехватывающие. Различие фаз перехвата и всплытия
относится к предкам цели.
Пример использования
Базовое использование метода
Обработчик на списке и на его пунктах
Нажмите и кликните по пункту списка: обработчик на ul сделает его зелёным. Двойной клик по пункту вернёт чёрный цвет через обработчик самого пункта.
index.html
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
main.js
const ul = document.querySelector('ul'),
listElements = document.querySelectorAll('li')
const myFunc = function (event) {
if (event.target.tagName === 'LI') {
event.target.style.color = 'green'
}
}
ul.addEventListener('click', myFunc)
listElements.forEach(function (elem) {
elem.addEventListener('dblclick', function () {
this.style.color = '#000'
})
})
Нажмите , чтобы запустить пример.
В этом примере мы разместили маркированный список (<ul>), внутри которого мы разместили пять элементов списка (<li>). С помощью метода
querySelector()
мы выбрали первый элемент
<ul>
в документе, а с помощью метода
querySelectorAll()
все элементы
<li>
в документе, и инициализировали переменные этими значениями.
С помощью метода addEventListener() мы установили для элемента
<ul>
обработчик события "click" (нажатие левой кнопкой мыши на элементе), который с помощью свойства
target
объекта
Event
проверяет имя тега элемента, и если это значение соответствует значению "LI", то изменяет стиль цвета
текста на зеленый при срабатывании.
Обратите внимание, что зачастую удобнее установить один обработчик на родительский элемент, а не для каждого
элемента по отдельности, особенно это актуально при динамическом изменении количества элементов, в этом случае
отсутствует необходимость обновлять обработчики для элементов.
Далее с помощью метода
forEach()
объекта
Array
мы проходим по каждому элементу коллекции, и с помощью метода addEventListener() устанавливаем каждому
элементу
<li>
обработчик события "dblclick" (двойное нажатие левой кнопки мыши), который изменяет стиль цвета текста
элемента на черный при срабатывании.
Обращаю Ваше внимание на то, что допускается устанавливать элементу несколько обработчиков как одного, так и разных типов событий, например:
Несколько обработчиков разных событий
Нажмите , затем нажмите и отпустите кнопку мыши на результате: в консоли появятся event 1–3 при нажатии и event 4–5 при отпускании.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
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
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
В этом примере с помощью метода addEventListener() для элемента
<body>
мы установили пять обработчиков событий - три обработчика событий "mousedown", которые
сработают при нажатии кнопки мыши на элементе, и еще два сработают в тот момент, когда кнопка будет отпущена
(курсор должен находиться при этом на элементе
<body>). Обратите внимание, что в качестве обработчиков событий были использованы анонимные стрелочные функции.
В следующем примере мы рассмотрим, что произойдет, если для одного типа событий мы будем использовать именованные функции, а не анонимные:
Повторная регистрация одной функции
Нажмите и нажмите кнопку мыши на результате: функция зарегистрирована трижды, но в консоли появится только одно сообщение my event.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
const myFunc = () => console.log('my event')
document.body.addEventListener('mousedown', myFunc)
document.body.addEventListener('mousedown', myFunc)
document.body.addEventListener('mousedown', myFunc)
// при нажатии кнопки мыши на элементе body произойдет вызов только одного обработчика
// my event
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
В этом примере с помощью метода addEventListener() для элемента
<body>
мы установили три обработчика событий "mousedown". Обратите внимание на важный момент, если в
одном EventTarget (в нашем случае элемент
<body>) с одинаковыми параметрами зарегистрировано несколько одинаковых прослушивателей (обработчиков) событий,
повторяющиеся экземпляры в этом случае будут отброшены. Они не вызывают повторный вызов обработчиков
событий, и их не нужно удалять вручную с помощью метода removeEventListener().
Использование параметра useCapture
Ранее некоторые браузеры требовали передачи методу addEventListener() третьего аргумента,
поэтому при регистрации обычного, неперехватывающего обработчика, который срабатывает в фазе всплытия (bubbling phase) в третьем аргументе следует передавать значение false для поддержки старых версий браузеров.
При capture: true обработчик вызывается во время перехвата у предков и на самой цели. При
capture: false (значение по умолчанию) — на цели и при всплытии у предков.
Давайте рассмотрим следующий пример:
Порядок перехвата и всплытия
Нажмите и нажмите кнопку мыши на результате: сначала выполнятся перехватывающие обработчики event 2 и event 4, затем event 1 и event 3. Поменяйте true и false и сравните порядок.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
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
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
В примере для body зарегистрированы четыре обработчика mousedown. При событии на самом
body сначала вызываются два перехватывающих, затем два обычных; у всех eventPhase равен 2.
Если событие начинается у потомка, обработчики body относятся соответственно к перехвату и всплытию.
На изображении ниже представлено как будет распространяться событие до элемента
<body>
с указанием всех фаз (стадий) - фаза перехвата, фаза цели и
фаза всплытия:
bodyИспользование параметра options
Ключ capture объекта options действует так же, как логический третий аргумент:
true выбирает перехватывающий обработчик, false — обычный. На самой цели оба работают в фазе
AT_TARGET.
Давайте рассмотрим следующий пример:
Настройка фазы через объект options
Нажмите и нажмите кнопку мыши на результате: обработчики с { capture: true } (event 2 и event 4) выполнятся раньше обработчиков с { capture: false }.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
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
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
В примере для body зарегистрированы четыре обработчика mousedown. При событии на самом
body сначала вызываются два перехватывающих, затем два обычных; у всех eventPhase равен 2.
Если событие начинается у потомка, обработчики body относятся соответственно к перехвату и всплытию.
Следующий ключ объекта options, который мы рассмотрим это ключ once, он позволяет указать
логическое значение, которое будет указывать на то, должен ли быть вызван обработчик события не более одного раза
после добавления, или нет. Если указано логическое значение true, то прослушиватель будет автоматически
удален после первого вызова. Давайте рассмотрим следующий пример:
Однократный обработчик с once
Нажмите и дважды нажмите кнопку мыши на результате: при первом нажатии сработают event 1 и event 2, при втором — только event 2, так как первый обработчик удалён после вызова.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
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
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
В этом примере с помощью метода addEventListener() для элемента
<body>
мы установили два обработчика событий "mousedown", которые сработают при нажатии кнопки мыши на
элементе. Обратите внимание, что при первом нажатии будут вызваны оба обработчика, а во
втором случае, только второй, это связано с тем, что для первого обработчика был установлен
ключ once объекта options в значение true, в результате чего обработчик события
был удален сразу же после первого вызова. Это удобно, если вам необходимо вызвать обработчик только единожды
и не беспокоиться о его удалении в дальнейшем.
Заключительный ключ объекта options, который мы рассмотрим это ключ passive, он позволяет
указать логическое значение, которое будет указывать на то, может ли функция обработчик отменить действие события по
умолчанию (вызвать метод
preventDefault()
объекта
Event).
Если указано логическое значение true, то функция, указанная прослушивателем, никогда не отменит действие
события по умолчанию (вызовет метод
preventDefault()). Если обработчик события всё же вызовет метод
preventDefault(), то браузер пользователя проигнорирует его, и создаст при этом предупреждение в консоли. Логическое значение
false свидетельствует о том, что функция обработчик может отменить действие события по умолчанию (может
вызвать метод
preventDefault()).
Если passive не задан, DOM Standard определяет true для touchstart,
touchmove, wheel и mousewheel на Window, Document,
корневом элементе документа и его body. Для остальных сочетаний значение по умолчанию —
false.
Если обработчик должен отменять действие, задайте { passive: false } явно. Отмена возможна только для
отменяемого события. При passive: true вызов preventDefault() не действует.
Вам не нужно беспокоиться о значении passive для базового события прокрутки (scroll), так
как его нельзя отменить, прослушиватели событий в любом случае не смогут блокировать показ страницы.
Нюансы использования this в обработчике события
Как правило необходимо передавать элемент, на котором сработал обработчик события, особенно это актуально при
использовании обобщённого обработчика событий для элементов одного типа. Если добавить обычную функцию обработчик
события к элементу с помощью метода addEventListener(), то значением this внутри такого
обработчика будет являться ссылка на элемент. Значение this будет совпадать со значением свойства
currentTarget
аргумента события объекта
Event, передаваемого обработчику события, например:
Значение this в обычном обработчике
Нажмите , затем кнопку в результате: обычная функция получает this, равный кнопке, поэтому в консоли появятся класс action и true.
index.html
<button id="example" class="action">Проверить this</button>
main.js
const elem = document.getElementById('example')
elem.addEventListener('click', function (e) {
console.log(this.className) // выведет в консоль значение свойства className элемента elem
console.log(e.currentTarget === this) // true
})
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
Значение this в стрелочном обработчике
Нажмите , затем кнопку в результате: у стрелочной функции нет собственного this, поэтому className не будет найден, а сравнение с currentTarget даст false.
index.html
<button id="example" class="action">Проверить this</button>
main.js
const elem = document.getElementById('example')
elem.addEventListener('click', (e) => {
console.log(this.className) // this не будет ссылаться на элемент elem
console.log(e.currentTarget === this) // false
})
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите , чтобы запустить пример.
Давайте разберем еще ситуацию, при которой обработчик события указан как
атрибут события
на HTML элементе. Код JavaScript в значении атрибута эффективно упаковывается в функцию обработчика, которая связывает
значение this способом, соответствующим методу addEventListener() (this
в коде представляет ссылку на элемент):
<input id="btn" type="button" onclick="console.log(this.id)"> <!-- выведет в консоль значение свойства id элемента -->
При нажатии на элемент
<input>
в консоль будет выведена информация о значении глобального атрибута
id
элемента, так как значение this внутри функции, вызываемой кодом в значении атрибута ведет себя согласно
стандартным правилам, и ссылается в данном случае на элемент, который инициировал событие. Давайте рассмотрим
следующий пример:
<script>
const myFunc = function() {
console.log(this.id) // this не будет ссылаться на элемент, который инициировал событие
};
</script>
<input id="btn" type="button" onclick="myFunc()"> <!-- добавляем атрибут событий onclick -->
Для того, чтобы передать необходимый контекст, вы можете использовать метод
call()
объекта
Function, который позволяет вызывать (выполнять) функцию как метод объекта, устанавливая ее контекст исполнения
(this) в указанное значение, передавая при этом необходимые аргументы:
<script>
const myFunc = function() {
console.log(this.id) // this будет ссылаться на элемент, который инициировал событие
};
</script>
<input id="btn" type="button" onclick="myFunc.call(this)"> <!-- добавляем атрибут событий onclick -->
В этом случае при срабатывании события в консоль будет выводиться значение глобального атрибута
id
элемента, который вызвал событие.
Удаление обработчика через AbortSignal
Нажмите , затем «Событие» несколько раз. Нажмите «Отменить подписку»: новые события больше не увеличивают счётчик. Повторный запуск создаёт новую подписку.
index.html
<button id="event" type="button">Событие</button>
<button id="abort" type="button">Отменить подписку</button>
<p>Обработано: <output>0</output></p>
main.js
const controller = new AbortController()
const output = document.querySelector('output')
let count = 0
document.querySelector('#event').addEventListener(
'click',
() => {
output.textContent = String(++count)
},
{ signal: controller.signal }
)
document.querySelector('#abort').addEventListener('click', () => controller.abort())
Нажмите , чтобы запустить пример.
Фаза на цели и отмена события
Нажмите кнопку и сравните фазы 1, 2, 2, 3. Затем измените bubbles или cancelable: отмена действия не останавливает распространение.
index.html
<div id="parent"><button id="target" type="button">Отправить событие</button></div>
<pre id="result" aria-live="polite"></pre>
main.js
const target = document.querySelector('#target')
const parent = document.querySelector('#parent')
const result = document.querySelector('#result')
function log(label, event) {
result.textContent += label + ': фаза ' + event.eventPhase + '\n'
}
parent.addEventListener('check', (event) => log('Предок, capture', event), true)
target.addEventListener('check', (event) => log('Цель, capture', event), true)
target.addEventListener('check', (event) => {
log('Цель, обычный обработчик', event)
event.preventDefault()
})
parent.addEventListener('check', (event) => log('Предок, всплытие', event))
target.addEventListener('click', () => {
result.textContent = 'До dispatchEvent\n'
const event = new Event('check', { bubbles: true, cancelable: true })
const accepted = target.dispatchEvent(event)
result.textContent += 'После dispatchEvent: ' + accepted + '\n'
result.textContent += 'Действие отменено: ' + event.defaultPrevented
})
Нажмите , чтобы запустить пример.