DOM и события
Финальный аккорд: JavaScript управляет страницей — находит элементы, меняет их и реагирует на действия пользователя.
Что такое DOM
DOM (Document Object Model) — древовидная модель документа. Браузер превращает HTML в дерево объектов, а JavaScript умеет ходить по этому дереву, читать и менять его. Любая «живая» страница — это работа с DOM.
Поиск элементов
const h1 = document.querySelector("h1"); // первый совпавший
const cards = document.querySelectorAll(".card"); // все
const btn = document.getElementById("save"); // по id
console.log(h1.textContent); // читаем текст
Изменение элементов
const h1 = document.querySelector("h1");
h1.textContent = "Новый заголовок";
const card = document.querySelector(".card");
card.style.backgroundColor = "#1e293b";
card.classList.add("active"); // или remove / toggle
textContent — безопасный текст, innerHTML подставляет HTML-код. Используйте innerHTML аккуратно: если подставляете пользовательский ввод, есть риск XSS-атак.События
Событие — сигнал «что-то произошло». Слушаем клики:
const btn = document.querySelector("#likeBtn");
btn.addEventListener("click", () => {
likes++;
document.querySelector("#likeCount").textContent = likes;
});
Популярные события: click, input, submit, keydown, scroll, mouseover.
Живой пример: счётчик кликов
Полный интерактив — тот же код, что и выше, уже живёт здесь:
Создание элементов
Можно строить элементы программно и добавлять их в документ:
const li = document.createElement("li");
li.textContent = "Новый пункт";
list.appendChild(li);
list.innerHTML = ""; // очистить список
Финальный мини-проект
Соберите всё вместе — маленький «todo-список»:
- Поле ввода и кнопка «Добавить».
- По клику — создать элемент
<li>и добавить в список. - Клик по элементу — перечёркивать его (класс
.done). - Использовать
event.preventDefault()на форме, чтобы страница не перезагружалась.
const input = document.querySelector("#task");
const list = document.querySelector("#list");
form.addEventListener("submit", (e) => {
e.preventDefault(); // без перезагрузки
const li = document.createElement("li");
li.textContent = input.value;
li.addEventListener("click", () => li.classList.toggle("done"));
list.appendChild(li);
input.value = "";
});
Путешествие по дереву DOM
DOM — дерево, поэтому от любого элемента можно гулять к соседям: родителю, детям, братьям и сёстрам:
const el = document.querySelector(".card");
el.parentElement; // родитель
el.children; // массив прямых детей
el.firstElementChild; // первый ребёнок
el.lastElementChild; // последний ребёнок
el.nextElementSibling; // следующий сосед
el.previousElementSibling; // предыдущий сосед
parentElement: нашли кнопку, у неё поднялись к карточке — и уже её правим.Клавиатура и ввод: событие input
Событие input срабатывает на каждое изменение текста, а keydown ловит нажатия клавиш:
const search = document.querySelector("#search");
const hint = document.querySelector("#count");
search.addEventListener("input", () => {
hint.textContent = search.value.length + " симв.";
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") closeModal(); // ESC закрывает окно
});
e.key — имя клавиши («Enter», «Escape», «ArrowUp»). Так делают «живой счётчик символов», перенос фокуса и горячие клавиши.Делегирование событий
Один слушатель на родителя вместо сотни на детей — событие «всплывает» вверх, и мы ловим его один раз:
document.querySelector(".todo-list").addEventListener("click", (e) => {
const item = e.target.closest(".todo-item");
if (item) item.classList.toggle("done");
});
localStorage — память браузера
Данные можно сохранить между визитами без сервера. Внутри хранятся только строки, поэтому объекты сериализуем через JSON:
const theme = { dark: true };
localStorage.setItem("theme", JSON.stringify(theme)); // сохранить
const saved = JSON.parse(localStorage.getItem("theme")); // прочитать
localStorage.removeItem("theme"); // удалить
classList и data-атрибуты
Классами управляют через classList, а свои данные читают через dataset — модернизированный способ работы с data-*:
const box = document.querySelector(".box");
box.classList.add("active"); // добавить
box.classList.remove("active"); // убрать
box.classList.toggle("active"); // переключить
box.classList.contains("active"); // проверить
<div data-id="42">...</div>
box.dataset.id; // "42" — data-id превращается в dataset.id
.selected, не меняя остальных.Таймеры: setTimeout и setInterval
JS умеет откладывать запуск кода во времени — без сервера и без блокировки страницы:
const timer = setTimeout(() => {
console.log("через 3 секунды");
}, 3000);
clearTimeout(timer); // отменить, если передумали
const tick = setInterval(() => {
seconds++;
}, 1000); // каждую секунду
clearInterval(tick); // остановить
Стили и атрибуты через JS
Кроме классов, в DOM можно править стили и атрибуты напрямую — иногда это проще:
const el = document.querySelector(".card");
el.style.width = "300px"; // инлайн-стиль
el.setAttribute("title", "Помощь");
el.removeAttribute("disabled");
el.getAttribute("data-tooltip"); // просто прочитать
style используют в анимациях. Для логики — лучше классы: стили остаются «в одном месте» и не размазаны по JS.fetch: получение данных с сервера
Браузерный fetch запрашивает данные по API. Ответ обрабатывается цепочкой .then() — она приходит не сразу, поэтому код «продолжается» по готовности:
fetch("https://api.example.com/users")
.then(res => res.json())
.then(users => {
displayUsers(users);
})
.catch(err => {
console.error("Сеть упала:", err);
});
async/await — тоньше и ближе к обычному коду. Но с цепочками .then() начинают такой стиль понимать.Несколько обработчиков на одном элементе
addEventListener не заменяет старый обработчик — он добавляет ещё один:
const btn = document.querySelector("#green");
btn.addEventListener("click", () => console.log("1-й слушатель"));
btn.addEventListener("click", () => console.log("2-й слушатель"));
// сработают оба, по порядку
once и removeEventListener
Реакция один раз или отписка, когда обработчик больше не нужен:
btn.addEventListener("click", handler, { once: true });
// или снятие вручную (handler должна быть та же функция):
btn.removeEventListener("click", handler);
submit и preventDefault
Форма при отправке сама перезагружает страницу. Обычно мешает:
form.addEventListener("submit", (e) => {
e.preventDefault(); // отменяем перезагрузку
console.log("Отправлено без перезагрузки");
});
Остановка всплытия: stopPropagation
События «всплывают» от кликнутого элемента к предкам — это иногда мешает:
btn.addEventListener("click", (e) => {
e.stopPropagation(); // предки не узнают о клике
e.preventDefault(); // и браузерного действия не будет
});
Клавиатура: keydown, keyup
Горячие клавиши и поля проверяем по ключу кода:
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") closeModal();
if (e.code === "Enter") send();
});
e.key — символ («Enter», «я»), e.code — физическая клавиша (KeyW). Физику используйте для раскладок, символ для игр по букве.getBoundingClientRect: координаты
Точное положение элемента относительно окна — для всплывашек и анимаций:
const btnRect = btn.getBoundingClientRect();
btnRect.top; // от верхней кромки окна
btnRect.left;
btnRect.width; // ширина с учётом масштаба
insertAdjacentHTML: вставка куском
Рядом с элементом или вовнутрь можно вставить готовую HTML-строку:
const box = document.querySelector(".box");
box.insertAdjacentHTML("beforeend", "<span>конец списка</span>");
box.insertAdjacentHTML("afterbegin", "<span>начало</span>");
box.insertAdjacentHTML("beforebegin", "<p>перед блоком</p>");
Удаление узлов
Убрать элемент из документа проще простого:
const card = document.querySelector(".card");
card.remove(); // сам себя
parent.removeChild(card); // удаляет указанный ребёнок
closest: подъём по дереву
Ищем ближайшего предка с селектором (включая сам элемент):
const input = document.querySelector("input");
const form2 = input.closest("form"); // форма, если есть
const box = input.closest(".box");
FormData: форма в объект
Собрать все поля формы разом:
form.addEventListener("submit", (e) => {
e.preventDefault();
const data = new FormData(form);
console.log(data.get("name")); // значение поля name
console.log(Object.fromEntries(data));
});
scroll: событие и позиция
Следить за прокруткой, знать, насколько ушли вниз:
window.addEventListener("scroll", () => {
const scrolled = window.scrollY; // сколько пикселей после верха
if (scrolled > 200) header.classList.add("compact");
});
resize: размеры окна
Ловим повороты и ресайзы — например, переключать режим:
window.addEventListener("resize", () => {
const wide = window.innerWidth > 768;
document.body.classList.toggle("desktop", wide);
});
Живые и статические коллекции
querySelectorAll — статичный список, getElementsByClassName — живой:
const items1 = document.querySelectorAll("li"); // статическая
const items2 = document.getElementsByTagName("li"); // живая
list.append(document.createElement("li"));
items1.length; // не изменилось
items2.length; // изменилось — она «живая»
Инлайн-стили: style и setProperty
Точные значения пишутся прямо в style — например, анимации и метрики:
el.style.transform = "translateX(40px)";
el.style.width = `${w}px`;
// CSS-переменная — через setProperty:
el.style.setProperty("--gap", "12px");
classList.Что дальше
Вы прошли весь курс! Теперь стоит идти к большой практике:
- Возьмите макет из Figma — сверстайте его на HTML/CSS.
- Добавьте JS: меню бургер, слайдер, фильтрация товаров.
- Выложите сайт бесплатно на Netlify или GitHub Pages.
- После основ идите к фреймворкам: React или Vue.