Рассмотрев положения стандарта W3C DOM, можно приступать к использова_ нию DOM API. В этом и следующих разделах демонстрируется, как можно орга_ низовать обход дерева элементов документа, а также изменять содержимое до_ кумента и добавлять новое содержимое.
Как уже отмечалось, DOM представляет HTML_документ в виде дерева объектов Node. Для любой древовидной структуры наиболее частое выполняемое действие – это обход дерева с поочередным просмотром каждого узла. Один из способов по_ казан в примере 15.2. Это JavaScript_функция, рекурсивно просматривающая узел и все дочерние узлы и подсчитывающая количество HTML_тегов (т. е. узлов Element), встреченных в процессе обхода. Обратите внимание на свойство childNo_ des текущего узла. Значением этого свойства является объект NodeList, ведущий себя (в JavaScript) как массив объектов Node. Поэтому функция может перечис_ лить все дочерние узлы данного узла путем циклического перебора элементов массива childNodes[]. Функция рекурсивно перечисляет не только все дочерние узлы данного узла, но и все узлы в дереве узлов. Обратите внимание, что эта функция демонстрирует также применение свойства nodeType для определения типа каждого узла.
Пример 15.2. Обход узлов документа
<head>
<script>
// Этой функции передается DOM_объект Node. Функция проверяет, представляет
// ли этот узел HTML_тег, т. е. является ли узел объектом Element. Она рекурсивно
// вызывает сама себя для каждого дочернего узла, проверяя их таким же образом.
// Функция возвращает общее число найденных ею объектов Element. Если вы вызываете
// эту функцию, передавая ей DOM_объект, она выполнит обход всего DOM_дерева.
function countTags(n) {
// n – это Node
var numtags = 0;
// Инициализируем счетчик тегов
if (n.nodeType
== 1 /*Node.ELEMENT_NODE*/) // Проверяем, является ли n
// объектом Element
numtags++;
// Если это так, увеличиваем счетчик
var children = n.childNodes;
// Теперь получаем все дочерние элементы n
for(var i=0; i
< children.length; i++) {
// Цикл по всем дочерним элементам
numtags +=
countTags(children[i]);
// Рекурсия по всем дочерним элементам
}
return numtags;
// Возвращаем общее число тегов
}
</script>
</head>
<!__ Это пример использования функции countTags() __>
<body onload="alert('Количество тегов в документе: ' + countTags(document))"> Это <i>пример</i> документа.
</body>
15.6. Поиск элементов в документе
Обратите внимание: определенная в примере 15.2 функция countTags() вызывает_ ся из обработчика события onload, поэтому она вызвана не будет, пока документ не загрузится целиком. Это обязательное требование при работе с DOM: нельзя обходить дерево документа или манипулировать им до тех пор, пока документ полностью не загружен. (В разделе 13.5.7 детально обсуждаются причины этого ограничения. Дополнительно в примере 17.7 приводится функция, которая по_ зволяет регистрировать обработчики события onload нескольких модулей.)
В дополнение к свойству childNodes интерфейс Node определяет несколько других полезных свойств. Свойства firstChild и lastChild ссылаются на первый и послед_ ний дочерние узлы, а свойства nextSibling и previousSibling – на ближайшие смежные узлы. (Два узла называются смежными, если имеют один и тот же роди_ тельский узел.) Эти свойства предоставляют еще один способ обхода дочерних уз_ лов, который демонстрируется в примере 15.3. В этом примере приводится опре_ деление функции getText(), которая отыскивает все узлы Text, вложенные в ука_ занный узел. Она извлекает и объединяет текстовое содержимое узлов и возвра_ щает полученный результат в виде JavaScript_строки. Потребность в такой функции при программировании с использованием DOM_интерфейсов возника_ ет на удивление часто.
Пример 15.3. Получение текстового содержимого из всех вложенных DOM'узлов
/**
* getText(n): Отыскивает все узлы Text, вложенные в узел n.
* Объединяет х содержимое и возвращает результат в виде строки. */
function getText(n) {
// Операция объединения строк очень ресурсоемка, потому сначала
// содержимое текстовых узлов помещается в массив, затем выполняется
// операция конкатенации элементов массива в одну строку.
var strings = []; getStrings(n, strings); return strings.join("");
// Эта рекурсивная функция отыскивает все текстовые узлы
// и добавляет их содержимое в конец массива.
function getStrings(n, strings) {
if (n.nodeType == 3 /* Node.TEXT_NODE */) strings.push(n.data);
else if (n.nodeType == 1 /* Node.ELEMENT_NODE */) {
// Обратите внимание, обход выполняется
// с использованием firstChild/nextSibling
for(var m = n.firstChild; m != null; m = m.nextSibling) { getStrings(m, strings);