Какие объекты считаются узлами DOM

Что является узлами в DOM?

Теория: что такое узел DOM

DOM представляет HTML-документ как дерево объектов, где базовой единицей является узел (node). Узел — это объект DOM, который участвует в дереве документа и имеет общий набор свойств/методов базового интерфейса Node.

Интерфейс Node является базовой абстракцией: “просто Node” как отдельный самостоятельный тип обычно не создается, но многие конкретные типы DOM-объектов реализуют его и поэтому считаются узлами. В веб-разработке чаще всего встречаются узлы документа (Document), элементы (Element), текст (Text), комментарии (Comment), а также фрагменты (DocumentFragment).

DOM — это объектное представление документа в памяти (дерево узлов), а не исходная строка HTML.

Ключевые типы узлов и nodeType

Для различения типов узлов используется числовой идентификатор nodeType. Для удобства чтения кода применяются константы вроде Node.ELEMENT_NODE, Node.TEXT_NODE и т. п.

Таблица наиболее важных типов nodeType (чаще всего применяются на практике):

Тип узлаКонстантаnodeTypeЧто представляет
DocumentNode.DOCUMENT_NODE9документ целиком (document)
ElementNode.ELEMENT_NODE1HTML-элемент (<div>, <span>)
TextNode.TEXT_NODE3текст внутри элементов
CommentNode.COMMENT_NODE8комментарий <!-- ... -->
DocumentTypeNode.DOCUMENT_TYPE_NODE10<!doctype html>
DocumentFragmentNode.DOCUMENT_FRAGMENT_NODE11временный контейнер поддерева
Attr (исторически важно знать)Node.ATTRIBUTE_NODE2узел атрибута (в современной практике обычно обходится без него)
Существуют типы узлов, которые в современных браузерных DOM-деревьях почти не встречаются или считаются устаревшими в практическом смысле (например, некоторые “служебные” XML-типы), поэтому для начинающего обычно достаточно типов из таблицы выше.

Практика: как “увидеть” узлы

Проверка типа узла выполняется через 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-элементы.