Получается неудобно. Хотя стандарты – и весьма полезная штука, в нашем случае преимущество независимости от языка не такое уж и полезное. Лучше иметь интерфейс, хорошо приспособленный к языку, который вы используете, чем интерфейс, который будет знаком при использовании разных языков.
Чтобы показать неудобную интеграцию с языком, рассмотрим свойство >childNodes
, которое есть у узлов DOM. В нём содержится объект, похожий на массив, со свойством >length
, и пронумерованные свойства для доступа к дочерним узлам. Но это – экземпляр типа >NodeList
, не настоящий массив, поэтому у него нет методов вроде >forEach
.
Есть также проблемы, связанные с плохой продуманностью системы. К примеру, нельзя создать новый узел и сразу добавить к нему свойства или дочерние узлы. Сначала нужно его создать, затем добавить дочерние по одному, и в конце назначить свойства по одному, с использованием побочных эффектов. Код, плотно работающий с DOM, получается длинным, некрасивым и со множеством повторов.
Но эти проблемы не фатальные. JavaScript позволяет создавать абстракции. Легко написать вспомогательные функции, позволяющие выражать операции более понятно и коротко. Вообще, такого рода инструменты предоставляют много библиотек, направленных на программирование для браузера.
Узлы DOM содержат много ссылок на соседние. Это показано на диаграмме:
Хотя тут показано только по одной ссылке каждого типа, у каждого узла есть свойство >parentNode
, указывающего на его родительский узел. Также у каждого узла-элемента (тип 1) есть свойство >childNodes
, указывающее на массивоподобный объект, содержащий его дочерние узлы.
В теории можно пройти в любую часть дерева, используя только эти ссылки. Но JavaScript предоставляет нам много дополнительных вспомогательных ссылок. Свойства >firstChild
и >lastChild
показывают на первый и последний дочерний элементы, или содержат >null
у тех узлов, у которых нет дочерних. >previousSibling
и >nextSibling
указывают на соседние узлы – узлы того же родителя, что и текущего узла, но находящиеся в списке сразу до или после текущей. У первого узла свойство >previousSibling
будет >null
, а у последнего >nextSibling
будет >null
.
При работе с такими вложенными структурами пригождаются рекурсивные функции. Следующая ищет в документе текстовые узлы, содержащие заданную строку, и возвращает >true
, когда находит:
>function talksAbout(node, string) {
> if (node.nodeType == document.ELEMENT_NODE) {
> for (var i = 0; i < node.childNodes.length; i++) {
> if (talksAbout(node.childNodes[i], string))