Какие объекты считаются узлами DOM
Что является узлами в DOM?
Теория: что такое узел DOM
DOM представляет HTML-документ как дерево объектов, где базовой единицей является узел (node). Узел — это объект DOM, который участвует в дереве документа и имеет общий набор свойств/методов базового интерфейса Node.
Интерфейс Node является базовой абстракцией: “просто Node” как отдельный самостоятельный тип обычно не создается, но многие конкретные типы DOM-объектов реализуют его и поэтому считаются узлами. В веб-разработке чаще всего встречаются узлы документа (Document), элементы (Element), текст (Text), комментарии (Comment), а также фрагменты (DocumentFragment).
Ключевые типы узлов и nodeType
Для различения типов узлов используется числовой идентификатор nodeType. Для удобства чтения кода применяются константы вроде Node.ELEMENT_NODE, Node.TEXT_NODE и т. п.
Таблица наиболее важных типов nodeType (чаще всего применяются на практике):
| Тип узла | Константа | nodeType | Что представляет |
|---|---|---|---|
| Document | Node.DOCUMENT_NODE | 9 | документ целиком (document) |
| Element | Node.ELEMENT_NODE | 1 | HTML-элемент (<div>, <span>) |
| Text | Node.TEXT_NODE | 3 | текст внутри элементов |
| Comment | Node.COMMENT_NODE | 8 | комментарий <!-- ... --> |
| DocumentType | Node.DOCUMENT_TYPE_NODE | 10 | <!doctype html> |
| DocumentFragment | Node.DOCUMENT_FRAGMENT_NODE | 11 | временный контейнер поддерева |
| Attr (исторически важно знать) | Node.ATTRIBUTE_NODE | 2 | узел атрибута (в современной практике обычно обходится без него) |
Практика: как “увидеть” узлы
Проверка типа узла выполняется через nodeType, а понимание состава дочерних узлов — через childNodes (все узлы) и children (только элементы). Это помогает увидеть, что между HTML-тегами часто присутствуют текстовые узлы, даже если визуально кажется, что “там ничего нет”.
Пример: элемент и текстовый узел внутри него
const p = document.createElement("p");
p.textContent = "Once upon a time…";
console.log(p.nodeType === Node.ELEMENT_NODE); // true
console.log(p.firstChild.nodeType === Node.TEXT_NODE); // true
Пример: документ, doctype и фрагмент
console.log(document.nodeType === Node.DOCUMENT_NODE); // true
console.log(document.doctype.nodeType === Node.DOCUMENT_TYPE_NODE); // true
const frag = document.createDocumentFragment();
console.log(frag.nodeType === Node.DOCUMENT_FRAGMENT_NODE); // true
Пример: комментарий как отдельный узел
const comment = document.createComment("hi");
console.log(comment.nodeType === Node.COMMENT_NODE); // true
console.log(comment.nodeName); // обычно "#comment"
Пример: разница childNodes и children (частая причина ошибок у начинающих)
Однострочные фрагменты кода следует оборачивать в кавычки кода: childNodes и children.
// HTML: <div id="root">A<span>B</span><!--C--></div>
const root = document.getElementById("root");
console.log(root.childNodes.length); // элементы + текст + комментарии
console.log(root.children.length); // только элементы
Если в разметке есть текст между тегами (включая переносы строк и пробелы форматирования), в childNodes появятся узлы Text. Из-за этого цикл по childNodes может неожиданно “натыкаться” на текст, хотя ожидались только элементы.
Схема дерева (упрощенно), показывающая смешанные типы дочерних узлов:
Document
└── html (Element)
└── body (Element)
└── div#root (Element)
├── #text "A" (Text)
├── span (Element)
│ └── #text "B" (Text)
└── #comment "C" (Comment)
В итоге: верным является вариант 3, потому что DOM включает разные типы узлов (Document, Element, Text, Comment, DocumentFragment и др.), а не только HTML-элементы.