Пошук уроків, статей та іншого контенту
Навчитеся додавати обробники подій і реагувати на типові дії користувача.
Подія — це сигнал про те, що в браузері щось відбулося. Наприклад:
користувач натиснув кнопку;
ввів текст у поле;
відправив форму;
навів курсор на елемент;
натиснув клавішу;
прокрутив сторінку;
завантажився документ.
JavaScript може «підписатися» на подію та виконати функцію, коли ця подія станеться. Таку функцію називають обробником події.
Наприклад:
const button = document.querySelector("button");
button.addEventListener("click", () => {
console.log("Кнопку натиснуто");
});Метод addEventListener приймає щонайменше два аргументи:
назву події;
функцію, яку потрібно виконати.
Розглянемо просту HTML-сторінку:
<button id="greetButton">Привітатися</button>
<p id="message"></p>Обробник події можна додати так:
const button = document.querySelector("#greetButton");
const message = document.querySelector("#message");
button.addEventListener("click", () => {
message.textContent = "Вітаю!";
});Після натискання кнопки текст елемента p буде змінено.
addEventListenerМожна призначити обробник через властивість onclick:
button.onclick = () => {
console.log("Кнопку натиснуто");
};Проте в такому випадку наступне присвоєння замінить попередній обробник:
button.onclick = () => {
console.log("Перший обробник");
};
button.onclick = () => {
console.log("Другий обробник");
};Під час натискання спрацює лише другий обробник.
addEventListener дозволяє додати кілька незалежних обробників:
button.addEventListener("click", () => {
console.log("Перший обробник");
});
button.addEventListener("click", () => {
console.log("Другий обробник");
});Обидві функції будуть викликані.
Під час виклику обробника браузер передає йому об’єкт події. За домовленістю його називають event або eventObject.
button.addEventListener("click", (event) => {
console.log(event);
});Об’єкт події містить інформацію про те, що сталося, де саме це сталося та які клавіші були натиснуті.
target і currentTargetВластивість event.target містить елемент, на якому фактично відбулася подія.
Властивість event.currentTarget містить елемент, на якому зареєстровано поточний обробник.
<div id="container">
<button id="button">Натиснути</button>
</div>const container = document.querySelector("#container");
container.addEventListener("click", (event) => {
console.log("target:", event.target);
console.log("currentTarget:", event.currentTarget);
});Якщо натиснути кнопку:
event.target буде кнопкою;
event.currentTarget буде div.
Ці властивості особливо важливі під час делегування подій.
Для натискання миші або активації елемента клавіатурою часто використовують подію click:
const button = document.querySelector("#saveButton");
button.addEventListener("click", () => {
console.log("Дані збережено");
});Події mouseenter і mouseleave спрацьовують, коли курсор відповідно входить на елемент або залишає його:
const card = document.querySelector(".card");
card.addEventListener("mouseenter", () => {
card.classList.add("active");
});
card.addEventListener("mouseleave", () => {
card.classList.remove("active");
});На відміну від mouseover і mouseout, події mouseenter та mouseleave не спливають від дочірніх елементів до батьківського.
Подія input спрацьовує щоразу, коли значення поля змінюється:
const input = document.querySelector("#search");
const result = document.querySelector("#searchResult");
input.addEventListener("input", (event) => {
result.textContent = `Пошук: ${event.target.value}`;
});Подія change зазвичай спрацьовує після завершення зміни значення, наприклад після втрати фокусу полем.
const nameInput = document.querySelector("#name");
nameInput.addEventListener("change", (event) => {
console.log("Нове значення:", event.target.value);
});Події focus і blur повідомляють про отримання та втрату фокусу:
const emailInput = document.querySelector("#email");
emailInput.addEventListener("focus", () => {
emailInput.classList.add("focused");
});
emailInput.addEventListener("blur", () => {
emailInput.classList.remove("focused");
});Для роботи з клавіатурою використовують події:
keydown — клавішу натиснуто;
keyup — клавішу відпущено.
document.addEventListener("keydown", (event) => {
console.log("Натиснуто клавішу:", event.key);
});Властивість event.key містить значення натиснутої клавіші:
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
console.log("Користувач натиснув Escape");
}
});Для комбінацій клавіш можна перевіряти властивості:
event.ctrlKey;
event.shiftKey;
event.altKey;
event.metaKey.
document.addEventListener("keydown", (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
event.preventDefault();
console.log("Збереження перехоплено");
}
});metaKey потрібна, зокрема, для клавіші Command у macOS, а ctrlKey — для Control у Windows та Linux.
Подія submit спрацьовує під час надсилання форми. Для обробки форми краще слухати саме submit, а не лише click кнопки. Це враховує також надсилання форми клавішею Enter.
<form id="loginForm">
<label>
Email
<input id="email" name="email" type="email" required>
</label>
<label>
Пароль
<input id="password" name="password" type="password" required>
</label>
<button type="submit">Увійти</button>
<p id="formMessage"></p>
</form>const form = document.querySelector("#loginForm");
const formMessage = document.querySelector("#formMessage");
form.addEventListener("submit", (event) => {
event.preventDefault();
const formData = new FormData(form);
const email = formData.get("email");
formMessage.textContent = `Форму підготовлено для ${email}`;
});preventDefaultЗа замовчуванням браузер виконує стандартну дію для деяких подій:
форма перезавантажує сторінку після надсилання;
посилання переходить на іншу адресу;
клавіші виконують стандартні команди.
Метод event.preventDefault() скасовує стандартну дію:
const link = document.querySelector("#externalLink");
link.addEventListener("click", (event) => {
event.preventDefault();
console.log("Перехід за посиланням скасовано");
});Скасовуйте стандартну поведінку лише тоді, коли справді потрібно замінити її власною логікою.
Події в DOM зазвичай спочатку виникають на цільовому елементі, а потім «спливають» до його батьківських елементів.
<div id="parent">
<button id="child">Натиснути</button>
</div>const parent = document.querySelector("#parent");
const child = document.querySelector("#child");
parent.addEventListener("click", () => {
console.log("Обробник батьківського елемента");
});
child.addEventListener("click", () => {
console.log("Обробник кнопки");
});Після натискання кнопки в консолі з’являться повідомлення в такому порядку:
Обробник кнопки;
Обробник батьківського елемента.
Щоб зупинити спливання, використовується event.stopPropagation():
child.addEventListener("click", (event) => {
event.stopPropagation();
console.log("Натиснуто кнопку");
});Тепер обробник на parent не буде викликаний через це натискання.
Не варто використовувати stopPropagation() без потреби: він може перешкодити іншим частинам програми реагувати на подію.
Делегування — це підхід, за якого один обробник додають до спільного батьківського елемента замість окремих обробників для кожного дочірнього елемента.
Це працює завдяки спливанню подій.
<ul id="taskList">
<li>
Вивчити події
<button data-action="delete">Видалити</button>
</li>
<li>
Написати приклад
<button data-action="delete">Видалити</button>
</li>
</ul>const taskList = document.querySelector("#taskList");
taskList.addEventListener("click", (event) => {
const deleteButton = event.target.closest('[data-action="delete"]');
if (!deleteButton || !taskList.contains(deleteButton)) {
return;
}
deleteButton.closest("li").remove();
});Метод closest(selector) шукає найближчого предка, який відповідає CSS-селектору. Він корисний, коли всередині кнопки є, наприклад, іконка або інший дочірній елемент.
Делегування особливо корисне, коли:
список містить багато елементів;
елементи додаються динамічно;
потрібно зменшити кількість обробників.
const list = document.querySelector("#taskList");
const addButton = document.querySelector("#addTask");
addButton.addEventListener("click", () => {
const item = document.createElement("li");
item.innerHTML = `
Нова задача
<button data-action="delete">Видалити</button>
`;
list.append(item);
});
list.addEventListener("click", (event) => {
if (event.target.matches('[data-action="delete"]')) {
event.target.closest("li").remove();
}
});Обробник на list працюватиме і для кнопок, які були додані після завантаження сторінки.
Щоб видалити обробник, потрібно передати до removeEventListener ту саму функцію, яка використовувалася під час додавання:
function handleClick() {
console.log("Натискання");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick);Цей код не спрацює так, як очікується:
button.addEventListener("click", () => {
console.log("Натискання");
});
button.removeEventListener("click", () => {
console.log("Натискання");
});Дві стрілкові функції мають різні посилання в пам’яті, хоча їхній код однаковий.
Обробник можна зняти, якщо відома його функція:
const handleMouseMove = (event) => {
console.log(event.clientX, event.clientY);
};
document.addEventListener("mousemove", handleMouseMove);
// Пізніше обробник більше не потрібен
document.removeEventListener("mousemove", handleMouseMove);addEventListenerТретій аргумент addEventListener може бути об’єктом налаштувань.
Параметр once: true автоматично видаляє обробник після першого виклику:
button.addEventListener(
"click",
() => {
console.log("Це повідомлення з’явиться лише один раз");
},
{ once: true }
);Параметр passive: true повідомляє браузеру, що обробник не викликатиме preventDefault():
window.addEventListener(
"scroll",
() => {
console.log("Сторінку прокручено");
},
{ passive: true }
);Це може допомогти браузеру ефективніше обробляти події прокручування. Не використовуйте passive: true, якщо в обробнику потрібно скасовувати стандартну дію.
Нижче наведено невеликий застосунок зі списком задач. Він демонструє:
обробку submit;
читання даних форми;
делегування подій;
використання event.target;
скасування стандартної поведінки форми.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>Список задач</title>
<style>
.completed {
text-decoration: line-through;
color: #777;
}
li {
margin: 8px 0;
}
button {
margin-left: 8px;
}
</style>
</head>
<body>
<h1>Список задач</h1>
<form id="taskForm">
<input
id="taskInput"
name="task"
type="text"
placeholder="Нова задача"
required
>
<button type="submit">Додати</button>
</form>
<ul id="taskList"></ul>
<script>
const form = document.querySelector("#taskForm");
const taskInput = document.querySelector("#taskInput");
const taskList = document.querySelector("#taskList");
form.addEventListener("submit", (event) => {
// Не перезавантажуємо сторінку після надсилання форми
event.preventDefault();
const taskText = taskInput.value.trim();
if (!taskText) {
return;
}
const item = document.createElement("li");
item.innerHTML = `
<span data-role="text"></span>
<button type="button" data-action="complete">
Виконано
</button>
<button type="button" data-action="delete">
Видалити
</button>
`;
item.querySelector('[data-role="text"]').textContent = taskText;
taskList.append(item);
form.reset();
taskInput.focus();
});
taskList.addEventListener("click", (event) => {
const actionButton = event.target.closest("[data-action]");
if (!actionButton) {
return;
}
const item = actionButton.closest("li");
const action = actionButton.dataset.action;
if (action === "complete") {
item.classList.toggle("completed");
}
if (action === "delete") {
item.remove();
}
});
</script>
</body>
</html>У цьому прикладі кнопки задач створюються динамічно. Обробники не додаються до кожної нової кнопки окремо. Замість цього один обробник працює на батьківському ul.
Якщо скрипт виконується раніше за HTML-елемент, querySelector може повернути null:
const button = document.querySelector("#button");
button.addEventListener("click", handleClick);Виникне помилка, якщо кнопки ще немає в DOM.
Варіанти розв’язання:
розмістити script перед закриваючим тегом body;
додати атрибут defer до зовнішнього скрипту;
виконати код після події DOMContentLoaded.
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector("#button");
button.addEventListener("click", () => {
console.log("Готово");
});
});click замість submit для формиЯкщо слухати лише натискання кнопки, надсилання клавішею Enter може залишитися необробленим. Для форми використовуйте подію submit.
targetevent.target може бути дочірнім елементом, а не елементом, який ви очікували:
<button id="button">
<span>Зберегти</span>
</button>Якщо користувач натисне на span, event.target буде span. Для пошуку кнопки можна використати closest:
document.addEventListener("click", (event) => {
const button = event.target.closest("button");
if (!button) {
return;
}
console.log("Натиснуто кнопку:", button.textContent);
});preventDefault() без потребиЯкщо скасувати стандартну дію посилання або форми без власної альтернативної логіки, елемент може перестати працювати для користувача.
Якщо кожен елемент списку має власний обробник, керування ним стає складнішим. Для динамічних списків часто краще використати делегування подій.
Щоб згодом видалити обробник, збережіть функцію в змінній або оголосіть її окремо:
const handler = () => {
console.log("Подія");
};
element.addEventListener("click", handler);
element.removeEventListener("click", handler);Створіть форму пошуку з полем введення та списком товарів.
Реалізуйте таку поведінку:
Під час введення тексту список фільтрується.
Пошук не залежить від регістру літер.
Якщо збігів немає, відображається повідомлення.
Натискання Escape очищає поле пошуку.
Обробник введення додається до поля через addEventListener.
Для фільтрації можна використати методи toLowerCase() і includes().
Події повідомляють JavaScript про дії користувача та зміни в браузері.
Обробники подій додають методом addEventListener.
Об’єкт події містить дані про подію та її джерело.
event.target — фактичний елемент, на якому сталася подія.
event.currentTarget — елемент із зареєстрованим обробником.
preventDefault() скасовує стандартну поведінку браузера.
Події можуть спливати від дочірніх елементів до батьківських.
Делегування подій дає змогу обробляти події динамічних елементів через спільного батька.
Для форм зазвичай потрібно обробляти submit, а для введення тексту — input.
Щоб видалити обробник, потрібно передати те саме посилання на функцію, яке використовувалося під час його додавання.