Узлы и элементы DOM-дерева в JavaScript

В этой статье мы разберём типы узлов в DOM, а также чем они являются. Познакомимся с некоторыми свойствами и методами узлов, и изучим иерархию их классов. Кроме этого познакомимся с основами исследования DOM в браузере Chrome.
Типы и имена DOM-узлов
Как мы уже знаем структурой DOM является дерево. Оно состоит из связанных друг с другом узлов. Узлы бывают разных типов, в зависимости от того, чему соответствует этот узел в HTML. То есть при преобразовании HTML-текста в DOM, разные сущности в нём преобразуются в разные типы узлов.
Основную структуру DOM-дерева составляют именно узлы, образованные HTML-тегами. Их называют узлами-элементами или просто элементами.
Узнать тип узла в DOM можно с помощью свойства nodeType
:
console.log(document.nodeType); // 9
console.log(document.body.nodeType); // 1
Это свойство возвращает число от 1 до 12, обозначающее тип узла.
Основные значения:
1
– элемент (Node.ELEMENT_NODE
);2
– атрибут (Node.ATTRIBUTE_NODE
);3
– текстовый узел (Node.TEXT_NODE
);8
– комментарий (Node.COMMENT_NODE
);9
– document (Node.DOCUMENT_NODE
);10
– узел, содержащий тип документа (Node.DOCUMENT_TYPE_NODE
);11
– узел, представляющий фрагмент документаDocumentFragment
(Node.DOCUMENT_FRAGMENT_NODE
).
В скобках приведены константы класса Node. Они обычно используются в коде, когда нужно проверить тип DOM-узла в JavaScript. Их намного удобнее использовать, чем запоминать числовые коды:
console.log(document.body.nodeType === Node.DOCUMENT_NODE); // false
console.log(document.body.nodeType === Node.ELEMENT_NODE); // true
Например, получим doctype
документа и узнаем его числовой код:
const doctype = document.doctype;
// это ещё один способ как можно получить doctype документа
const doctypeSame = document.childNodes[0];
// получим числовой код узла
console.log(doctype.nodeType); // 10
console.log(doctypeSame.nodeType); // 10
Теперь изучим свойство nodeName
. С его помощью мы можем узнать имя узла или тег, если узел является элементом:
console.log(document.body.nodeName); // "BODY"
console.log(document.doctype.nodeName) // "html"
console.log(document.nodeName); // #document"
Свойство nodeName
для других узлов, не являющимися элементами возвращает различные значения:
- для текстовых узлов –
"#text"
; - для узлов-комментариев –
"#comment"
; - для
document
–"#document"
и так далее.
Получить имя тега элемента можно не только с помощью nodeName
,
но также посредством свойства tagName
.
tagName
запрограммирован в браузере как геттер, он содержится в prototype
класса Element
. nodeName
– это тоже геттер,
но находится он в другом месте, в prototype
класса Node
.
Поэтому свойство tagName
доступно только для узлов-элементов, и не доступно для других типов узлов.
Прочитать про объекты, прототипы и наследование можно в этой статье.
Исследование DOM
В браузерах при разработке веб-приложений и сайтов имеется очень полезный инструмент DevTools.
Открыть в браузере Chrome его можно через меню или посредством комбинации клавиш:
- macOS – Cmd + Shift + I;
- Windows – Ctrl + Shift + I или F12;
- Linux – Ctrl + Shift + I.
На вкладке Element вы можете исследовать DOM и CSS. При необходимости их можно изменять прямо здесь, и смотреть как будут выглядеть эти правки прямо на веб-странице.
Выбрать нужный элемент на веб-странице можно разными способами:
- кликнуть по нему правой кнопкой мыши и выбрать в открывшемся меню пункт «Inspect» или «Посмотреть код»;
- найти его в DOM, для поиска элемента дополнительно можно использовать окно поиска, которое можно вызвать с помощью комбинации клавиш Ctrl + F;
- нажать на значок и визуально выбрать нужный элемент.
После выбора узла мы можем обратиться к нему в консоли через $0
. При этом предыдущий выбранный узел будет доступен как $1
и так далее. Это можно использовать при изучении DOM и отладке сайта.
Например, выберем комментарий:
// тип узла
$0.nodeType // 8
// имя узла
$0.nodeName // "#comment"
// значение узла
$0.nodeValue // " Заголовок H1 "

Свойство nodeValue
позволяет получить содержимое текстового узла или комментария. Для остальных узлов оно возвращает в качестве значения null
.
С помощью nodeValue
e мы можем также установить новое значение этому узлу:

Кроме nodeValue
нам также доступно свойство data
, с помощью которого мы можем выполнить аналогичные действия:

Получить и изменить содержимое элементов осуществляется с помощью других свойств, таких как textContent
и innerHTML
. Например, выведем значения которые возвращают эти свойства для элемента <ul>
:

Здесь мы видим \n
и пробелы. \n
– это перевод строки. Так как по факту, например, первый <li>
расположен не сразу после <ul>
, а перед ним имеется вот такой контент. Он при парсинге страницы будет преобразован браузером в текстовый узел DOM. Таким образом, первым дочерним узлом <ul>
будет именно этот тестовый узел, и только потом уже <li>
. \n
образовался из-за того что мы поставили Enter, а четыре пробела – это то количество пробелов, которые мы установили перед тем как написали тег <li>
.

В DOM пробелы, переводы строк, знаки табуляции и другие символы расположенные между элементами образуют текстовые DOM-узлы.
Например, чтобы их не было в <ul>
, его разметка должна быть записана следующим образом:
<ul><li>Android</li><li>iOS</li></ul>
При выборе DOM-элемента на вкладке Styles будет отображаться весь CSS, применённый к этому элементу, в том числе будет отображены и дефолтные стили браузера. Правила можно редактировать, отключать с помощью чекбоксов и дописывать новые. Все изменения применяются сразу.

На вкладке Computed мы можем посмотреть результирующие стили, примененные к элементу.

На вкладке Event Listeners отображаются все обработчики событий, привязанные к данному DOM-элементу.

Классы DOM-узлов
Узлы в DOM являются объектами или другими словами экземплярами определенных классов.
Например, DOM-элемент <body>
является экземпляром класса HTMLBodyElement
. В этом можно убедиться следующим образом:
document.body.constructor.name // HTMLBodyElement
// или так
document.body.toString() // [object HTMLBodyElement]
// или так
document.body instanceof HTMLBodyElement // true
Таким образом, DOM-узлы в JavaScript являются обычными объектами. С некоторыми свойствами и методами этих объектов мы уже познакомились выше.
// например, установим свойству id значение wrapper
document.body.id = 'wrapper';
// получим тег элемента
document.body.tagName // "BODY"
Например, если выбрать на странице ссылку и получить её класс, то мы увидим, что она является
экземпляром HTMLAnchorElement
, а не HTMLBodyElement
.
$0.constructor.name // HTMLAnchorElement
Экземпляры HTMLAnchorElement
в отличие от HTMLBodyElement
имеют свои определённые свойства и методы, которых нет у <body>
.
В DOM разные элементы могут являются экземплярами разных классов. Но все они в качестве прототипа имеют объект HTMLElement.prototype
, то есть значение свойства prototype
класса HTMLElement
:

document.querySelector('body').__proto__ === HTMLBodyElement.prototype; // true
// при наличии элемента <a> на странице
document.querySelector('a').__proto__ === HTMLAnchorElement.prototype; // true
// при наличии элемента <div> на странице
document.querySelector('div').__proto__ === HTMLDivElement.prototype; // true
Таким образом, HTMLElement
– это базовый класс, от которого наследуется другие классы, такие как HTMLBodyElement
, HTMLAnchorElement
, HTMLDivElement
и другие. Они в отличие от HTMLElement
используются для создания конкретных HTML-элементов.
Но если пойти дальше и рассмотреть класс HTMLElement
, то он наследуется от Element
.
Кстати, класс Element
является основой не только для HTMLElement
, но и других классов, например, предназначенных для XML и SVG:

document.querySelector('body').__proto__.__proto__.__proto__ === Element.prototype; // true
// при наличии элемента <svg> на странице
document.querySelector('svg').__proto__.__proto__.__proto__.__proto__ === Element.prototype; // true
Ещё выше находится класс Node
. Он содержит общие свойства и методы, характерные для всех DOM-узлов.
При этом класс Node
не применяется непосредственно для создания объектов. Он применяется для организации наследования. От него наследуется Element
и CharacterData
. От CharacterData
в свою очередь наследуются классы Text
и Comment
, которую используются соответственно для создания текстовых узлов и комментариев.

Если пойти ещё выше, то увидим в цепочке прототипов объект EventTarget.prototype
. Класс EventTarget
– это корневой класс, благодаря которому все DOM-узлы поддерживают обработку событий. В EventTarget.prototype
содержатся такие методы, как, например, addEventListener
, dispatchEvent
и другие.
После EventTarget
идёт уже Object
. Object
– это класс, который является потомком для всех объектов, которые имеются в JavaScript.
Таким образом, каждый узел в DOM является экземпляром того или иного класса. Набор свойств и методов, который имеет тот или иной узел в DOM определяется не только его классом, но и результатом наследования. Пример наследования классов DOM-узлов приведен на следующей схеме:

Следующая тема: Перемещение по элементам и узлам DOM-дерева.
prntscr.com/qbxhu1
Специально создал простую страничку для лучшего понимания DOM, но есть моменты с непредсказуемым результатом. Созданы 2 div-a и по сути div с индексом (0) и div с индексом (1). Внутри каждого 2 элемента с индексами 0 и 1. Почему в первом случае выводится только 0-ой «ребенок» div-а, а во втором случае и 0-ой и 1-вый «ребенок».
Спасибо
В примере забыл теги div закрыть:
Да и не совсем понял суть задания (Измените код HTML документа, представлено на этом уроке таким образом, чтобы в дереве было как можно меньше ненужных текстовых узлов.)
В этом случае можете использовать этот пример в песочнице.
В этом примере написан код JavaScript, который перебирает DOM узлы документа и выводит их на экран в виде схемы с учетом их вложенности. Все текстовые узлы документа в ней выводятся как #text.
Ваша задача изменить HTML код так, чтобы при выводе не было ненужных текстовых узлов.
(код VBA Excel)
2 способа загнать в переменную слитый текст из 2-х переменных и прбела.
sName = a.getElementsByClassName(«text»)(3).FirsChild.textContent _
& " " & a.getElementsByClassName(«text»)(3).LastChild.textContent
sName = a.getElementsByClassName(«text»)(3).Children(0).textContent _
& " " & a.getElementsByClassName(«text»)(3).Children(1).textContent
Кроме FirsChild и LastChild есть еще какие-нибудь Child? Где почитать?
Спасибо
Почитать про методы для поиска узлов можно в этой статье, а про методы, основанные на связях — здесь.