События и обработка событий в браузере
События
Что такое событие?
Событие — это сигнал о том, что что-то произошло. Например — пользователь кликнул по кнопке, загрузилась страница, или вы сами отправили событие программно, чтобы уведомить другие части приложения.
В браузере событие связано с DOM и передаётся через объект Event (для клика — MouseEvent) — тип, цель, фаза распространения, координаты и другие поля. Обработчик клика попадает в очередь макрозадач Event Loop — тяжёлую работу внутри слушателя лучше выносить в async или откладывать.
Событие не ограничивается одним элементом: при клике по кнопке внутри блока браузер проходит цепочку предков и потомков. Порядок вызова обработчиков задаёт распространение событий (capture → target → bubble).
События форм — валидация
Выделение текста — Selection и Range
Окно и URL — BOM
Перетаскивание файлов — чтение файлов
Клик и системное "Поделиться" — Web Share API
Обзор API браузера — Web API в браузере
Кодовые примеры — Web API на практике.
Встроенные события
on… в HTML и addEventListenerВ разметке можно писать onclick="…", onchange="…" — это те же типы событий (click, change), но обработчик задан в HTML. В скрипте предпочтительнее element.addEventListener('click', handler) — несколько обработчиков на один элемент, снятие через removeEventListener, выбор фазы (capture / bubble).
На document вешают слушатель, когда нужно делегирование — один обработчик на список кнопок вместо подписки на каждую кнопку.
Существует множество встроенных событий, которые можно использовать:
- События мыши:
- click — клик мышью
- mouseover / mouseout — наведение/уход курсора
- mousedown / mouseup — нажатие/отпускание кнопки мыши
- Клавиатурные события:
- keydown — нажата клавиша
- keyup — клавиша отпущена
- Формы:
- submit — отправка формы
- focus / blur — фокус на элементе / потеря фокуса
- change — изменение значения элемента формы
- UI-события:
- resize — изменение размера окна
- scroll — прокрутка страницы
Вот более полный список основных событий:
| Имя события | Значение события | Применяется к элементам |
|---|---|---|
click | Щелчок мышью или нажатие клавиши Enter при фокусе | Кнопки, ссылки, изображения, любые интерактивные элементы |
dblclick | Двойной щелчок мышью | Кнопки, ссылки, текстовые области, изображения |
mousedown | Нажатие любой кнопки мыши | Любые DOM-элементы |
mouseup | Отпускание кнопки мыши | Любые DOM-элементы |
mouseover | Перемещение курсора над элементом | Любые DOM-элементы |
mouseout | Перемещение курсора за пределы элемента | Любые DOM-элементы |
mousemove | Перемещение курсора внутри элемента | Любые DOM-элементы |
mouseenter | Перемещение курсора внутрь элемента (не всплывает) | Любые DOM-элементы |
mouseleave | Перемещение курсора из элемента (не всплывает) | Любые DOM-элементы |
keydown | Нажатие клавиши на клавиатуре | Элементы с фокусом, окно документа |
keyup | Отпускание клавиши на клавиатуре | Элементы с фокусом, окно документа |
keypress | Нажатие клавиши для ввода символа (устаревшее) | Элементы с фокусом, окна ввода текста |
focus | Получение фокуса элементом | Поля ввода, кнопки, ссылки |
blur | Потеря фокуса элементом | Поля ввода, кнопки, ссылки |
focusin | Фокус переходит внутрь элемента (всплывает) | Поля ввода, кнопки, ссылки |
focusout | Фокус покидает элемент (всплывает) | Поля ввода, кнопки, ссылки |
change | Изменение значения элемента формы | Поля ввода, выпадающие списки, чекбоксы |
input | Мгновенное изменение значения поля ввода | Текстовые поля, поля поиска, contenteditable |
submit | Отправка формы | Тег <form> |
reset | Сброс формы | Тег <form> |
resize | Изменение размера окна браузера | Окно браузера (window) |
scroll | Прокрутка содержимого элемента или окна | Окно браузера, прокручиваемые блоки (div, body) |
load | Загрузка ресурса (страницы, изображения) | Окно, изображения, скрипты, фреймы |
unload | Закрытие страницы или переход на другую | Окно браузера |
error | Ошибка загрузки ресурса | Окно, изображения, скрипты, фреймы |
abort | Прерывание загрузки (например, отмена) | Элементы, поддерживающие AJAX или загрузку файлов |
contextmenu | Открытие контекстного меню (правая кнопка мыши) | Любые DOM-элементы |
wheel | Прокрутка колесиком мыши | Любые DOM-элементы |
copy | Копирование выделенного текста | Любой активный элемент |
cut | Вырезание выделенного текста | Любой активный элемент |
paste | Вставка скопированного текста | Любой активный элемент |
Пользовательские события
JavaScript позволяет создавать пользовательские события с помощью конструктора CustomEvent:
const myEvent = new CustomEvent('myCustomEvent', {
detail: {
message: 'Привет! Это событие!',
userId: 1001
}
});
Разбор:
new CustomEvent('myCustomEvent', ...)создаёт пользовательское событие с именемmyCustomEvent, которое можно обрабатывать так же, как встроенныеclickилиinput.- Поле
detailработает как контейнер полезной нагрузки — сюда кладут данные, которые обработчик получит вevent.detail. messageиuserIdв примере показывают типичный сценарий: передать сразу и текст, и технический идентификатор.- На этом этапе событие только создано в памяти; до
dispatchEventникакие обработчики ещё не запускаются. - Такой подход удобно использовать для связи между независимыми частями UI, когда прямой вызов функции нежелателен.
- Ключевая идея — отделить факт события (что произошло) от реакции (что делать), чтобы код оставался слабо связанным.
Работа с событиями
Чтобы работать с событиями, нужно понять их жизненный цикл:
- Создание события — создаётся объект события.
- Инициация события — событие отправляется (dispatch).
- Прослушивание события — другой код ожидает его и реагирует.
Отправка события:
window.dispatchEvent(myEvent);
Разбор:
dispatchEventзапускает жизненный цикл события и передаёт его по стандартным фазам распространения.- Вызов на
windowделает событие глобальным для текущего окна, поэтому его могут слушать разные модули. - В аргумент передаётся готовый объект
myEvent, созданный заранее черезCustomEvent. - Срабатывают только те обработчики, которые уже подписаны на тип
myCustomEventк моменту вызова. - Этот вызов не "ждёт" отдельного подтверждения — обработчики выполняются синхронно в рамках текущего тика.
window используется здесь как глобальный объект, но события можно отправлять и на конкретные DOM-элементы.
Чтобы реагировать на событие, нужно добавить обработчик события с помощью метода addEventListener().
window.addEventListener('myCustomEvent', (event) => {
console.log("Получено событие:", event.type);
const { message, userId } = event.detail;
console.log(`Сообщение: ${message}, Пользователь ID: ${userId}`);
});
Разбор:
addEventListener('myCustomEvent', ...)регистрирует реакцию на конкретный тип события; строка типа должна совпадать с именем вCustomEvent.- Параметр
event— объект события, гдеevent.typeпоказывает имя события, аevent.detailсодержит пользовательские данные. - Деструктуризация
const { message, userId } = event.detail;делает доступ к полям короче и читабельнее. console.logздесь используется как отладочный маркер, чтобы проверить факт срабатывания и структуру данных.- Такой шаблон удобен для подписки на события приложения — обработчик ничего не знает о том, кто отправил событие, и реагирует только на контракт данных.
Важно: убедитесь, что обработчик добавлен до отправки события. Иначе он может его "не поймать".
Вы можете привязывать обработчики не только к window, но и к любым DOM-элементам:
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Кнопка нажата!');
});
Разбор:
document.getElementById('myButton')получает конкретный DOM-узел поid; без этого нельзя подписать обработчик на нужный элемент.button.addEventListener('click', ...)привязывает реакцию только к этой кнопке, а не ко всему документу.- Событие
clickгенерируется браузером при нажатии мышью или активации клавиатурой на фокусированном элементе. - Колбэк в примере минимальный — только лог, но в реальном коде здесь обычно запускают обновление интерфейса или отправку данных.
- Этот шаблон даёт локальную, предсказуемую обработку пользовательского действия.
Также можно отправлять кастомные события именно на элементы:
const customClick = new CustomEvent('customClick', {
detail: { info: 'Это наш собственный клик' }
});
button.dispatchEvent(customClick);
Разбор:
- Создаётся отдельное событие
customClick, семантически связанное с кнопкой, но не ограниченное нативнымclick. detail.infoзадаёт контекст события: обработчик понимает, почему событие было отправлено и с какими данными.button.dispatchEvent(customClick)запускает событие именно на элементеbutton, поэтому его можно обрабатывать локально или на родителях через всплытие.- Такой приём полезен для унификации интерфейса, когда часть реакций должна работать одинаково и для реального клика, и для программного триггера.
- Важно, что
dispatchEventне создаёт событие автоматически — он только отправляет уже собранный объект.
Методы:
- addEventListener(type, handler) — добавляет обработчик
- removeEventListener(type, handler) — удаляет обработчик
- dispatchEvent(event) — отправляет событие
Свойства объекта event:
- type — тип события
- target — элемент, на котором произошло событие
- currentTarget — элемент, на котором сработал обработчик
- detail — данные, переданные в кастомном событии
Подробнее про фазы и делегирование — в разделе ниже.

Алгоритмические шаблоны
Регистрация обработчика события
<элемент>.addEventListener("<тип_события>", <обработчик>);
Удаление обработчика события
<элемент>.removeEventListener("<тип_события>", <обработчик>);
Создание пользовательского события
const <имя_события> = new CustomEvent("<тип_события>", {
detail: <данные>
});
Отправка события
<элемент>.dispatchEvent(<имя_события>);
Обработчик события с доступом к данным
<элемент>.addEventListener("<тип_события>", (event) => {
const данные = event.detail;
// работа с данными
});
Доступ к свойствам события внутри обработчика
event.type // строка с типом события
event.target // элемент, на котором произошло событие
event.currentTarget // элемент, на котором установлен обработчик
event.detail // данные, переданные через CustomEvent
Привязка обработчика к конкретному DOM-элементу
const элемент = document.querySelector("<селектор>");
элемент.addEventListener("<тип_события>", <обработчик>);
Использование анонимной функции как обработчика
<элемент>.addEventListener("<тип_события>", (event) => {
// реакция на событие
});
Использование именованной функции как обработчика
function <имя_функции>(event) {
// реакция на событие
}
<элемент>.addEventListener("<тип_события>", <имя_функции>);
Отмена действия по умолчанию
<элемент>.addEventListener("<тип_события>", (event) => {
event.preventDefault();
});
Распространение событий — всплытие, перехват и делегирование
Распространение (event propagation) — прохождение события по DOM-дереву. Если на пути висят несколько обработчиков одного типа, браузер вызывает их в строгом порядке. Это основа делегирования и контроля вложенных интерфейсов (меню, модалки, списки).
Три фазы
| Фаза | Направление | Когда срабатывают обработчики |
|---|---|---|
| Capture (перехват) | сверху вниз: window → … → родитель цели | только если слушатель зарегистрирован с { capture: true } |
| Target (цель) | на элементе, где произошло действие | все слушатели на этом узле |
| Bubble (всплытие) | снизу вверх: цель → … → window | слушатели без capture: true (режим по умолчанию) |
По умолчанию addEventListener подписывает обработчик на всплытие. Для перехвата на пути вниз:
element.addEventListener('click', handler, { capture: true });
// эквивалент устаревшей записи: addEventListener('click', handler, true)
| Свойство | Смысл |
|---|---|
event.target | узел, на котором возникло событие (часто самый глубокий элемент под курсором) |
event.currentTarget | узел, на котором выполняется текущий обработчик |
event.eventPhase | 1 — capture, 2 — target, 3 — bubble |
Всплытие — порядок по умолчанию
Разметка:
<div id="parent">
<button id="child" type="button">Нажми</button>
</div>
Оба слушателя на фазе bubble:
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.addEventListener('click', () => console.log('parent'));
child.addEventListener('click', () => console.log('child'));
Клик по кнопке в консоли:
childparent
Сначала цель, затем предки — так устроено всплытие.
Перехват (capturing)
Тот же HTML; родитель слушает capture, кнопка — bubble:
parent.addEventListener('click', () => console.log('parent (capture)'), { capture: true });
child.addEventListener('click', () => console.log('child'));
Клик по кнопке:
parent (capture)child
Перехват удобен, когда нужно обработать событие до дочерних узлов — глобальные горячие клавиши, закрытие выпадающего меню по клику "снаружи", логирование на уровне контейнера.
Остановка распространения
event.stopPropagation() обрывает дальнейший проход по дереву (и вверх, и вниз — в зависимости от текущей фазы). Обработчики на других узлах той же фазы не вызовутся.
child.addEventListener('click', (event) => {
event.stopPropagation();
console.log('только child');
});
parent.addEventListener('click', () => console.log('parent не сработает'));
Типичные случаи:
- клик по кнопке внутри карточки — карточка не должна открываться целиком;
- клик по содержимому модального окна — фон (оверлей) не получает
click; - вложенный интерактивный виджет внутри большого
click-обработчика.
event.stopImmediatePropagation() дополнительно отменяет остальные обработчики на том же элементе для этого типа события. Используйте редко — мешает композиции нескольких библиотек на одном узле.
Для списков и таблиц обычно хватает делегирования на родителе. stopPropagation уместен точечно — там, где родитель и ребёнок реагируют на одно действие по-разному.
События без всплытия
Делегирование через родителя работает только если событие всплывает (bubbles: true).
| Событие | Всплывает? | Альтернатива для делегирования |
|---|---|---|
click, input, change, keydown | да | родитель + target / closest |
focus, blur | нет | focusin, focusout (всплывают) |
mouseenter, mouseleave | нет | mouseover, mouseout или обработчик на общем контейнере |
load на <img> | нет | слушатель на элементе или event.target в фазе target |
Опции addEventListener
Третий аргумент — объект (рекомендуемый стиль):
| Опция | Эффект |
|---|---|
capture: true | слушать фазу перехвата |
once: true | снять обработчик после первого срабатывания |
passive: true | обработчик не вызовет preventDefault() — браузер может сразу прокручивать (см. scroll в BOM) |
button.addEventListener('click', handleOnce, { once: true });
Делегирование событий
Делегирование — один обработчик на родителе вместо сотен обработчиков на динамических дочерних узлах. Событие всплывает; в обработчике проверяют, подходит ли event.target:
const list = document.getElementById('todo-list');
list.addEventListener('click', (event) => {
const item = event.target.closest('[data-id]');
if (!item || !list.contains(item)) return;
if (event.target.matches('[data-action="delete"]')) {
item.remove();
}
});
Когда делегирование уместно:
- список, таблица, меню — элементы добавляются и удаляются без перепривязки слушателей;
- много однотипных кнопок (чекбоксы, "удалить", звёзды рейтинга).
Когда лучше вешать обработчик на сам элемент:
- редкие уникальные виджеты с тяжёлой логикой;
- события без всплытия (
focus,blur,mouseenter,mouseleave) — делегирование через родителя не сработает, нужен другой контейнер илиfocusin/focusout.
Element.closest(selector) поднимается от target к родителям и находит подходящий узел — удобно, если клик пришёлся на вложенный <span> внутри кнопки.
Полный сценарий — динамический список задач:
<ul id="todo-list">
<li data-id="1">
Задача 1
<button type="button" data-action="delete">×</button>
</li>
</ul>
<button type="button" id="add">Добавить</button>
Код ITЗагрузка примера кода…
Новые <li> появляются без addEventListener на каждой строке — один слушатель на #todo-list.
В применении JavaScript в вебе делегирование сочетают с кэшированием ссылок на DOM: не вызывайте querySelector на весь документ в каждом клике, держите один слушатель на стабильном контейнере.
Методы объектов событий
Методы используются для управления поведением события, его распространением и выполнением действий по умолчанию.
Основные методы объекта Event
| Метод | Описание |
|---|---|
event.preventDefault() | Отменяет стандартное действие браузера для события. Например, предотвращает переход по ссылке при клике или отправку формы. |
event.stopPropagation() | Прерывает дальнейшее распространение по DOM (см. остановку распространения). |
event.stopImmediatePropagation() | То же плюс отмена остальных обработчиков на текущем элементе для этого типа. |
event.initEvent(type, bubbles, cancelable) | Инициализирует событие программно (устаревший метод, используется редко). |
event.isTrusted | Возвращает true, если событие вызвано пользователем (клик, ввод), и false, если создано скриптом. |
Методы работы с обработчиками (на объектах DOM)
Эти методы вызываются непосредственно на элементах DOM или глобальных объектах.
| Метод | Описание |
|---|---|
element.addEventListener(type, handler, options) | Добавляет новый обработчик события. Принимает тип события, функцию-обработчик и опциональные параметры (флаг capture, once). |
element.removeEventListener(type, handler, options) | Удаляет ранее добавленный обработчик события. Требуется указать ту же функцию-ссылку, что и при добавлении. |
element.dispatchEvent(event) | Создает и запускает событие на конкретном элементе. Используется для ручного инициирования событий (в том числе пользовательских). |
element.click() | Синтетический метод, вызывающий событие click как если бы пользователь нажал кнопку. |
element.focus() | Устанавливает фокус на элемент, вызывая события focus и focusin. |
element.blur() | Снимает фокус с элемента, вызывая события blur и focusout. |
Свойства объекта события
Свойства предоставляют информацию о событии, контексте его возникновения и данных, переданных вместе с ним.
Базовые свойства
| Свойство | Тип данных | Описание |
|---|---|---|
type | Строка | Название типа события (например, "click", "keydown"). |
target | Объект DOM | Исходный элемент, на котором произошло событие. |
currentTarget | Объект DOM | Элемент, на котором в данный момент установлен обработчик события. |
eventPhase | Число | Фаза: 1 — capture, 2 — target (на целевом элементе), 3 — bubble. |
bubbles | Булево | Указывает, всплывает ли событие вверх по DOM-дереву. |
cancelable | Булево | Указывает, можно ли отменить действие события с помощью preventDefault(). |
defaultPrevented | Булево | true, если было вызвано preventDefault(). |
isTrusted | Булево | true, если событие сгенерировано браузером напрямую пользователем; false — если создано скриптом. |
timeStamp | Число | Время возникновения события в миллисекундах с момента загрузки страницы. |
Специфические свойства для типов событий
Свойства событий мыши
| Свойство | Описание |
|---|---|
clientX, clientY | Координаты курсора относительно видимой области окна браузера. |
screenX, screenY | Координаты курсора относительно всего экрана монитора. |
pageX, pageY | Координаты курсора относительно всей страницы (учитывает прокрутку). |
button | Номер нажатой кнопки мыши (0 — левая, 1 — средняя, 2 — правая). |
buttons | Битовая маска состояния всех нажатых кнопок мыши во время движения. |
movementX, movementY | Смещение курсора относительно предыдущего события движения. |
Свойства событий клавиатуры
| Свойство | Описание |
|---|---|
key | Значение нажатой клавиши (строка, например, "a", "Enter", "Shift"). |
code | Физическое расположение клавиши на клавиатуре (например, "KeyA", "Space"). |
ctrlKey, shiftKey, altKey, metaKey | Статус соответствующих модификаторов (нажата ли клавиша Ctrl, Shift и т.д.). |
repeat | true, если событие генерируется при удержании клавиши. |
location | Расположение клавиши на клавиатуре (0 — основная зона, 1 — Numpad, 2 — Meta/AltGr). |
Свойства событий формы
| Свойство | Описание |
|---|---|
inputType | Тип ввода (для событий input), описывающий источник изменения (например, "insertText", "deleteContentBackward"). |
dataTransfer | Объект для передачи данных при перетаскивании (drag and drop). |
Свойства пользовательских событий (CustomEvent)
| Свойство | Описание |
|---|---|
detail | Произвольный объект или данные, переданные при создании события через конструктор new CustomEvent(). |
Drag-and-drop (перетаскивание)
API Drag and Drop переносит данные между элементами или из ОС в страницу (файлы). Цепочка событий на источнике и приёмнике:
| Событие | Где | Назначение |
|---|---|---|
dragstart | источник | начало перетаскивания; заполнить dataTransfer |
dragover | приёмник | элемент под курсором — обязательно preventDefault(), иначе drop не сработает |
dragleave | приёмник | курсор ушёл с зоны |
drop | приёмник | отпускание; читать dataTransfer |
dragend | источник | завершение (успех или отмена) |
Источник должен быть draggable="true" (для файлов с диска — зона drop без draggable).
<ul id="list">
<li draggable="true" data-id="1">Задача 1</li>
<li draggable="true" data-id="2">Задача 2</li>
</ul>
<div id="done-zone">Выполнено</div>
Код ITЗагрузка примера кода…
dataTransfer хранит строки по MIME-ключу (text/plain, text/html). Для файлов с рабочего стола:
dropZone.addEventListener('drop', (event) => {
event.preventDefault();
const file = event.dataTransfer.files[0];
if (file) handleFile(file); // см. чтение файлов
});
Связь: чтение файлов в браузере, FormData.
На тач-устройствах Drag and Drop работает не везде одинаково — для мобильных часто делают отдельный UI (кнопки, long-press с полифилом).
Дополнительные примеры использования свойств
Пример доступа к координатам мыши
document.addEventListener('mousemove', (event) => {
console.log(`Позиция X: ${event.clientX}, Y: ${event.clientY}`);
console.log(`Координаты на странице: ${event.pageX}, ${event.pageY}`);
});
Пример проверки модификаторов клавиш
document.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.key === 's') {
event.preventDefault(); // Отменяем стандартное сохранение браузера
console.log('Выполняется кастомное сохранение...');
// Логика сохранения
}
});
Пример работы с данными в пользовательском событии
Код ITЗагрузка примера кода…
Пример разницы между target и currentTarget
<div id="parent">
<button id="child">Нажми меня</button>
</div>
<script>
const parent = document.getElementById('parent');
parent.addEventListener('click', (event) => {
console.log('Целевой элемент (target):', event.target.id); // child
console.log('Элемент обработчика (currentTarget):', event.currentTarget.id); // parent
});
</script>
При клике на кнопку id="child":
event.target— кнопка (источник клика);event.currentTarget—parent, потому что обработчик зарегистрирован на нём.
Та же пара свойств лежит в основе делегирования: currentTarget — контейнер со слушателем, target — конкретный кликнутый узел.
Краткий итог
Событие в браузере идёт по DOM в фазах capture → target → bubble. По умолчанию слушают всплытие; { capture: true } — перехват сверху вниз. stopPropagation() обрывает цепочку; target и currentTarget различают источник клика и элемент с обработчиком. Делегирование — один слушатель на родителе для динамических списков и сложных UI. Для форм, файлов и окна см. валидацию, чтение файлов и BOM.
Практический шаблон работы с событиями
Чтобы интерфейс не деградировал с ростом функциональности, полезно держать единый подход к обработке событий.
Базовые правила:
- использовать делегирование на стабильном контейнере;
- не выполнять тяжелые вычисления прямо в
click-обработчике; - аккуратно применять
stopPropagation, только когда действительно нужно; - обязательно снимать слушатели при уничтожении виджета.
Код ITЗагрузка примера кода…
Смежные материалы: