DOM-дерево и поиск элементов
DOM — это то, как JavaScript «видит» HTML-страницу. Не текст, а живое дерево объектов. Разберём структуру DOM и научимся находить любой элемент на странице.
🌳 Что такое DOM и зачем он нужен
Представьте: вы написали HTML-страницу. Это просто текстовый файл — набор символов, тегов, атрибутов. Браузер читает этот файл и создаёт из него нечто гораздо более мощное — DOM (Document Object Model).
DOM — это живое дерево объектов в памяти браузера. Каждый HTML-тег становится объектом (узлом). Между ними есть отношения: родитель, дочерний элемент, сосед. JavaScript работает именно с DOM — не с текстом HTML, а с этим деревом объектов.
💡 Аналогия: HTML — это чертёж здания на бумаге. DOM — это само здание, уже построенное. JavaScript — это рабочие, которые могут ходить по зданию, переставлять мебель, открывать двери и строить новые комнаты.
Вот простая HTML-страница:
<!DOCTYPE html>
<html>
<head>
<title>Моя страница</title>
</head>
<body>
<h1 id="title">Привет!</h1>
<p class="text">Первый абзац</p>
<p class="text">Второй абзац</p>
<ul>
<li>Пункт 1</li>
<li>Пункт 2</li>
</ul>
</body>
</html>
Браузер превращает это в дерево. Схематично оно выглядит так:
Каждый прямоугольник в этом дереве — узел (node). В DOM есть разные типы узлов: элементы (теги), текстовые узлы (содержимое), комментарии и другие. В 99% случаев вы работаете с элементами.
Точка входа в DOM — объект document. Это самый верхний узел, доступный из JavaScript. Через него можно добраться до любого элемента страницы.
🔍 getElementById — поиск по уникальному ID
Самый простой способ найти элемент — по его атрибуту id. ID должен быть уникальным на странице — только один элемент может иметь конкретный ID.
<!-- HTML: элемент с ID -->
<h1 id="main-title">Заголовок страницы</h1>
<button id="submit-btn">Отправить</button>
// JavaScript: нашли элемент по ID
const title = document.getElementById('main-title');
const btn = document.getElementById('submit-btn');
console.log(title); // HTMLHeadingElement (объект элемента h1)
console.log(title.textContent); // "Заголовок страницы"
// Если элемент не найден — возвращается null
const missing = document.getElementById('nope');
console.log(missing); // null
⚠️ Обратите внимание: в getElementById знак # писать не нужно. Просто имя ID: getElementById('main-title'), а не getElementById('#main-title'). Это частая ошибка новичков.
🎯 querySelector — поиск по CSS-селектору
querySelector — это мощный современный метод, который принимает любой CSS-селектор. Если вы умеете писать CSS — вы уже умеете пользоваться querySelector.
Важно: querySelector возвращает первый найденный элемент, или null если ничего не нашлось.
<!-- Вот наша HTML-страница -->
<div class="card">
<h2 class="card-title">Карточка 1</h2>
<p>Текст карточки</p>
<a href="https://example.com" data-type="external">Ссылка</a>
</div>
<div class="card">
<h2 class="card-title">Карточка 2</h2>
</div>
// querySelector принимает CSS-селекторы — те же правила, что в CSS!
// По тегу
const paragraph = document.querySelector('p'); // первый
// По классу (нужна точка)
const card = document.querySelector('.card'); // первый div.card
// По ID (нужна решётка)
const title = document.querySelector('#main-title');
// По атрибуту
const externalLink = document.querySelector('[data-type="external"]');
// Сложные CSS-селекторы — тоже работают!
const cardTitle = document.querySelector('.card .card-title'); // h2 внутри .card
const firstLi = document.querySelector('ul li:first-child');
console.log(card); // HTMLDivElement — первая карточка
console.log(cardTitle); // HTMLHeadingElement — заголовок первой карточки
💡 querySelector vs getElementById: querySelector('#id') и getElementById('id') делают одно и то же, но getElementById чуть быстрее. Однако querySelector удобнее — один метод для любых селекторов. В современном коде обычно используют querySelector.
📋 querySelectorAll — найти все подходящие элементы
querySelectorAll — как querySelector, но возвращает все совпадающие элементы, а не только первый. Результат — NodeList (коллекция узлов).
<ul id="todo-list">
<li class="task">Купить хлеб</li>
<li class="task done">Позвонить маме</li>
<li class="task">Сходить в спортзал</li>
</ul>
// Найти все элементы с классом .task
const tasks = document.querySelectorAll('.task');
console.log(tasks); // NodeList(3) [li, li, li]
console.log(tasks.length); // 3
// NodeList можно перебрать с помощью forEach:
tasks.forEach(function(task) {
console.log(task.textContent);
});
// "Купить хлеб"
// "Позвонить маме"
// "Сходить в спортзал"
// Или через цикл for...of:
for (const task of tasks) {
console.log(task.textContent);
}
// Найти только выполненные задачи
const doneTasks = document.querySelectorAll('.task.done');
console.log(doneTasks.length); // 1
⚠️ NodeList — не массив! У NodeList есть forEach и length, но нет map, filter, reduce. Чтобы получить настоящий массив: Array.from(tasks) или [...tasks]. После этого доступны все методы массива.
// Превращаем NodeList в массив — получаем все методы
const tasksArray = Array.from(document.querySelectorAll('.task'));
// Теперь можем использовать filter, map и другие методы массива
const notDone = tasksArray.filter(task => !task.classList.contains('done'));
console.log(notDone.length); // 2
🗿 Устаревшие методы — встречаются в чужом коде
Вы будете встречать в старом коде и туториалах другие методы поиска элементов. Самому их использовать не нужно, но понимать — важно.
// Ищет все элементы с указанным классом
const cards = document.getElementsByClassName('card');
// Возвращает HTMLCollection — "живую" коллекцию
// Автоматически обновляется при изменении DOM!
// Ищет все элементы с указанным тегом
const paragraphs = document.getElementsByTagName('p');
const allElements = document.getElementsByTagName('*'); // все элементы
- Возвращают HTMLCollection
- «Живая» — меняется вместе с DOM
- Нет метода forEach
- Не поддерживают сложные селекторы
- Возвращают NodeList
- «Статичная» — снимок на момент запроса
- Есть forEach
- Поддерживают любые CSS-селекторы
🔬 Поиск внутри конкретного элемента
querySelector и querySelectorAll можно вызывать не только на document, но и на любом элементе. Тогда поиск ограничится потомками этого элемента.
<div class="card" id="card1">
<h2>Заголовок 1</h2>
<p>Текст 1</p>
</div>
<div class="card" id="card2">
<h2>Заголовок 2</h2>
<p>Текст 2</p>
</div>
const card1 = document.getElementById('card1');
// Ищем h2 ТОЛЬКО внутри card1, а не по всей странице
const titleInCard1 = card1.querySelector('h2');
console.log(titleInCard1.textContent); // "Заголовок 1" (не "Заголовок 2"!)
// Все параграфы внутри card1
const paragraphsInCard1 = card1.querySelectorAll('p');
console.log(paragraphsInCard1.length); // 1 (не 2!)
Это очень полезно, когда у вас несколько похожих блоков и нужно работать с конкретным.
🧭 Навигация по дереву DOM
Когда вы нашли элемент, можно перемещаться по дереву от него к соседним узлам:
const item = document.querySelector('.task');
// Родительский элемент
item.parentElement; // ul#todo-list
// Дочерние элементы (только элементы, без текстовых узлов)
item.parentElement.children; // HTMLCollection всех li
// Первый и последний дочерний элемент
item.parentElement.firstElementChild; // первый li
item.parentElement.lastElementChild; // последний li
// Следующий и предыдущий соседний элемент
item.nextElementSibling; // следующий li
item.previousElementSibling; // предыдущий li (null если первый)
✅ Совет: используйте parentElement, children, nextElementSibling — они работают только с элементами (тегами). Есть аналоги без Element в названии (parentNode, childNodes) — они включают текстовые узлы и комментарии, что обычно ни к чему.
✅ Итоги урока
- DOM — живое дерево объектов в памяти браузера, которое создаётся из HTML. JavaScript работает с DOM, а не с текстом HTML
document— точка входа. Через него доступен весь DOMgetElementById('id')— быстрый поиск по уникальному ID, без знака #querySelector('.class')— поиск первого элемента по CSS-селекторуquerySelectorAll('.class')— поиск всех элементов, возвращает NodeList- NodeList поддерживает
forEach, но это не массив. Для полного набора методов:Array.from() - Поиск можно вызывать на элементе, а не только на document — тогда поиск идёт только в потомках
- Навигация по дереву:
parentElement,children,nextElementSibling
В следующем уроке мы научимся изменять найденные элементы — менять их текст, HTML и атрибуты. 🖊️
DOM-дерево и поиск элементов
8 вопросов
Поиск элементов по селекторам
Навигация по DOM-дереву
- ) — в переменную parent
// 2. Следующего соседнего элемента — в переменную next
// 3. Первого дочернего элемента родителя — в переменную firstChild
let parent = null;
let next = null;
let firstChild = null;
// Ваш код здесь (используйте item)