Прежде чем мы начнем рассматривать дерево и перемещаться с ветки на ветку, давайте рассмотрим подробнее, что именно мы будем с ним делать.
Каждый узел дерева DOM является объектом, представляющим один элемент на странице. Узлы знают об отношениях с другими узлами в своей непосредственной близости, и содержат достаточно много информации о себе. Поэтому можно легко получить всю необходимую информацию из узла, чтобы получить доступ к его предку или к потомкам.
Как и можно было ожидать, учитывая объектную природу JavaScript, искомая информация в данном случае находится в свойствах узла. В частности в свойствах parentNode и childNodes. Так как каждый элемент на странице имеет максимум одного предка, то свойство parentNode является простым: оно просто предоставляет доступ к предку узла. Однако узлы могут иметь любое число потомков, поэтому свойство childNodes является в действительности массивом. Каждый элемент массива указывает на одного потомка, в том же порядке, в котором они появляются в документе. Элемент body документа нашего примера будет, поэтому, иметь массив childNodes, содержащий элементы h1, первый p, затем второй p, в данном порядке.
Эти, конечно, не единственные интересные свойства узла. Но это хорошее начало. Какой же код необходимо использовать, чтобы, прежде всего, получить доступ к одному из этих узлов? Где следует начать исследование дерева?
Лучшим местом для старта является корень документа, доступный через объект document. Так как document находится точно в корне, он не имеет свойства parentNode, но имеет единственного потомка: узел элемента html, который доступен через массив childNodes:
var theHtmlNode = document.childNodes[0];
Эта строка кода создает новую переменную с именем theHtmlNode, и присваивает ей значение первого потомка объекта document (вспомните, что массивы JavaScript начинают нумерацию с 0, а не с 1). Можно убедиться, что мы получили доступ к узлу html, проверяя свойство nodeName переменной theHtmlNode, которое предоставляет жизненно важную информацию о точном виде узла, с которым мы имеем дело:
alert( "theHtmlNode is a " + theHtmlNode.nodeName + " node!" );
Этот код выводит окно сообщения, которое содержит "theHtmlNode is a HTML node!" ("theHtmlNode является узлом HTML!"). Отлично! Свойство nodeName предоставляет доступ к типу узла. Для узлов элементов, это свойство содержит имя тега в верхнем регистре: в данном случае это "HTML"; для ссылки это будет "A", для параграфа "P", и т.д. Свойство nodeName текстового узла будет "#text", а nodeName объекта document будет "#document".
Мы знаем также, что theHtmlNode должен содержать ссылку на своего предка. Можно убедиться, что это работает ожидаемым образом, с помощью следующего теста:
if ( theHtmlNode.parentNode == document ) {
alert( "Hooray! The HTML node's parent is the document object!" );
}
Это работает, как мы и ожидали. Используя эту информацию, давайте напишем некоторый код для получения ссылки на первый параграф тела примера документа. Это будет второй потомок элемента body, который является вторым потомком элемента html, который является первым потомком объекта document. Вот так!
var theHtmlNode = document.childNodes[0];
var theBodyNode = theHtmlNode.childNodes[1];
var theParagraphNode = theBodyNode.childNodes[1];
alert( "theParagraphNode is a " + theParagraphNode.nodeName + " node!" );
Отлично. Мы получили то, что хотели. Но в действительности это достаточно многословно, и существует значительно лучший способ для записи этого кода. В лекции об объектах мы узнали, что ссылки на объекты можно соединять цепочкой, то же самое можно сделать здесь, пропуская промежуточные переменные и записывая следующий код:
var theParagraphNode = document.childNodes[0].childNodes[1].childNodes[1];
alert( "theParagraphNode is a " + theParagraphNode.nodeName + " node!" );
Этот код значительно короче.
Первый потомок узла всегда будет node.childNodes[0], а последний потомок узла всегда будет node.childNodes[node.childNodes.length - 1]. Эти элементы используются достаточно часто, но записывать их снова и снова достаточно громоздко. В связи c этим в DOM имеются для них явные сокращенные записи: .firstChild и .lastChild, соответственно. Так как узел html является первым потомком объекта document, а узел body является последним потомком узла html, можно переписать код еще более коротко следующим образом:
var theParagraphNode = document.firstChild.lastChild.childNodes[1];
alert( "theParagraphNode is a " + theParagraphNode.nodeName + " node!" );
Эти методы перемещения между узлами на близком расстоянии будут полезны, и позволяют оказаться в любом месте документа, но они несколько громоздки. Даже в этом крошечном примере документа можно видеть, насколько трудоемко может быть перемещение из корневого узла в глубину разметки. Должен быть лучший способ для таких задач!