Курс по JavaScript от Хекслет

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

Александр Мальцев
Александр Мальцев
20K
6
Содержание:
  1. Типы и имена DOM-узлов
  2. Исследование DOM
  3. Классы DOM-узлов
  4. Комментарии

Типы и имена DOM-узлов

Как мы уже знаем структурой DOM является дерево. Оно состоит из связанных друг с другом из узлов. Узлы бывают разных типов, в зависимости от того, чему соответствует этот узел в HTML. То есть при преобразовании HTML-текста в DOM, разные сущности в нём преобразуются в разные типы узлов.

Основную структуру DOM-дерева составляют именно узлы, образованные HTML-тегами. Их называют узлами-элементами или просто элементами.

Узнать тип узла в DOM можно с помощью свойства nodeType:

JavaScript
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).

В скобках приведены константы, их, например можно использовать вместо чисел в выражениях, для того чтобы проверить тип узла:

JavaScript
console.log(document.body.nodeType === Node.DOCUMENT_NODE); // false
console.log(document.body.nodeType === Node.ELEMENT_NODE); // true

Например, получим узел, содержащий тип документа и узнаем его тип:

JavaScript
const doctype = document.doctype;
// ещё один способ получить doctype
const doctypeSame = document.childNodes[0];
console.log(doctypeSame.nodeType); // 10

Теперь изучим свойство nodeName. С его помощью мы можем узнать имя узла или тег, если узел является элементом:

JavaScript
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 можно через меню или посредством комбинации клавиш:

  • macOSCmd + Shift + I;
  • WindowsCtrl + Shift + I или F12;
  • LinuxCtrl + Shift + I.

На вкладке Element вы можете исследовать DOM и CSS. При необходимости их можно изменять прямо здесь, и смотреть как будут выглядеть эти правки прямо на веб-странице.

Выбрать нужный элемент на веб-странице можно разными способами:

  • кликнуть по нему правой кнопкой мыши и выбрать в открывшемся меню пункт «Inspect» или «Посмотреть код»;
  • найти его в DOM, для поиска элемента дополнительно можно использовать окно поиска, которое можно вызвать с помощью комбинации клавиш Ctrl + F;
  • нажать на значок и визуально выбрать нужный элемент.

После выбора узла мы можем обратиться к нему в консоли через $0. При этом предыдущий выбранный узел будет доступен как $1 и так далее. Это можно использовать при изучении DOM и отладке сайта.

Например, выберем комментарий:

JavaScript
// тип узла
$0.nodeType // 8
// имя узла
$0.nodeName // "#comment"
// значение узла
$0.nodeValue // " Заголовок H1 "
Узнаем у выбранного узла DOM его тип, имя и значение

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

С помощью nodeValuee мы можем также установить новое значение этому узлу:

Изменим значение комментария в DOM с помощью свойства nodeValue

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

Получим и изменим значение текстового узла в DOM с помощью свойства data

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

Получим текстовое и HTML содержимое элемента в DOM соответственно с помощью методов textContent и innerHTML

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

Текстовый узел, который был образован в DOM из символов переноса строки и пробелов, расположенных перед li

В DOM пробелы, переводы строк, знаки табуляции и другие символы расположенные между элементами образуют текстовые DOM-узлы.

Например, чтобы их не было в <ul>, его разметка должна быть записана следующим образом:

HTML
<ul><li>Android</li><li>iOS</li></ul>

При выборе DOM-элемента на вкладке Styles будет отображаться весь CSS, применённый к этому элементу, в том числе будет отображены и дефолтные стили браузера. Правила можно редактировать, отключать с помощью чекбоксов и дописывать новые. Все изменения применяются сразу.

Весь CSS, применённый к DOM-элементу, можно увидеть на вкладке Styles в инструментах разработчика

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

На вкладке Computed в инструментах разработчика можно посмотреть итоговые стили, примененные в выбранному элементу

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

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

Классы DOM-узлов

Узлы в DOM являются объектами или другими словами экземплярами определенных классов.

Например, DOM-элемент <body> является экземпляром класса HTMLBodyElement. Это можно очень легко проверить:

JavaScript
document.body.constructor.name // HTMLBodyElement

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

JavaScript
// например, установим свойству id значение wrapper
document.body.id = 'wrapper';
// получим тег элемента
document.body.tagName // "BODY"

Таким образом, DOM-узлы являются обычными JavaScript объектами.

Например, если выделить ссылку на странице и получить имя её конструктора, то увидим, что она является экземпляром другого класса, не HTMLBodyElement.

JavaScript
$0.constructor.name // HTMLAnchorElement

Этот объект в отличие от HTMLBodyElement может иметь свои определённые свойства и методы, которых нет у <body>.

Таким образом в DOM разные элементы могут являться экземплярами разных классов. Но все они в качестве прототипа имеют объект HTMLElement.prototype, то есть значение свойства prototype класса HTMLElement.

HTMLElement – это базовый класс, от которого наследуется другие классы, такие как HTMLBodyElement, HTMLAnchorElement и другие. Они в отличие от HTMLElement предназначены уже для конкретных HTML-элементов.

Следующим объектом в цепочке прототипов является Element.prototype. Класс Element – это базовый класс для всех DOM-элементов. То есть он является основой не только для класса HTMLElement, но и других классов, например, предназначенных для XML и SVG:

Дальше в цепочке идёт прототип Node.prototype. Он содержит свойства и методы, доступные для всех DOM-узлов, если они, конечно, не переопределены в наследуемых классах. Класс Node является базой для всех DOM-узлов.

Кстати для текстовых узлов и комментариев классами являются Text и Comment. Они наследуются от CharacterData, а он в свою очередь от Node.

Следующим прототипом в цепочке является EventTarget.prototype. Класс EventTarget – это корневой класс, благодаря которому все DOM-узлы поддерживают обработку событий. То есть в EventTarget.prototype содержатся такие методы, как addEventListener, dispatchEvent и так далее.

И заканчивается прототипная цепочка объектом Object.prototype. Object – это класс, который является потомком для всех объектов, которые имеются в JavaScript.

Следующая тема: Перемещение по элементам и узлам DOM-дерева.

Комментарии: 6

  1. Александр
    Александр
    2019-12-17 12:16:16
    Добрый день.

    prntscr.com/qbxhu1
    Специально создал простую страничку для лучшего понимания DOM, но есть моменты с непредсказуемым результатом. Созданы 2 div-a и по сути div с индексом (0) и div с индексом (1). Внутри каждого 2 элемента с индексами 0 и 1. Почему в первом случае выводится только 0-ой «ребенок» div-а, а во втором случае и 0-ой и 1-вый «ребенок».
    Спасибо
  1. Александр Мальцев
    Александр Мальцев
    2019-12-18 14:23:44
    Привет!
    В примере забыл теги div закрыть:
    <div>
      <p class="Hi_01">Привет_1</p>
      <p class="GoodBy_01">Пока_1</p>
    </div>
    <div>
      <p class="Hi_02">Привет_2</p>
      <p class="GoodBy_02">Пока_2</p>
    </div>
    
  • Александр
    Александр
    2019-12-15 00:21:49
    DOM Inspector сейчас вроде отсутствует в Хроме в таком виде как на картинке!?
    Да и не совсем понял суть задания (Измените код HTML документа, представлено на этом уроке таким образом, чтобы в дереве было как можно меньше ненужных текстовых узлов.)
    1. Александр Мальцев
      Александр Мальцев
      2019-12-16 15:04:43
      Да, сейчас уже нет такого дополнения.
      В этом случае можете использовать этот пример в песочнице.
      В этом примере написан код JavaScript, который перебирает DOM узлы документа и выводит их на экран в виде схемы с учетом их вложенности. Все текстовые узлы документа в ней выводятся как #text.
      Ваша задача изменить HTML код так, чтобы при выводе не было ненужных текстовых узлов.
    2. Александр
      Александр
      2019-12-16 18:36:13
      Я тут экспериментирую с DOM и столкнулся с проблемкой. Не могу вывести список одинаковых элементов prntscr.com/qblhnk там есть 2 span элемента (firstChildren and lastChildren). Как их вытянуть и посчитать их количество?

      (код 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? Где почитать?

      Спасибо
    3. Александр Мальцев
      Александр Мальцев
      2019-12-18 14:32:30
      Если в VBA поддерживается метод querySelectorAll, то используйте его:
      a.querySelectorAll('div.text > span + span.second').length
      
      Почитать про методы для поиска узлов можно в этой статье, а про методы, основанные на связях — здесь.