WebClub - Всероссийский Клуб Веб-разработчиков
WebClub.RU » Архив » Общее представление о Document Object Model

Общее представление о Document Object Model


Дата публикации: 16-03-2013

Структура документа в DOM

DOM представляет документ как иерархию объектов нового типа -- узлов (node). На вершине иерархии находится узел (объект) document , который представляет весь документ. В качестве узлов в DOM представлено все содержание документа: HTML элементы, атрибуты этих элементов и текст HTML элементов-контейнеров. Возьмем для примера небольшой фрагмент HTML документа:

<HTML>   
<HEAD>   
<TITLE>   
Overview of the DOM   
</TITLE>   
</HEAD>   
<BODY>   
<H1>   
Иерархия узлов   
</H1>   
<P>   
На вершине иерархии находится узел   
<TT>   
document   
</TT>   
, который представляет в DOM сам документ.   
</P>   
</BODY>   
</HTML>

Раздел HEAD этого документа содержит элемент TITLE с текстом. Раздел BODY содержит заголовок первого уровня и параграф, в котором одно слово набрано телетайпным шрифтом. На следующей диаграмме показаны все узлы этого документа. Узлы типа элемент изображены в виде тэгов этого элемента и размещены в прямоугольных ячейках. Узлы типа текст (их содержание сокращено для компактности) размещены в ячейках с закругленными углами. Линии соединяют родительский узел с расположенными под ним его детками.
пример дерева документа

Сравнивая эту диаграмму с ее HTML источником, можно легко понять, что иерархия узлов задается вложенностью тэгов друг в друга и текста внутрь элементов. Текст элемента P представлен двумя текстовыми узлами, поскольку он разбит на две части элементом TT . Показанную на рисунке иерархию узлов принято называть деревом документа. Надо отметить, что Internet Explorer 5 не считает корневой узел document частью этого дерева.

Очевидно, что элементы, имеющие открывающий и закрывающий тэги (элементы-контейнеры), могут иметь деток. Текст, атрибуты и элементы типа IMG , не имеющие закрывающего тэга, иметь деток не могут. Очевидно также, что узлы типа атрибут могут быть детками только узлов типа элемент. В HTML разные элементы, например, P или IMG , имеют разный набор допустимых для них атрибутов. В DOM узлы-элементы разного вида также имеют разный набор допустимых для них узлов-атрибутов. За редким исключением они соответствуют HTML 4 . Списки рекомендованных атрибутов для различных элементов можно найти в документации W3C. Впрочем, реальные списки зависят от реализации броузеров.

Узлы разного типа (document , элементы, атрибуты и текст) имеют свой набор свойств и методов, которые позволяют через сценарии манипулировать ими. Ключевые свойства и методы узлов описаны в этой статье. Ряд узлов-элементов (объектов) имеют свои собственные свойства и методы. Так, например, объект элемента TABLE имеет метод createTHead() . Полный набор рекомендованных свойств и методов можно найти в документации W3C. Надо отметить, что Internet Explorer 5 поддерживает гораздо более широкий набор, чем содержится в рекомендации W3C.

При обсуждении свойств и методов узлов мы будем использовать следующий фрагмент документа:

<DIV>   
<UL ID="components">   
<LI>HTML</LI>   
<LI>CSS</LI>   
<LI>javascript</LI>   
</UL>   
</DIV>

В DOM этому фрагменту соответствует ветка дерева, растущая из узла <DIV> к узлу <UL> . Здесь она разветвляется на три веточки по числу узлов <LI> (узлы-атрибуты не принято включать в состав дерева). Каждый из этих узлов имеет по одному побегу, который заканчивается текстовым узлом.
Навигация по дереву документа

Навигацию по дереву документа можно начинать с любого узла-элемента, для которого мы знаем идентификатор, присваиваемый ему в качестве значения атрибута ID . Ссылку на такой узел можно получить с помощью метода getElementById() объекта document . Параметром метода является идентификатор. Следующая строка кода присваивает переменной oList ссылку на наш список:

 var oList =   
 document.getElementById("components   
 ")

Стартуя с некоторого узла, мы можем бродить по дереву в любом направлении, используя ряд свойств узлов. Так, узлы-элементы и текстовые узлы имеют свойство parentNode , которое возвращает ссылку на родительский узел. Возьмем для примера узел (объект) oList . Ссылку на родительский элемент DIV этого узла можно получить следующий образом:

 var oParent = oList   
 .parentNode

Узлы-элементы и текстовые узлы, являющиеся детками некоторого узла, входят в состав коллекции childNodes этого узла. (Узлы-атрибуты составляют отдельную коллекцию attributes , которая обсуждается в последнем разделе статьи.) К каждому из них можно обращаться по индексу массива. Например, строка кода

 var oItem1 = oList   
 .childNodes[1]

присваивает переменной oItem1 ссылку на элемент <LI>CSS</LI> нашего списка. Именно этот элемент представлен в DOM как узел childNodes[1] узла oList . Первый (childNodes[0] ) и последний элементы коллекции имеют специальные имена: firstChild и lastChild . Эти имена являются свойствами родительского узла. Каждый из элементов коллекции имеет свойства previousSibling и nextSibling . Эти свойства хранят ссылку на ближайщих братков элемента -- предыдущий и последующий элементы коллекции (возвращают null , когда братков нет). Так, элемент childNodes[1] является свойством nextSibling элемента childNodes[0] и свойством previousSibling элемента childNodes[2] . Используя эти свойства, мы можем получить ссылку на узел childNodes[1] любым из следующих способов:

 oList   
 .firstChild.nextSiblingoList   
 .childNodes[2].previousSibling

Ссылка на более удаленные узлы как по горизонтали, так и по вертикали дерева формируется путем слияния ссылок на ближайших родственников по стандартным правилам объектно-ориентированного программирования. Так,

 oList.   
 childNodes[1].firstChild

является ссылкой на текст "CSS" элемента <LI>CSS</LI> нашего списка.

На следующей диаграмме приведены имена всех ближайших родственников некоторого узла oNode .
ближайшие родственники узла

Заметим, что все описанные выше свойства узлов (parentNode , firstChild , lastChild , nextSibling и previousSibling ), необходимые для навигации по дереву документа, являются свойствами только для чтения. Помимо них, узлы имеют еще ряд свойств, которые мы сейчас опишем.
Свойства-характеристики узлов

Свойство узла nodeType (только для чтения) возвращает 1, 2 или 3 для узлов, соответствующих тэгу, атрибуту или тексту, соответственно. Свойство nodeName (только для чтения) возвращает имя HTML тэга, которому соответствует данный узел, например, P для параграфа или UL для ненумерованного списка. Для узлов-атрибутов nodeName возвращает название атрибута, а для тестовых узлов возвращает #text .

Текстовые узлы имеют еще одно очень важное свойство: nodeValue . Это свойство для чтения и записи хранит содержание текстового узла. Для элементов оно возвращает null , а для атрибутов -- значение атрибута.
Создание новых узлов

    метод createElement()
    метод createTextNode()

Технику создания новых элементов обсудим на конкретном примере. Мы хотим добавить к нашему списку элемент

<LI>XML</LI>

Этому HTML элементу в DOM соответствуют два узла: узел-элемент <LI> и текстовый узел "XML" . Следовательно, мы должны создать два новых узла. Новые узлы создаются с помощью методов createElement() и createTextNode() объекта document .
Метод createElement()

Метод createElement() принимает в качестве параметра строку с названием тэга элемента и создает новый HTML элемент указанного типа. Например, строка кода

 var oItem =   
 document.createElement("LI"   
 )

создает новый элемент списка <LI> , у которого нет содержания. Метод возвращает ссылку на созданный им объект. Созданный выше элемент <LI> находится в памяти, но не входит в состав текущего документа. Для того, чтобы он стал частью документа, его надо добавить к существующим узлам документа с помощью методов insertBefore() или appendChild() , которые подробно описаны ниже .

Хотя название тэга элемента можно набирать как заглавными, так и прописными буквами, рекомендуется использовать ЗАГЛАВНЫЕ БУКВЫ.

Заметим, что в Internet Explorer 5 можно создавать через сценарий все элементы, кроме FRAME , IFRAME и SELECT . Свойства создаваемых элементов являются свойствами для чтения и записи.
Метод createTextNode()

Метод createTextNode() используется для создания текстового узла. Он принимает в качестве параметра строку текста, которая задает значение свойства nodeValue текстового узла. Например, строка кода

 var oText =   
 document.createTextNode("XML"   
 )

создает новый текстовый узел "XML" . Метод возвращает ссылку на созданный им объект. Созданный текст еще не входит в состав текущего документа. Для того, чтобы он стал частью документа, его надо присоединить к существующим узлам документа с помощью методов appendChild() , replaceNode() или insertBefore() , которые подробно описаны ниже .

Если у вас случайно завалялась ссылка на ненужный текстовый узел (например, oText ), то можно не создавать нового узла, а воспользоваться уже существующим. Для этого надо просто присвоить новый текст в качестве значения свойства nodeValue существующего узла:

 oText   
 .nodeValue= "XML"

Итак, мы создали два новых узла: узел-элемент <LI> и текстовый узел "XML" . Теперь займемся встраиванием этих узлов в документ.
Редактирование дерева документа

    вставка: методы appendChild() и insertBefore()
    копирование: метод cloneNode()
    замещение: методы replaceChild() и replaceNode() *
    удаление: методы removeChild() и removeNode() *
    перестановка: метод swapNode() *
    Дополнительные методы: applyElement() * и hasChildNodes()

Примечание. Методы, помеченные звездочкой * , не входят в список рекомендуемых W3C

DOM, как редактор, позволяет копировать, вставлять, замещать и удалять как отдельные узлы, так и целые ветви дерева документа. Мы начнем с присоединения одного узла к другому.

Напомню, что у нас есть два узла: узел-элемент <LI> и текстовый узел "XML" . Оба узла находятся в памяти и мы хотим встроить их в текущий документ. Прежде всего, надо задать текст "XML" в качестве содержания элемента <LI> . Сделать это можно с помощью метода appendChild() .
Вставка: метод appendChild()

Этот метод добавляет элемент в конец коллекции childNodes узла, который его активизировал. Сам этот узел становится родительским узлом для узла, ссылку на который метод принимает в качестве параметра. Например, строка кода

 oItem   
 .appendChild(oText   
 )

добавляет узел oText к узлу oItem . Отметим, что метод возвращает ссылку на объект, который он добавляет. В нашем случае это объект oItem. firstChild . Теперь мы имеет в памяти элемент (веточку дерева из двух узлов)

<LI>XML</LI>

Пора вставлять эту веточку в текущий документ. Если мы хотим вставить ее в самый конец нашего списка, то надо, как и выше, использовать метод appendChild() :

 oList   
 .appendChild(oItem   
 )

Поскольку узел oList , к которому мы присоединили узел oItem , является частью текущего документа, созданный нами элемент списка также становится частью документа. Теперь наш список выглядит так:

<UL ID="components">   
<LI>HTML</LI>   
<LI>CSS</LI>   
<LI>javascript</LI>   
<LI>XML</LI>   
</UL>

Обсудим теперь как можно вставить созданный нами элемент списка не в конец, а, скажем, после элемента списка <LI>HTML</LI> . Сделать это можно с помощью метода insertBefore() .
Вставка: метод insertBefore()

В отличие от метода appendChild() , метод insertBefore() позволяет указать, в какое место коллекции childNodes будущего родительского узла будет вставлен новый узел. Как следует из названия, метод требует ссылки на узел, перед которым будет вставлен его новый браток. Мы создадим эту ссылку в отдельной строке, хотя это и необязательно. Итак, код

 var oBrother = oList   
 .firstChild.nextSiblingoList   
 .insertBefore(oItem, oBrother   
 )

добавляет в коллекцию childNodes узла oList узел oItem сразу после узла childNodes[0] . В качестве первого параметра метод insertBefore() принимает ссылку на узел, который мы хотим добавит, а в качестве второго параметра -- ссылку на узел, перед которым будет вставлен новый браток. Второй параметр метода является необязательным. Если родительский узел не имеет деток, то задавать его не следует. Если родительский узел имеет деток, а второй параметр не задан, то добавляемый узел становится самым последним среди деток родительского объекта. Метод insertBefore() возвращает ссылку на вставленный в документ объект.

Теперь наш первоначальный список выглядит так:

<UL ID="components">   
<LI>HTML</LI>   
<LI>XML</LI>   
<LI>CSS</LI>   
<LI>javascript</LI>   
</UL>

Продолжим обсуждение методов редактирования дерева документа.
Копирование: метод cloneNode()

Если мы хотим скопировать некоторый узел вместе со всеми его атрибутами, то надо воспользоваться методом cloneNode() . В качестве параметра метод принимает выражение типа Boolean. Если оно равно false , то копируется только тот узел, который активизирует метод. Если параметр метода равен true , то копируется узел вместе со всеми его потомками. Например, строка кода

 var oClone = oList   
 .cloneNode(true)

копирует в память всю ветвь дерева, начинающуюся на узле oList , то есть весь наш список целиком. Метод возвращает ссылку на копию узла. Используя эту ссылку, мы можем в дальнейшем работать с этой копией, например, отредактировать ее и вставить в документ.
Замещение: методы replaceChild() и replaceNode()

Метод replaceChild() позволяет у узла, который его активизирует, заменить одного из его деток на нового. Ссылку на новый и на заменяемый узлы метод принимает в качестве первого и второго параметров, соответственно. Так, следующий фрагмент сценария

 var oItem =   
 document.createElement("LI"   
 )oItem   
 .appendChild(document.createTextNode("JScript"   
 ))oList   
 .replaceChild(oItem, oList.lastChild   
 )

создает сначала элемент списка с текстом "JScript", а затем заменяет им последний элемент нашего списка. Отметим, что метод возвращает ссылку на вставленный в документ узел.

Теперь наш список выглядит так:

<UL ID="components">   
<LI>HTML</LI>   
<LI>CSS</LI>   
<LI>JScript</LI>   
</UL>

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

 oList   
 .lastChild.firstChild.nodeValue= "JScript"

Если мы хотим заменить некоторый узел в документе другим узлом, то надо воспользоваться методом replaceNode() . Подчеркнем, что этот метод не входит в список рекомендуемых W3C, но поддерживается Internet Explorer 5. Метод replaceNode() удаляет из документа узел, который его активизирует, и вставляет в документ вместо него новый узел, ссылку на который от принимает в качестве параметра. Заметим, что узел удаляется вместе со всеми своими атрибутами и потомками. Так, следующий фрагмент сценария

 var oParagraph =   
 document.createElement("P"   
 )var oText =   
 document.createTextNode("Составные части DHTML"   
 )oParagraph   
 .appendChild(oText   
 )oList   
 .replaceNode(oParagraph   
 )

создает сначала параграф с текстом "Составные части DHTML", а затем заменяет наш список oList на этот параграф. Отметим, что метод возвращает ссылку на вставленный в документ узел.
Удаление: методы removeChild() и removeNode()

Метод removeChild() позволяет у узла, который его активизирует, удалить одного из его деток. Ссылку на удаляемый узел метод принимает в качестве параметра. Например, строка кода

 var oRemovedItem = oList   
 .removeChild(oList.lastChild   
 )

удаляет из нашего списка последний элемент. Метод возвращает ссылку на удаляемый им узел. Поскольку удаленный из документа узел остается в памяти, мы можем в дальнейшем работать с ним, используя эту ссылку. Например, пристроить в какой-нибудь другой список.

Если мы хотим удалить некоторый узел из документа, то надо воспользоваться методом removeNode() . Подчеркнем, что этот метод не входит в список рекомендуемых W3C, но поддерживается Internet Explorer 5. В качестве параметра метод принимает выражение типа Boolean. Если оно равно false , то удаляется только тот узел, который активизировал метод. При этом, идущая от него ветвь дерева присоединяется к его родительскому узлу. Если параметр метода равен true , то узел удаляется вместе со всеми своими потомками. Например, строка кода

 var oRemovedList = oList   
 .removeNode(true)

удаляет из документа весь наш список целиком. Метод removeNode() возвращает ссылку на удаляемый им узел. Поскольку удаленный из документа узел остается в памяти, мы можем в дальнейшем работать с ним, используя эту ссылку.

Использовать false в качестве параметра надо осмысленно. Так, если мы применим метод removeNode() с параметром false к нашему списку, то его детки должны будут перейти к элементу DIV . Но, согласно требованиям HTML 4, элементы списка LI не могут размещаться в этом контейнере!
Перестановка: метод swapNode()

Если мы хотим переставить два узла в документе (в общем случае, две ветви дерева), то надо воспользоваться методом swapNode() . Подчеркнем, что этот метод не входит в список рекомендуемых W3C, но поддерживается Internet Explorer 5. Этот метод меняет местами узел, который его активизировал, и узел, ссылку на который от принимает в качестве параметра. Например, строка кода

 var oSwappedNode = oFirst   
 .swapNode(oSecond   
 )

переставляет узлы oFirst и oSecond . Возвращает ссылку на узел, который активизирует метод.

Небольшое отступление для поклонников Internet Explorer. W3C DOM не поддерживает известные нам еще с Internet Explorer 4 очень удобные для копирования и вставки различных фрагментов документа такие свойства объектов, как innerHTML , innerText , outerHTML и outerText . Многие действия с помощью этих свойств программируются гораздо проще, чем в рамках W3C DOM. В качестве иллюстрации ниже приведен простенький пример использования свойства outerHTML . Когда пользователь щелкает по кнопке передачи формы на сервер, вместо кнопки появляется сообщение с благодарностью:

 <INPUT TYPE="submit" VALUE="Отправить"   
 onclick="this.outerHTML=   
 'Благодарим Вас за участие в нашем опросе.'">   

А вот как это же действие можно реализовать, используя только средства W3C DOM:

 <INPUT TYPE="submit" VALUE="Отправить"   
 onclick="replaceButton(this)">   
    
    
 <SCRIPT TYPE="text/javascript">   
    
 function replaceButton(oButton) {   
 var oText =   
 document.createTextNode("Благодарим Вас за   
 участие в нашем опросе.")   
 var oParent = oButton.parentNode   
 oParent.replaceChild(oParagraph,oButton)   
 }   
    
 </SCRIPT>

Помимо рассмотренных нами методов редактирования дерева документа, имеется еще два метода, о которых необходимо сказать.
Метод applyElement()

Описанные выше методы appendChild() и insertBefore() позволяют узлам заводить деток. Internet Explorer 5 поддерживает также метод, который позволяет узлу-элементу обзаводиться родителем. Таким методом является метод applyElement() , который принимает в качестве параметра ссылку на будущего родителя. Например, строка кода

 var oParent = oChildNode   
 .applyElement(oParentNode   
 )

задает узел oParentNode в качестве родительского для узла oChildNode . При этом, узел oParentNode лишается (если они у него были) как своего родителя, так и своих деток перед тем, как "заполучить" нового наследника. Метод возвращает ссылку на нового родителя. Подчеркнем, что метод applyElement() не применим к текстовым узлам.
Метод hasChildNodes()

Узнать, есть или нет у узла детки, можно с помощью метода hasChildNodes() . Например, строка кода

 oTestedNode   
 .hasChildNodes()

возвращает true , когда узел oTestedNode имеет деток, и false в противном случае.
Работа с атрибутами элементов

    Метод createAttribute()
    Метод setAttribute()
    Метод removeAttribute()
    Метод getAttribute()
    Дополнительные методы в Internet Explorer 5

Все атрибуты узла-элемента (за исключением атрибута STYLE ) составляют коллекцию attributes . W3C определяет эту коллекцию как массив с доступом по именам элементов, например, oNode .attributes.align или oNode .attributes["align"] возвращает значение атрибута ALIGN узла oNode . (В документации Microsoft по DHTML сказано, что к элементам коллекции надо обращаться по индексу массива. Но на практике работает описанное выше обращение по именам. Вероятно, документация обновляется реже, чем появляются новые версии броузера.) Имя атрибута надо набирать прописными буквами вне зависимости от того, в каком регистре они набраны в HTML-источнике. Свойство nodeName для узлов-атрибутов возвращает название атрибута, а nodeValue  -- значение атрибута. Свойство атрибутов specified позволяет узнать, определен или нет этот атрибут. Если, например, у узла oNode атрибут ALIGN определен, то

 oNode   
 .attributes["align"].specified

возвращает true , а если не определен, то false . (Такое поведение свойства specified реализовано и в Internet Explorer 5 вопреки документации Microsoft по DHTML)
Метод createAttribute()

Метод createAttribute() объекта document позволяет создать узел-атрибут. В качестве параметра метод принимает имя атрибута. Internet Explorer 5 не поддерживает этот метод.
Метод setAttribute()

Атрибуты как новых элементов, так и тех, что заданы через HTML, можно задать либо традиционным способом -- присваивая значение свойству (атрибуту) узла, либо с помощью метода setAttribute() . Оба способа демонстрируются ниже для атрибута ID . Так, любая из строк кода

 oNode   
 .id= "newItem"   
    
 oNode   
 .setAttribute("id","newItem"   
 )

задает для элемента oNode в качестве идентификатора строку "newItem" . Метод setAttribute() требует два параметра. Первый параметр -- строка, которая задает название атрибута. Второй параметр -- строка, число или булево выражение соответствует значению атрибута. Согласно документации в Internet Explorer 5, по умолчанию названия атрибутов чувствительны к регистру, в котором они набраны. Если набор строчных и прописных букв не совпадает с использованным ранее в названии атрибута, то будет создан новый атрибут. Зависимость от регистра можно отменить, задав в качестве третьего параметра метода число 0 .
Метод removeAttribute()

Удалить атрибут у элемента можно, активизируя метод removeAttribute() , который требует в качестве параметра название этого атрибута. Если удаленный атрибут имеет значение по умолчанию, то оно восстанавливается. Согласно документации в Internet Explorer 5, по умолчанию в параметре набор строчных и прописных букв должен совпадать с использованным ранее в названии атрибута. Зависимость от регистра можно отменить, задав в качестве второго параметра метода число 0 . Метод возвращает true при успешном выполнении действия и false в противном случае.
Метод getAttribute()

Узнать текущее значение атрибута у элемента можно, активизируя метод getAttribute() , который требует в качестве параметра название этого атрибута.
Дополнительные методы в Internet Explorer 5

Метод clearAttributes() удаляет все атрибуты у объекта, который активизирует этот метод. Не требует параметров. Возвращает ссылку на этот объект.

Метод mergeAttributes() позволяет скопировать все атрибуты у объекта, ссылку на который он принимает в качестве параметра, и добавить эти атрибуты к объекту, который активизировал этот метод. Например, строка кода

 oTargetNode   
 .mergeAttributes(oSourceNode   
 )

задает те же атрибуты для узла oTargetNode , что и у узла oSourceNode . Метод возвращает ссылку на узел, который его активизировал. Оба узла ( oTargetNode и oSourceNode ) должны соответствовать одинаковым HTML-элементам, например, элементам H1 или P.

Домен продается

Популярное

Не так давно в сети появился новый сервис, под названием Dead Man Zero. Этот сервис сделал...
Рынок социальных площадок уже давно стал стабильным. Несмотря на то, что время от времени...
Artisteer 4 – единственный в своем роде продукт, позволяющий автоматизировать работу над созданием...
Апрель 2024 (1)
Октябрь 2018 (14)
Февраль 2017 (3)
Январь 2017 (1)
Август 2016 (1)
Май 2016 (2)

Карта сайта: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41

Друзья сайта



Случайная цитата

Alan J. Perlis:

"Низкоуровневый язык — это когда требуется внимание к вещам, которые никак не связаны с программами на этом языке."

Опрос

Ваша техника?

Настольный компютер
Ноутбук
Смартфон
iPad
iPhone
другое