Перейти к основному содержимому

События и обработка событий в браузере

Разработчику Архитектору

События

Что такое событие?

Событие — это сигнал о том, что что-то произошло. Например — пользователь кликнул по кнопке, загрузилась страница, или вы сами отправили событие программно, чтобы уведомить другие части приложения.

В браузере событие связано с 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 вешают слушатель, когда нужно делегирование — один обработчик на список кнопок вместо подписки на каждую кнопку.

Существует множество встроенных событий, которые можно использовать:

  1. События мыши:
    • click — клик мышью
    • mouseover / mouseout — наведение/уход курсора
    • mousedown / mouseup — нажатие/отпускание кнопки мыши
  2. Клавиатурные события:
    • keydown — нажата клавиша
    • keyup — клавиша отпущена
  3. Формы:
    • submit — отправка формы
    • focus / blur — фокус на элементе / потеря фокуса
    • change — изменение значения элемента формы
  4. 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 — данные, переданные в кастомном событии

Подробнее про фазы и делегирование — в разделе ниже.

image-10.png


Алгоритмические шаблоны

Регистрация обработчика события

<элемент>.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.eventPhase1 — 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'));

Клик по кнопке в консоли:

  1. child
  2. parent

Сначала цель, затем предки — так устроено всплытие.


Перехват (capturing)

Тот же HTML; родитель слушает capture, кнопка — bubble:

parent.addEventListener('click', () => console.log('parent (capture)'), { capture: true });
child.addEventListener('click', () => console.log('child'));

Клик по кнопке:

  1. parent (capture)
  2. 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 и т.д.).
repeattrue, если событие генерируется при удержании клавиши.
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.currentTargetparent, потому что обработчик зарегистрирован на нём.

Та же пара свойств лежит в основе делегирования: currentTarget — контейнер со слушателем, target — конкретный кликнутый узел.


Краткий итог

Событие в браузере идёт по DOM в фазах capture → target → bubble. По умолчанию слушают всплытие; { capture: true } — перехват сверху вниз. stopPropagation() обрывает цепочку; target и currentTarget различают источник клика и элемент с обработчиком. Делегирование — один слушатель на родителе для динамических списков и сложных UI. Для форм, файлов и окна см. валидацию, чтение файлов и BOM.


Практический шаблон работы с событиями

Чтобы интерфейс не деградировал с ростом функциональности, полезно держать единый подход к обработке событий.

Базовые правила:

  • использовать делегирование на стабильном контейнере;
  • не выполнять тяжелые вычисления прямо в click-обработчике;
  • аккуратно применять stopPropagation, только когда действительно нужно;
  • обязательно снимать слушатели при уничтожении виджета.

Код ITЗагрузка примера кода…

Смежные материалы:

Содержание