JavaScript / 37 минут

События и формы

Обрабатывайте события, формы и делегирование без дублирования обработчиков.

В процессе Теория: впереди Практика: впереди Тест: впереди
18 / 22
После этой главы
  • Обрабатывайте события, формы и делегирование без дублирования обработчиков.
  • Применить тему в задаче «События и формы» и объяснить граничные случаи.

Около 7 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Событие связывает действие и реакцию

Интерфейс реагирует на клики, ввод текста, отправку формы и действия клавиатуры. Событие описывает произошедшее действие, а обработчик — функцию, которую среда вызывает при соответствующем условии. Регистрация обработчика не выполняет его немедленно. Между регистрацией и вызовом состояние программы может измениться, поэтому callback должен использовать актуальные данные по понятному контракту.

Предпочитайте семантические элементы. Для действия подходит button, для перехода — ссылка с адресом, для ввода — соответствующий input. Если заменить кнопку обычным div, придётся самостоятельно воспроизводить клавиатурное поведение, фокус и доступность. Обработчик click сам по себе не превращает любой элемент в полноценный элемент управления.

addEventListener и владение подпиской

addEventListener добавляет слушателя выбранного типа события. Функцию можно зарегистрировать отдельно и сохранить ссылку для последующего удаления. removeEventListener должен получить тот же объект функции и совместимые параметры подписки. Две одинаково написанные стрелки являются разными объектами и не отменяют регистрацию друг друга.

JavaScript
const button = document.querySelector('#save');
function handleSave(event) {
  console.log('Сохраняем', event.type);
}
button.addEventListener('click', handleSave);
// При завершении жизни интерфейса:
button.removeEventListener('click', handleSave);

Подписке нужен владелец. Если интерфейс создаётся повторно и каждый раз добавляет новый обработчик, одно действие может начать вызывать несколько сохранений. Опции once или signal помогают управлять жизненным циклом в подходящих сценариях. Не используйте once для действия, которое по требованиям должно работать многократно, только чтобы случайно скрыть дублирование регистрации.

Источник, текущий узел и распространение

event.target описывает исходный объект события, а currentTarget — объект, чей обработчик выполняется сейчас. При всплытии обработчик родителя может получить событие, возникшее на вложенной иконке кнопки. Поэтому сравнение только target с самой кнопкой часто ломается при изменении её внутренней разметки.

JavaScript
list.addEventListener('click', event => {
  if (!(event.target instanceof Element)) return;
  const button = event.target.closest('button[data-action]');
  if (!button || !list.contains(button)) return;
  console.log(button.dataset.action);
});

Это пример делегирования: один обработчик родителя обслуживает подходящие вложенные элементы. Проверка границы контейнера не позволяет случайно принять совпадение за его пределами. Некоторые события имеют особенности распространения, поэтому не стоит предполагать одинаковое всплытие для всех типов без проверки документации.

Стандартное действие и остановка распространения

preventDefault отменяет отменяемое стандартное действие события: например, переход по ссылке или обычную отправку формы. stopPropagation останавливает дальнейшее распространение. Это разные механизмы. Отмена действия браузера не обязана отключить обработчик родителя, а остановка всплытия не является способом запретить стандартную навигацию.

JavaScript
form.addEventListener('submit', event => {
  event.preventDefault();
  const data = new FormData(form);
  const name = data.get('name');
  console.log(name);
});

Не добавляйте preventDefault ко всем кликам без разбора: так можно сломать обычные ссылки, фокус и привычные клавиатурные сценарии. Для пассивного слушателя среда специально ограничивает возможность отмены. Выбирайте параметры обработчика в соответствии с его реальной ответственностью, а не копируйте опции из несвязанного примера.

Формы, валидация и повторная отправка

Событие submit возникает у формы и позволяет обслужить как нажатие кнопки, так и отправку с клавиатуры. Поэтому бизнес-действие обычно лучше связывать с формой, а не только с click на её кнопке. Значения формы остаются внешним вводом: required и input type помогают интерфейсу, но сервер обязан проверить данные самостоятельно.

Во время отправки полезно показывать состояние ожидания и предотвращать случайную двойную операцию. При ошибке нужно восстановить возможность повтора и сохранить полезный ввод. Отключённая кнопка не заменяет серверную идемпотентность: запрос может быть повторён другим клиентом или сетью. Интерфейсная защита улучшает удобство, серверная определяет корректность действия.

Практика с EventTarget

В учебной задаче используется EventTarget без документа. Он позволяет сосредоточиться на регистрации, отправке события и удалении подписки. Один listener реагирует только на имя click. События input и submit не должны увеличивать счётчик, даже если переданы рядом в массиве. Состояние создаётся заново для каждого solve.

Проверьте пустой набор, только посторонние события и несколько click подряд. Не возвращайте просто число совпадающих строк через filter: задача тренирует механизм событий, поэтому нужно действительно отправить Event в EventTarget. Затем перенесите тот же принцип в браузерную форму и добавьте очистку слушателя через AbortController при удалении интерфейсного компонента.

Загружаем редактор…

Музыка: Reverie — Scott Buckley · CC BY 4.0