Узлы и элементы 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 , с помощью которого мы можем выполнить аналогичные действия:
- , а перед ним имеется вот такой контент. Он при парсинге страницы будет преобразован браузером в текстовый узел DOM. Таким образом, первым дочерним узлом
- . \n образовался из-за того что мы поставили Enter , а четыре пробела – это то количество пробелов, которые мы установили перед тем как написали тег
- .
- будет именно этот тестовый узел, и только потом уже
В DOM пробелы, переводы строк, знаки табуляции и другие символы расположенные между элементами образуют текстовые DOM-узлы.
- , его разметка должна быть записана следующим образом:
При выборе DOM-элемента на вкладке Styles будет отображаться весь CSS, применённый к этому элементу, в том числе будет отображены и дефолтные стили браузера. Правила можно редактировать, отключать с помощью чекбоксов и дописывать новые. Все изменения применяются сразу.
На вкладке Computed мы можем посмотреть результирующие стили, примененные к элементу.
На вкладке Event Listeners отображаются все обработчики событий, привязанные к данному DOM-элементу.
Классы DOM-узлов
Узлы в DOM являются объектами или другими словами экземплярами определенных классов.
Например, DOM-элемент является экземпляром класса 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 имеют свои определённые свойства и методы, которых нет у .
В DOM разные элементы могут являются экземплярами разных классов. Но все они в качестве прототипа имеют объект HTMLElement.prototype , то есть значение свойства prototype класса HTMLElement :
document.querySelector('body').__proto__ === HTMLBodyElement.prototype; // true // при наличии элемента на странице document.querySelector('a').__proto__ === HTMLAnchorElement.prototype; // true // при наличии элемента на странице 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 // при наличии элемента на странице 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-узлов приведен на следующей схеме:
Источник