Дерево DOM
Определение
DOM (Document Object Model) — это иерархическая структура, представляющая HTML-документ в виде дерева узлов. Каждый узел может быть:
- Элементом (например,
<div>,<span>) - Текстом (например,
Hello world!) - Атрибутом (например,
class="example")
DOM позволяет взаимодействовать с HTML-страницей, изменять её содержимое, стили и структуру с помощью JavaScript.
Операции с document
DOM предоставляет методы для работы с узлами:
document.querySelector(selector)— находит первый элемент по селектору.document.querySelectorAll(selector)— находит все элементы по селектору.document.getElementById(id)— получает элемент поid.document.getElementsByClassName(className)— получает коллекцию элементов поclass.document.getElementsByTagName(tagName)— получает коллекцию элементов потегу.
Поиск элементов определённого цвета (DFS и BFS)
1. DFS (Поиск в глубину)
Используется стек (рекурсия или Stack), обход происходит "вглубь", сначала посещаются дочерние элементы.
function findElementsByColorDFS(root, color) {
let result = [];
function dfs(node) {
if (!node) return;
if (window.getComputedStyle(node).color === color) {
result.push(node);
}
for (let child of node.children) {
dfs(child);
}
}
dfs(root);
return result;
}
// Пример использования:
let elements = findElementsByColorDFS(document.body, "rgb(255, 0, 0)"); // ищем красные элементы
console.log(elements);
2. BFS (Поиск в ширину)
Используется очередь (Queue), элементы обрабатываются "по уровням".
function findElementsByColorBFS(root, color) {
let result = [];
let queue = [root];
while (queue.length) {
let node = queue.shift();
if (window.getComputedStyle(node).color === color) {
result.push(node);
}
for (let child of node.children) {
queue.push(child);
}
}
return result;
}
// Пример использования:
let elements = findElementsByColorBFS(document.body, "rgb(255, 0, 0)");
console.log(elements);
Сравнение DFS и BFS в DOM-дереве
Когда использовать DFS и BFS в DOM?
Оба алгоритма — DFS (глубина) и BFS (ширина) — применяются для обхода DOM-дерева, но их производительность и применимость зависят от размера и структуры документа.
Основные отличия в работе алгоритмов
| Метод | Структура данных | Преимущества | Недостатки | Когда использовать |
|---|---|---|---|---|
| DFS (глубина) | Стек (Stack) (рекурсия или массив) | 1. Экономит память на мелких деревьях. 2. Удобно применять при поиске глубоко вложенных элементов. | 1. Риск переполнения стека в больших деревьях (Stack Overflow). 2. В среднем требует больше операций возврата, чем BFS. 3. Может быть непредсказуемым при работе с асинхронными изменениями DOM. | Редкие случаи: 1. Если DOM-дерево глубокое, но узкое. 2. Когда нужен поиск только одного элемента в глубине. |
| BFS (ширина) | Очередь (Queue) (массив или shift()) | 1. Гарантирует, что первый найденный элемент — ближайший. 2. Не вызывает переполнение стека, так как использует итеративный подход. 3. Эффективен при работе с широкими деревьями. | 1. Требует больше памяти при очень широком дереве (может расти в ширину). 2. Операция shift() в JS-массиве медленнее, чем pop() (но это можно обойти). | Лучший вариант в 90% случаев: 1. Если документ большой (например, сайт с тысячами элементов). 2. Когда нужен поиск первого найденного элемента. 3. При массовых обновлениях (BFS обходит ближайшие элементы первым). |
Почему BFS предпочтительнее для DOM?
-
Безопасность против Stack Overflow
- DFS использует стек, а стек в JavaScript имеет ограниченный размер (~10,000 вызовов, но браузеры могут отличаться).
- Глубокий DOM (например, таблицы или вложенные
div) может привести кMaximum call stack size exceeded. - BFS не использует рекурсию, поэтому не имеет этой проблемы.
-
Оптимизированный поиск ближайшего элемента
- BFS обходит узлы "по уровням", поэтому находит нужный элемент быстрее, если он ближе к корню.
- DFS может уйти вглубь и пропустить более близкий элемент.
-
Хорошо работает с динамическим DOM
- В реальном мире элементы DOM могут добавляться и удаляться асинхронно (например, при бесконечной прокрутке или динамических изменениях контента).
- BFS быстрее адаптируется, так как проходит "первый уровень", обновляя список дочерних элементов.
- DFS может столкнуться с удалёнными узлами и выбрасывать ошибки.
-
Более предсказуемый при модификациях
- Если во время обхода изменяется структура DOM, BFS лучше справляется.
- DFS может "застрять", если ожидает определённое количество дочерних элементов, а они изменились.
Когда DFS всё же полезен в DOM?
-
Когда точно знаем, что глубина ограничена
- Например, поиск элемента в простом списке вложенных
ul > li. - В этом случае DFS может работать быстрее, так как тратит меньше памяти.
- Например, поиск элемента в простом списке вложенных
-
Когда нужен поиск только в глубине
- Например, поиск самого вложенного элемента (
<div><div><div>...</div></div></div>). - BFS в этом случае обойдет слишком много ненужных элементов, а DFS сразу пойдет вниз.
- Например, поиск самого вложенного элемента (
Вывод
BFS почти всегда предпочтителен для обхода DOM, потому что:
Гарантирует, что первый найденный элемент — ближайший
Не вызывает переполнение стека
Лучше работает с динамическим DOM
DFS стоит применять только если дерево гарантированно маленькое и неглубокое.