Пошук уроків, статей та іншого контенту
Розберете об’єкт події, його властивості та відмінності між подіями миші, клавіатури й введення.
Коли в браузері відбувається подія, обробник отримує аргумент — об’єкт події. Він містить інформацію про те, що сталося, де саме і в якому контексті.
button.addEventListener("click", (event) => {
console.log(event);
});Назву параметра можна змінити, але зазвичай використовують event або скорочення e.
Об’єкт події має спільні властивості для різних типів подій:
type — назва події, наприклад "click" або "keydown";
target — елемент, на якому фактично відбулася подія;
currentTarget — елемент, на якому зареєстровано обробник;
bubbles — чи поширюється подія вгору по DOM;
defaultPrevented — чи було скасовано стандартну дію;
timeStamp — приблизний момент виникнення події.
const list = document.querySelector("#tasks");
list.addEventListener("click", (event) => {
console.log("Тип:", event.type);
console.log("Фактична ціль:", event.target);
console.log("Елемент з обробником:", event.currentTarget);
});Якщо натиснути на дочірній елемент усередині #tasks, event.target і event.currentTarget можуть бути різними.
target і currentTargetРозглянемо розмітку:
<div id="card">
<button type="button">Видалити</button>
</div>const card = document.querySelector("#card");
card.addEventListener("click", (event) => {
console.log(event.target); // button
console.log(event.currentTarget); // div#card
});Подія кліку виникла на кнопці, але обробник зареєстрований на div.
event.target потрібен, щоб визначити елемент, який ініціював подію.
event.currentTarget потрібен, щоб звернутися до елемента, на якому працює поточний обробник.
Це особливо важливо для делегування подій:
const list = document.querySelector("#tasks");
list.addEventListener("click", (event) => {
const deleteButton = event.target.closest("[data-action='delete']");
if (!deleteButton || !list.contains(deleteButton)) {
return;
}
deleteButton.closest("li").remove();
});Один обробник на списку обробляє кліки по всіх поточних і майбутніх кнопках видалення.
Подія зазвичай проходить кілька етапів:
Фаза захоплення — подія рухається від window до цільового елемента.
Фаза цілі — подія досягає елемента, на якому сталася.
Фаза спливання — подія рухається від цілі назад угору по DOM.
За замовчуванням обробники працюють на фазі спливання.
const outer = document.querySelector("#outer");
const inner = document.querySelector("#inner");
outer.addEventListener("click", () => {
console.log("outer");
});
inner.addEventListener("click", () => {
console.log("inner");
});Якщо натиснути #inner, у консолі буде:
inner
outerЩоб зареєструвати обробник на фазі захоплення, передайте третім аргументом true або об’єкт із capture: true:
outer.addEventListener(
"click",
() => {
console.log("outer capture");
},
{ capture: true }
);Метод stopPropagation() зупиняє подальше поширення події.
inner.addEventListener("click", (event) => {
event.stopPropagation();
console.log("Клік лише на внутрішньому елементі");
});Метод stopImmediatePropagation() додатково зупиняє виклик інших обробників цієї самої події на поточному елементі. Використовуйте його обережно: він може ускладнити взаємодію між незалежними частинами програми.
Деякі події мають стандартну поведінку:
клік по посиланню переходить на іншу адресу;
відправлення форми перезавантажує сторінку;
натискання клавіші в полі введення змінює його значення;
натискання правої кнопки відкриває контекстне меню.
Щоб скасувати стандартну дію, використовуйте preventDefault().
const form = document.querySelector("#login-form");
form.addEventListener("submit", (event) => {
event.preventDefault();
console.log("Форму оброблено без перезавантаження сторінки");
});preventDefault() не зупиняє поширення події. Для цього потрібен окремий метод — stopPropagation().
До подій миші належать:
click — натискання та відпускання кнопки миші;
dblclick — подвійний клік;
mousedown — натискання кнопки;
mouseup — відпускання кнопки;
mousemove — рух миші;
mouseenter — вказівник увійшов в елемент;
mouseleave — вказівник залишив елемент;
contextmenu — відкриття контекстного меню.
Для подій миші властивість button показує кнопку, яка спричинила подію:
0 — основна кнопка, зазвичай ліва;
1 — середня кнопка;
2 — права кнопка.
document.addEventListener("mousedown", (event) => {
if (event.button === 0) {
console.log("Натиснуто ліву кнопку");
}
if (event.button === 2) {
console.log("Натиснуто праву кнопку");
}
});Властивість buttons є бітовою маскою і показує, які кнопки утримуються в цей момент. Вона особливо корисна під час mousemove:
document.addEventListener("mousemove", (event) => {
if (event.buttons === 1) {
console.log("Курсор рухається з утриманою основною кнопкою");
}
});Події вказівника містять кілька типів координат:
clientX, clientY — координати відносно області перегляду браузера;
pageX, pageY — координати відносно всієї сторінки, включно з прокруткою;
screenX, screenY — координати відносно екрана;
offsetX, offsetY — координати відносно цільового елемента.
Для малювання на полотні зазвичай потрібно врахувати його положення:
const canvas = document.querySelector("#drawing");
const context = canvas.getContext("2d");
canvas.addEventListener("mousemove", (event) => {
if (event.buttons !== 1) {
return;
}
const rectangle = canvas.getBoundingClientRect();
const x = event.clientX - rectangle.left;
const y = event.clientY - rectangle.top;
context.fillRect(x, y, 4, 4);
});Основні події клавіатури:
keydown — клавішу натиснуто;
keyup — клавішу відпущено.
Історичну подію keypress не варто використовувати в новому коді.
document.addEventListener("keydown", (event) => {
console.log("Клавіша:", event.key);
console.log("Фізична позиція:", event.code);
});key і codeevent.key описує символ або дію з урахуванням розкладки та модифікаторів:
document.addEventListener("keydown", (event) => {
console.log(event.key);
});Для англійської розкладки натискання клавіші A дасть "a", а з Shift — "A". В українській розкладці значення буде іншим.
event.code описує фізичну клавішу на клавіатурі. Для тієї самої клавіші це зазвичай "KeyA" незалежно від розкладки.
Використовуйте:
key, коли важливе введене значення або дія;
code, коли важливе фізичне розташування клавіші, наприклад для керування грою.
document.addEventListener("keydown", (event) => {
if (event.code === "ArrowUp") {
console.log("Рух угору");
}
if (event.key === "Escape") {
console.log("Закриття діалогу");
}
});Об’єкт клавіатурної події містить логічні властивості:
ctrlKey;
shiftKey;
altKey;
metaKey.
Вони дорівнюють true, якщо відповідну клавішу утримували під час події.
document.addEventListener("keydown", (event) => {
const isSaveShortcut =
(event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "s";
if (!isSaveShortcut) {
return;
}
event.preventDefault();
console.log("Дані збережено");
});Перевірка event.metaKey потрібна для клавіші Command на macOS, а event.ctrlKey — для Control у Windows і Linux.
Якщо користувач утримує клавішу, браузер може генерувати багато подій keydown. Властивість repeat показує, чи є подія автоматичним повторенням:
document.addEventListener("keydown", (event) => {
if (event.repeat) {
return;
}
console.log("Клавішу натиснуто вперше");
});Для полів форми важливо розрізняти input і change.
inputinput виникає щоразу, коли значення поля змінюється внаслідок дії користувача. Це основна подія для живого пошуку, валідації та оновлення інтерфейсу.
const search = document.querySelector("#search");
const result = document.querySelector("#result");
search.addEventListener("input", (event) => {
result.textContent = `Пошук: ${event.target.value}`;
});Подія input працює не лише для input type="text", а й для багатьох інших елементів форми, зокрема textarea, select і деяких типів input.
changechange сигналізує, що значення було змінене та підтверджене користувачем. Для текстового поля подія зазвичай відбувається після втрати фокуса, якщо значення змінилося. Для прапорців, перемикачів і списків вона виникає під час вибору.
const email = document.querySelector("#email");
email.addEventListener("change", (event) => {
console.log("Підтверджене значення:", event.target.value);
});Для текстового поля:
input — кожна зміна;
change — зміна підтверджена, зазвичай після втрати фокуса.
Не варто будувати обробку тексту лише на keydown. Текст може вводитися:
вставленням із буфера обміну;
автозаповненням;
голосовим введенням;
іншими методами введення;
редакторами для складних систем письма.
Тому для реакції на зміну текстового значення використовуйте input.
Під час введення деяких мов символ може формуватися в кілька етапів. Для цього існують події:
compositionstart — початок композиції;
compositionupdate — проміжна зміна;
compositionend — завершення композиції.
У більшості звичайних форм достатньо слухати input, але складні редактори можуть враховувати стан композиції.
const editor = document.querySelector("#editor");
let isComposing = false;
editor.addEventListener("compositionstart", () => {
isComposing = true;
});
editor.addEventListener("compositionend", () => {
isComposing = false;
console.log("Композицію завершено:", editor.value);
});
editor.addEventListener("input", () => {
if (isComposing) {
return;
}
console.log("Можна запускати звичайну обробку:", editor.value);
});У наступному прикладі:
input оновлює попередній перегляд і лічильник символів;
keydown обробляє Escape;
submit скасовує перезавантаження сторінки;
click делегується контейнеру;
preventDefault() скасовує стандартні дії.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>Об’єкт події</title>
<style>
body {
font-family: sans-serif;
max-width: 520px;
margin: 40px auto;
}
form {
display: grid;
gap: 12px;
}
textarea {
min-height: 120px;
resize: vertical;
}
.preview {
padding: 12px;
border: 1px solid #ccc;
min-height: 24px;
white-space: pre-wrap;
}
.error {
color: #b00020;
}
</style>
</head>
<body>
<form id="message-form">
<label for="message">Повідомлення</label>
<textarea
id="message"
name="message"
maxlength="120"
required
></textarea>
<div>
Символів: <span id="counter">0</span>/120
</div>
<div id="preview" class="preview"></div>
<p id="status" class="error"></p>
<div id="actions">
<button type="submit">Надіслати</button>
<button type="button" data-action="clear">Очистити</button>
</div>
</form>
<script>
const form = document.querySelector("#message-form");
const message = document.querySelector("#message");
const counter = document.querySelector("#counter");
const preview = document.querySelector("#preview");
const status = document.querySelector("#status");
const actions = document.querySelector("#actions");
message.addEventListener("input", (event) => {
const value = event.target.value;
counter.textContent = value.length;
preview.textContent = value;
status.textContent = "";
});
message.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
message.value = "";
message.dispatchEvent(new Event("input"));
}
});
form.addEventListener("submit", (event) => {
event.preventDefault();
if (!message.value.trim()) {
status.textContent = "Введіть повідомлення.";
message.focus();
return;
}
status.className = "";
status.textContent = "Повідомлення підготовлено до надсилання.";
});
actions.addEventListener("click", (event) => {
const clearButton = event.target.closest("[data-action='clear']");
if (!clearButton) {
return;
}
message.value = "";
message.dispatchEvent(new Event("input"));
message.focus();
});
</script>
</body>
</html>dispatchEvent() у цьому прикладі вручну запускає подію input, щоб після очищення повторно використати вже наявну логіку оновлення інтерфейсу.
Сучасний браузер також підтримує події вказівника:
pointerdown;
pointerup;
pointermove;
pointerenter;
pointerleave;
pointercancel.
Вони об’єднують взаємодію миші, пера та сенсорного екрана в одну модель.
Об’єкт PointerEvent додатково може містити:
pointerId — ідентифікатор конкретного вказівника;
pointerType — тип "mouse", "pen" або "touch";
pressure — сила натискання, якщо пристрій її підтримує.
const pad = document.querySelector("#pad");
pad.addEventListener("pointerdown", (event) => {
pad.setPointerCapture(event.pointerId);
console.log("Тип вказівника:", event.pointerType);
console.log("Тиск:", event.pressure);
});setPointerCapture() дозволяє продовжити отримувати події від конкретного вказівника, навіть якщо він залишив межі елемента. Це корисно для перетягування та малювання.
this в обробнику подіїУ звичайній функції, переданій до addEventListener, this посилається на currentTarget.
const button = document.querySelector("#button");
button.addEventListener("click", function (event) {
console.log(this === event.currentTarget); // true
});У стрілкової функції власного this немає, тому для надійності використовуйте event.currentTarget:
button.addEventListener("click", (event) => {
event.currentTarget.disabled = true;
});target замість currentTargetЯкщо обробник зареєстрований на контейнері, target може бути вкладеним елементом.
Неправильно:
container.addEventListener("click", (event) => {
event.target.disabled = true;
});Якщо користувач натисне на текст або іконку всередині кнопки, target може не мати властивості disabled.
Краще знайти потрібну кнопку:
container.addEventListener("click", (event) => {
const button = event.target.closest("button");
if (button) {
button.disabled = true;
}
});keydown для відстеження текстуkeydown не охоплює всі способи введення. Для зміни значення текстового поля використовуйте input.
preventDefault() і stopPropagation()preventDefault() скасовує стандартну дію браузера.
stopPropagation() зупиняє поширення події по DOM.
Це різні операції, і одна не замінює іншу.
click для складної взаємодіїclick зручний для кнопок і простих дій, але для перетягування, малювання або підтримки сенсорних пристроїв краще розглянути pointer-події.
stopPropagation()Зупинка поширення може зламати обробники батьківських компонентів, делегування подій або логіку доступності. Застосовуйте її лише тоді, коли справді потрібно ізолювати подію.
Перевірка лише event.key може перехопити звичайне натискання замість комбінації клавіш. Для гарячих клавіш перевіряйте ctrlKey, metaKey, shiftKey або altKey.
Для поля форми значення потрібно брати з event.target.value або з конкретного посилання на поле. Властивість textContent не містить введеного значення в input чи textarea.
Об’єкт події передається в обробник і містить дані про взаємодію.
target — елемент, на якому подія виникла, а currentTarget — елемент із поточним обробником.
preventDefault() скасовує стандартну дію, а stopPropagation() зупиняє поширення.
Для миші доступні кнопки, координати та модифікатори.
event.key описує символ або дію, а event.code — фізичну клавішу.
input підходить для реакції на кожну зміну значення, а change — для підтвердженої зміни.
Для взаємодії з мишею, пером і сенсорним екраном зручно використовувати Pointer Events.
Делегування подій дає змогу обробляти багато дочірніх елементів одним обробником.