Справочник · JavaScript
addEventListener(): реагируем на события
Метод addEventListener() подписывает элемент на событие — клик, ввод текста, отправку формы, нажатие клавиши — и вызывает переданную функцию каждый раз, когда событие происходит. Первый аргумент — имя события строкой, второй — функция-обработчик, третий — необязательные параметры.
el.addEventListener('click', handler)— передавай функцию, а не её вызов.- В обработчик приходит объект события:
event.target,event.preventDefault(). - Одному элементу можно назначить сколько угодно обработчиков.
- Для списков удобнее делегирование: один обработчик на родителе и
closest. - Удалить можно только ту же функцию — через
removeEventListenerилиsignal.
Минимальный пример
const button = document.querySelector('.theme-toggle');
button.addEventListener('click', () => {
document.body.classList.toggle('dark');
});При каждом клике у body переключается класс dark. Если элемента с классом theme-toggle на странице нет, querySelector вернёт null, и строка с addEventListener упадёт с ошибкой — проверяй результат поиска.
Синтаксис и параметры
target.addEventListener(type, listener, options);| Аргумент | Значение |
|---|---|
type | имя события без «on»: 'click', 'input', 'submit' |
listener | функция, которая получит объект события |
options.once | true — обработчик сработает один раз и удалится сам |
options.passive | true — обещание не вызывать preventDefault; ускоряет прокрутку для touch и wheel |
options.capture | true — срабатывать на фазе погружения, а не всплытия |
options.signal | сигнал AbortController: при abort() обработчик удаляется |
Частые события
| Событие | Когда происходит |
|---|---|
click | клик мышью, касание, Enter или пробел на кнопке |
input | каждое изменение значения поля |
change | значение изменено и подтверждено: ушли из поля, выбрали пункт списка |
submit | отправка формы, вешают на form, а не на кнопку |
keydown | нажатие клавиши; код в event.key |
DOMContentLoaded | HTML разобран, на document |
load | загружено всё, включая картинки, на window |
scroll | прокрутка, на window или прокручиваемом блоке |
Объект события
form.addEventListener('submit', (event) => {
event.preventDefault(); // не перезагружать страницу
const data = new FormData(event.currentTarget);
console.log(data.get('email'));
});Обработчик получает объект события. Главное в нём: event.target — элемент, где событие возникло; event.currentTarget — элемент, на котором висит обработчик; event.preventDefault() — отменить действие браузера (отправку формы, переход по ссылке); event.key — клавиша для клавиатурных событий.
Всплытие: target и currentTarget
Событие сначала происходит на самом глубоком элементе, а потом поднимается по предкам до document — это всплытие. Клик по иконке внутри кнопки сработает и на иконке, и на кнопке, и на списке. Поэтому event.target может оказаться иконкой svg, а event.currentTarget — кнопкой, к которой привязан обработчик.
event.stopPropagation() останавливает всплытие, но применяй его редко: другие обработчики выше (аналитика, закрытие меню по клику снаружи) перестанут узнавать о событии.
Делегирование событий
const list = document.querySelector('.tasks');
list.addEventListener('click', (event) => {
const button = event.target.closest('[data-delete]');
if (!button) return; // клик не по кнопке удаления
button.closest('li').remove();
});Вместо обработчика на каждой кнопке — один на родителе. Это работает и для элементов, добавленных позже, и экономит память на длинных списках. closest поднимается от места клика к нужной кнопке, даже если кликнули по иконке внутри неё.
Как удалить обработчик
function onKey(event) {
if (event.key === 'Escape') closeModal();
}
document.addEventListener('keydown', onKey);
document.removeEventListener('keydown', onKey);
// группа обработчиков снимается одним вызовом
const controller = new AbortController();
window.addEventListener('resize', onResize, { signal: controller.signal });
window.addEventListener('scroll', onScroll, { signal: controller.signal, passive: true });
controller.abort();removeEventListener снимает обработчик, только если передать ту же самую функцию и то же значение capture. Анонимную стрелку удалить нельзя: () => {} каждый раз создаёт новую функцию. Параметр signal удобнее, когда подписок несколько.
this внутри обработчика
В обычной функции-обработчике this равен элементу, на котором висит обработчик (то же, что event.currentTarget). В стрелочной функции своего this нет — он берётся снаружи. Чтобы не путаться, используй event.currentTarget: он работает одинаково в любой функции.
addEventListener или onclick
Несколько обработчиков на одно событие, параметры once, passive, signal, код отделён от HTML.
Свойство или атрибут: только один обработчик, новое присваивание затирает старое. Атрибут в HTML смешивает разметку и логику.
Частые ошибки
- Передают вызов вместо функции:
addEventListener('click', handleClick())выполнит её сразу. - Пишут имя события с «on»:
'onclick'вместо'click'. - Не проверяют, найден ли элемент, и получают
Cannot read properties of null. - Вешают
submitна кнопку вместо формы и забываютpreventDefault. - Добавляют обработчик при каждом рендере списка, и клик срабатывает несколько раз.
Доступность
Вешай click на настоящую button или ссылку: они получают фокус и срабатывают по Enter и пробелу. Клик на div не работает с клавиатуры. Если для действия нужна клавиатура (закрыть окно по Escape), слушай keydown на document. Не отменяй через preventDefault стандартное поведение без замены — например, переход по ссылке.
Поддержка браузерами
addEventListener работает во всех браузерах. Объект параметров с once, passive и capture поддерживается всеми актуальными браузерами, параметр signal — тоже, начиная с версий 2021 года.
Практика
Сделай список задач с кнопкой «Удалить» у каждого пункта. Повесь один обработчик на список, который удаляет нужный пункт, даже если клик пришёлся на иконку внутри кнопки. Добавь кнопку «Подсказка», которая показывает текст только при первом нажатии.
Показать решение
document.querySelector('.tasks').addEventListener('click', (event) => {
const button = event.target.closest('[data-delete]');
if (button) button.closest('li').remove();
});
document.querySelector('.hint').addEventListener('click', () => {
document.querySelector('.hint-text').hidden = false;
}, { once: true });Частые вопросы
Почему обработчик срабатывает сразу при загрузке?
Ты передал вызов функции, а не саму функцию: handleClick() вместо handleClick. Скобки выполняют функцию сразу, а в addEventListener попадает её результат.
Чем target отличается от currentTarget?
target — элемент, где событие возникло, например иконка внутри кнопки. currentTarget — элемент, на котором висит обработчик.
Почему removeEventListener не удаляет обработчик?
Нужно передать ту же самую функцию, что и в addEventListener. Анонимную стрелку удалить нельзя — вынеси функцию в переменную или используй параметр signal.
Что такое passive: true?
Обещание браузеру, что обработчик не вызовет preventDefault(). Тогда браузер не ждёт обработчик и прокручивает страницу плавно. Используй для scroll, touchmove и wheel.
Как отменить отправку формы?
Повесь обработчик submit на форму и вызови event.preventDefault() в начале.
Источники
Практикум по теме
- Скопировать текст кнопкой — Адрес урока или номер заказа удобно копировать одной кнопкой.