</> Praktum

Главная / JavaScript / addEventListener

Справочник · JavaScript

addEventListener(): реагируем на события

Редакция PraktumОбновлено 6 мин чтенияУровень: новичок

Метод 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);
Аргументы addEventListener
АргументЗначение
typeимя события без «on»: 'click', 'input', 'submit'
listenerфункция, которая получит объект события
options.oncetrue — обработчик сработает один раз и удалится сам
options.passivetrue — обещание не вызывать preventDefault; ускоряет прокрутку для touch и wheel
options.capturetrue — срабатывать на фазе погружения, а не всплытия
options.signalсигнал AbortController: при abort() обработчик удаляется

Частые события

События, которые нужны чаще всего
СобытиеКогда происходит
clickклик мышью, касание, Enter или пробел на кнопке
inputкаждое изменение значения поля
changeзначение изменено и подтверждено: ушли из поля, выбрали пункт списка
submitотправка формы, вешают на form, а не на кнопку
keydownнажатие клавиши; код в event.key
DOMContentLoadedHTML разобран, на 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

addEventListener

Несколько обработчиков на одно событие, параметры once, passive, signal, код отделён от HTML.

onclick

Свойство или атрибут: только один обработчик, новое присваивание затирает старое. Атрибут в 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() в начале.

Источники

Практикум по теме

Ещё в разделе JavaScript