Пошук уроків, статей та іншого контенту
Створюватимете нові вузли, вставлятимете їх у документ, переміщатимете та видалятимете.
DOM — це об’єктне представлення HTML-документа у вигляді дерева вузлів. Елемент, текст усередині нього та коментар — різні типи вузлів.
Наприклад, для розмітки:
<ul id="tasks">
<li>Вивчити DOM</li>
</ul>браузер створює дерево приблизно такого вигляду:
Document
└── ul#tasks
└── li
└── "Вивчити DOM"JavaScript може:
створювати нові вузли;
змінювати їхні властивості;
вставляти вузли в документ;
переміщати вже існуючі вузли;
видаляти вузли;
замінювати одні вузли іншими.
Для створення HTML-елемента використовують document.createElement():
const paragraph = document.createElement("p");
paragraph.textContent = "Цей абзац створено JavaScript-кодом.";
paragraph.className = "message";
console.log(paragraph);На цьому етапі елемент існує лише в пам’яті. Він ще не відображається на сторінці. Щоб побачити його в документі, потрібно вставити його в DOM.
Текст можна додати через властивість textContent:
const title = document.createElement("h2");
title.textContent = "Список завдань";Також можна створити окремий текстовий вузол за допомогою document.createTextNode():
const text = document.createTextNode("Текстовий вузол");
const paragraph = document.createElement("p");
paragraph.append(text);У більшості випадків textContent простіший і зручніший.
textContenttextContent встановлює звичайний текст:
const message = document.createElement("p");
message.textContent = "<strong>Важливо</strong>";Браузер відобразить рядок <strong>Важливо</strong> як текст, а не як HTML-елемент. Це безпечна поведінка, якщо вміст походить від користувача.
innerHTMLinnerHTML дозволяє встановити HTML-розмітку:
const message = document.createElement("p");
message.innerHTML = "<strong>Важливо</strong>";У результаті всередині p буде створено елемент strong.
Не слід вставляти в innerHTML неперевірені дані від користувача:
const userInput = "<img src=x onerror=alert('Небезпечно')>";
// Потенційно небезпечно
element.innerHTML = userInput;Для тексту, введеного користувачем, використовуйте textContent.
Атрибути можна встановлювати кількома способами:
const link = document.createElement("a");
link.setAttribute("href", "/about");
link.setAttribute("target", "_blank");
link.textContent = "Про сайт";Для стандартних властивостей часто зручно використовувати прямий запис:
const input = document.createElement("input");
input.type = "email";
input.name = "email";
input.placeholder = "Введіть email";
input.disabled = false;Для класів використовуйте className або classList:
const card = document.createElement("article");
card.className = "card";
card.classList.add("card--featured");
card.classList.remove("card--featured");
card.classList.toggle("is-hidden");Булеві атрибути, наприклад disabled, checked або required, краще змінювати через відповідні властивості:
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = true;
checkbox.required = true;append()Метод append() додає один або кілька вузлів у кінець елемента:
const list = document.querySelector("#tasks");
const item = document.createElement("li");
item.textContent = "Написати тест";
list.append(item);Метод також приймає текстові рядки:
list.append("Новий текст");Однак для створення елементів краще явно створювати вузол, якщо потрібна структурована розмітка.
prepend()prepend() додає вузол на початок:
const firstItem = document.createElement("li");
firstItem.textContent = "Перше завдання";
list.prepend(firstItem);appendChild()appendChild() додає один вузол у кінець:
const item = document.createElement("li");
item.textContent = "Ще одне завдання";
list.appendChild(item);На відміну від append(), цей метод приймає лише вузол і повертає доданий вузол:
const addedItem = list.appendChild(item);
console.log(addedItem === item); // trueУ сучасному коді для звичайного додавання часто використовують append(), оскільки він підтримує кілька аргументів і рядки.
before() та after()Ці методи вставляють вузол відповідно перед або після поточного елемента:
const currentItem = document.querySelector("li");
const beforeItem = document.createElement("li");
beforeItem.textContent = "Завдання перед першим";
const afterItem = document.createElement("li");
afterItem.textContent = "Завдання після першого";
currentItem.before(beforeItem);
currentItem.after(afterItem);insertBefore()Метод insertBefore() викликається на батьківському елементі:
const list = document.querySelector("#tasks");
const firstItem = list.firstElementChild;
const newItem = document.createElement("li");
newItem.textContent = "Вставлено перед першим завданням";
list.insertBefore(newItem, firstItem);Якщо другим аргументом передати null, вузол буде додано в кінець:
list.insertBefore(newItem, null);Нижче наведено повністю runnable-приклад. Він створює завдання, додає їх у список, позначає виконані завдання та видаляє їх.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>Список завдань</title>
<style>
body {
font-family: sans-serif;
max-width: 600px;
margin: 40px auto;
}
form {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
input {
flex: 1;
padding: 8px;
}
button {
cursor: pointer;
}
ul {
padding: 0;
list-style: none;
}
li {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
border-bottom: 1px solid #ddd;
}
li.completed .task-title {
color: #777;
text-decoration: line-through;
}
.task-title {
flex: 1;
}
</style>
</head>
<body>
<h1>Список завдань</h1>
<form id="task-form">
<input
id="task-input"
type="text"
placeholder="Нове завдання"
autocomplete="off"
>
<button type="submit">Додати</button>
</form>
<ul id="task-list"></ul>
<script>
const form = document.querySelector("#task-form");
const input = document.querySelector("#task-input");
const list = document.querySelector("#task-list");
function createTaskElement(taskText) {
const item = document.createElement("li");
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.className = "task-checkbox";
const title = document.createElement("span");
title.className = "task-title";
// textContent не сприймає введений текст як HTML
title.textContent = taskText;
const deleteButton = document.createElement("button");
deleteButton.type = "button";
deleteButton.className = "delete-button";
deleteButton.textContent = "Видалити";
checkbox.addEventListener("change", () => {
item.classList.toggle("completed", checkbox.checked);
});
deleteButton.addEventListener("click", () => {
item.remove();
});
item.append(checkbox, title, deleteButton);
return item;
}
form.addEventListener("submit", (event) => {
event.preventDefault();
const taskText = input.value.trim();
if (taskText === "") {
return;
}
const taskElement = createTaskElement(taskText);
list.append(taskElement);
input.value = "";
input.focus();
});
</script>
</body>
</html>Функція createTaskElement() створює повністю підготовлений елемент завдання, але не вставляє його в документ. Вставлення відбувається окремо:
const taskElement = createTaskElement(taskText);
list.append(taskElement);Такий поділ відповідає принципу єдиної відповідальності: одна функція створює елемент, інша частина коду вирішує, куди його вставити.
Вузол може мати лише одного батька. Якщо додати вже існуючий елемент в інше місце, він не копіюється, а переміщується.
const list = document.querySelector("#tasks");
const firstItem = list.firstElementChild;
const anotherContainer = document.querySelector("#another-container");
anotherContainer.append(firstItem);Після цього firstItem більше не буде дочірнім елементом list.
Це зручно для переміщення елементів між контейнерами:
const activeList = document.querySelector("#active");
const completedList = document.querySelector("#completed");
const item = document.querySelector(".task");
completedList.append(item);Важливо: обробники подій, додані через addEventListener(), зазвичай зберігаються під час переміщення елемента.
Щоб створити копію вузла, використовуйте cloneNode():
const original = document.querySelector(".card");
const copy = original.cloneNode(true);
document.body.append(copy);Аргумент:
false або відсутній — копіюється лише сам елемент без дочірніх вузлів;
true — копіюється елемент разом із усіма нащадками.
const shallowCopy = original.cloneNode();
const deepCopy = original.cloneNode(true);Обробники подій, додані через addEventListener(), не копіюються разом із вузлом:
const button = document.querySelector("button");
button.addEventListener("click", () => {
console.log("Натиснуто");
});
const copy = button.cloneNode(true);
// У копії немає обробника, доданого через addEventListener()Якщо клон має працювати так само, подію потрібно додати окремо або використати делегування подій.
DocumentFragmentЯкщо потрібно створити багато вузлів, можна спочатку додати їх до DocumentFragment, а потім вставити фрагмент у документ одним викликом.
const list = document.querySelector("#tasks");
const fragment = document.createDocumentFragment();
for (let index = 1; index <= 5; index += 1) {
const item = document.createElement("li");
item.textContent = `Завдання ${index}`;
fragment.append(item);
}
list.append(fragment);DocumentFragment — тимчасовий контейнер, який не є частиною видимого документа. Коли його вставляють у DOM, його дочірні вузли переміщуються до цільового елемента, а сам фрагмент не стає додатковим елементом у розмітці.
Це корисно, коли потрібно побудувати список або інший великий фрагмент DOM перед вставленням.
replaceWith()Метод replaceWith() замінює поточний вузол одним або кількома іншими вузлами:
const oldTitle = document.querySelector("h1");
const newTitle = document.createElement("h1");
newTitle.textContent = "Оновлений заголовок";
oldTitle.replaceWith(newTitle);Можна замінити елемент текстом:
const placeholder = document.querySelector(".placeholder");
placeholder.replaceWith("Дані завантажено");replaceChild()Старіший варіант — викликати replaceChild() на батьківському елементі:
const container = document.querySelector("#container");
const oldElement = container.firstElementChild;
const newElement = document.createElement("p");
newElement.textContent = "Новий елемент";
container.replaceChild(newElement, oldElement);Для нового коду зазвичай зручніше використовувати replaceWith().
remove()Щоб видалити елемент, викличте його метод remove():
const item = document.querySelector(".task");
if (item) {
item.remove();
}Перевірка потрібна, якщо елемент може бути відсутнім. Виклик remove() для null спричинить помилку, тому такий код небезпечний:
const item = document.querySelector(".task");
// Помилка, якщо елемент не знайдено
item.remove();removeChild()Старий спосіб видалення:
const list = document.querySelector("#tasks");
const item = list.firstElementChild;
if (item) {
list.removeChild(item);
}Метод remove() простіший, оскільки не потрібно окремо отримувати батьківський елемент.
Щоб видалити всіх дочірніх елементів контейнера:
const list = document.querySelector("#tasks");
list.replaceChildren();Це сучасний і зрозумілий спосіб очищення.
Також можна встановити порожній рядок:
list.innerHTML = "";Проте replaceChildren() не виконує парсинг HTML і прямо описує намір очистити контейнер.
insertAdjacentHTML()Метод insertAdjacentHTML() вставляє HTML у певну позицію відносно елемента:
const list = document.querySelector("#tasks");
list.insertAdjacentHTML(
"beforeend",
"<li>Нове завдання</li>"
);Можливі позиції:
"beforebegin" — перед самим елементом;
"afterbegin" — на початку вмісту елемента;
"beforeend" — у кінці вмісту елемента;
"afterend" — після самого елемента.
Приклад:
const title = document.querySelector("h1");
title.insertAdjacentHTML(
"afterend",
"<p>Цей абзац вставлено після заголовка.</p>"
);Як і у випадку з innerHTML, не вставляйте таким способом неперевірені дані користувача. Для динамічного тексту безпечніше створити елемент і встановити textContent.
Елементи, створені після завантаження сторінки, також повинні реагувати на події. Один зі способів — додавати обробник кожному новому елементу під час створення.
Інший спосіб — делегування подій. Обробник додається до спільного контейнера:
const list = document.querySelector("#tasks");
list.addEventListener("click", (event) => {
const deleteButton = event.target.closest(".delete-button");
if (!deleteButton) {
return;
}
const item = deleteButton.closest("li");
if (item) {
item.remove();
}
});Подія від кнопки поширюється до list, де її обробляє один спільний обробник. Це особливо зручно для великих або часто змінюваних списків.
Зазвичай роботу можна розділити на такі кроки:
Знайти батьківський контейнер.
Створити новий елемент через document.createElement().
Встановити текст, класи, атрибути та властивості.
Додати дочірні елементи.
Підключити обробники подій.
Вставити готовий елемент у документ.
const container = document.querySelector("#container");
const article = document.createElement("article");
article.className = "card";
const heading = document.createElement("h2");
heading.textContent = "Заголовок картки";
const description = document.createElement("p");
description.textContent = "Опис картки.";
article.append(heading, description);
container.append(article);const button = document.querySelector("#save");
button.addEventListener("click", saveData);Якщо елемента з таким ідентифікатором немає, button дорівнюватиме null.
Безпечніший варіант:
const button = document.querySelector("#save");
if (button) {
button.addEventListener("click", saveData);
}Або запускайте код після того, як потрібна розмітка вже завантажена, наприклад перед закривальним тегом body.
innerHTML для неперевірених данихconst name = userInput;
// Небезпечно, якщо userInput походить від користувача
message.innerHTML = name;Для звичайного тексту використовуйте:
message.textContent = name;anotherContainer.append(element);Цей код перемістить element, а не створить його копію. Якщо потрібен дубль:
anotherContainer.append(element.cloneNode(true));cloneNode(true)Клонування копіює атрибути та дочірні вузли, але не обробники подій, додані через addEventListener(). Після клонування події потрібно налаштувати окремо або використовувати делегування.
Цей варіант може бути менш ефективним для великої кількості елементів:
for (const product of products) {
const item = document.createElement("li");
item.textContent = product.name;
list.append(item);
}Для великих списків зручно спочатку створити DocumentFragment, а потім вставити його один раз:
const fragment = document.createDocumentFragment();
for (const product of products) {
const item = document.createElement("li");
item.textContent = product.name;
fragment.append(item);
}
list.append(fragment);Створіть список покупок із такими можливостями:
поле введення назви товару;
кнопка «Додати»;
додавання товару в список;
позначення товару як купленого;
видалення товару;
кнопка «Очистити список».
Для створення кожного пункту використайте document.createElement(), для вставлення — append(), а для видалення — remove().
document.createElement() створює новий елемент у пам’яті.
textContent безпечно додає текст.
innerHTML та insertAdjacentHTML() працюють із HTML-розміткою, але потребують обережності з неперевіреними даними.
append() додає вузли в кінець, а prepend() — на початок.
before() та after() вставляють вузли поруч із поточним елементом.
Уже існуючий вузол під час повторного додавання переміщується.
cloneNode(true) створює глибоку копію вузла, але не копіює обробники подій.
replaceWith() замінює елемент іншим вузлом.
remove() видаляє елемент із DOM.
DocumentFragment допомагає підготувати багато вузлів перед одноразовою вставкою.
Делегування подій спрощує роботу з динамічно створеними елементами.