Глава 4.2. Базовая объектная модель документа4.2.1. Введение в DOMПри описании каскадных таблиц стилей мы уже ввели рассмотрение HTML-документа как дерева элементов. Приведем еще раз пример, иллюстрирующий этот подход. Пусть наш документ имеет вид: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> <HTML> <HEAD> <TITLE>Моя домашняя страница</TITLE> </HEAD> <BODY> <H1>Моя домашняя страница</H1> <P>Добро пожаловать!</P> </BODY> </HTML> Тогда мы можем представить его в виде следующего дерева: Корнем этого дерева является элемент HTML, который имеет двух детей HEAD и BODY. Элемент HEAD является отцом элемента TITLE, а элемент BODY отцом элементов H1 и P (два последних элемента называются братьями, причем H1 является старшим братом, а P младшим). Все элементы дерева являются потомками корня, а тот является их предком. При этом все элементы и тексты, образующие их содержимое, являются узлами дерева документа. Каждый элемент данного дерева соответствует элементу HTML и, следовательно, имеет тег(и), содержимое и набор атрибутов. Для перехода к объектной модели документа остается сделать единственный шаг: назвать все элементы дерева объектами, а их атрибуты сделать доступными для чтения и для изменения из сценариев и аплетов. В результате дерево элементов HTML-документа становится динамически управляемым; более того, теперь мы можем легко добавлять к каждому элементу новые свойства, помимо стандартных атрибутов HTML. Именно такой подход был положен в основу динамической модели HTML обозревателей Microsoft, а затем принят за основу стандартов W3C, получивших название объектная модель документа (Document Object Model или DOM). При этом W3C расширил понятие DOM на любые XML-документы, рассматривая HTML DOM как специализированный частный случай с дополнительными возможностями. Таким образом, DOM это модель HTML- и XML-документов, независимая от платформы и языка программирования, которая определяет:
На сегодняшний день W3C стандартизовал DOM первого и второго уровней (DOM 1 и DOM 2); в стадии рабочего проекта находится DOM 3. Эти аббревиатуры соответственно обозначают следующее:
Учитывая текущее состояние вещей, мы рассматриваем здесь только DOM 2 (и содержащуюся в нем DOM 1). DOM 2 состоит из следующих групп взаимосвязанных интерфейсов:
См. также WDH+: Программы просмотра дерева объектов. Начнем с определения базовых интерфейсов, лежащих в основе всех дальнейших спецификаций. 4.2.2. Основные понятияDOM 2 Core представляет XML-документы в виде деревьев, состоящих из узлов, которые, в свою очередь, также являются объектами и реализуют более специализированные интерфейсы. Одни типы узлов могут иметь детей, т. е. сами являться поддеревьями, другие являются листьями, т. е. детей не имеют. В следующей таблице сведены все возможные типы узлов абстрактного документа; для каждого типа узлов перечислены те узлы, которые могут быть его детьми. О понятиях, соответствующих перечисленным узлам, см. описание структуры XML-документа.
Кроме того, DOM 2 Core содержит спецификацию интерфейсов NodeList (упорядоченные списки узлов, доступных по номеру в списке) и NamedNodeMap (неупорядоченные списки узлов, доступных по своему имени). Эти объекты являются живыми, т. е. любое изменение документа автоматически влечет изменение всех связанных с ним списков. Интерфейсы Text, Comment и CDATASection являются потомками интерфейса CharacterData. Следует подчеркнуть, что DOM 2 Core содержит два набора интерфейсов, каждый из которых обеспечивает полный доступ ко всем элементам документа. Первый набор представляет объектно-ориентированный подход со следующей иерархией наследования: документ составляющие его элементы их атрибуты и текстовое содержимое. При таком рассмотрении дерева документа мы говорим о иерархии объектов. Второй подход построен по принципу "все есть узлы (Nodes)". Здесь все составляющие документа рассматриваются как равноправные узлы его дерева, и мы можем говорить только о иерархии узлов. Таким образом, DOM 2 по своей сути является избыточной, но предоставляет нам возможность в зависимости от задачи рассматривать документ тем или иным способом. Все интерфейсы DOM 2 Core подразделяются на основные (fundamental) и дополнительные (extended). Основными являются интерфейсы DOMException, DOMImplementation, DocumentFragment, Document, Node, NodeList, NamedNodeMap, CharacterData, Attr, Element, Text и Comment. Эти интерфейсы должны поддерживаться всеми реализациями DOM, как для XML-, так и для HTML-документов. Дополнительные интерфейсы ориентированы на XML-документы, поэтому реализации DOM для HTML могут их не поддерживать. К ним относятся CDATASection, DocumentType, Notation, Entity, EntityReference и ProcessingInstruction. В целях независимости от языка и платформы DOM определяет следующие типы:
Ниже приведено краткое описание всех интерфейсов DOM с указанием уровня модели (DOM 1 или DOM 2), в которой определено то или иное свойство интерфейса. Спецификации W3C написаны на платформо-независимом языке IDL. Мы же приводим их в соответствии с синтаксисом JavaScript, который на сегодня является основным сценарным языком. Вместе с описание стандарта мы приводим краткое описание его реализации в объектных моделях Microsoft и Gecko. При этом следует учитывать, что реализации Microsoft для XML и HTML совершенно независимы (они реализуются соответственно программными компонентами XMLDOM и MSHTML), тогда как в Gecko объектная модель одинакова для HTML- и XML-документов. Основное внимание в последующем изложении уделено DOM для HTML; XML DOM подробно будет рассмотрена в Части VIII. 4.2.3. Исключения: интерфейс DOMExceptionОбъект DOMException является прототипом всех исключительных ситуаций, которые могут возникнуть в процессе обработки документа. Он имеет единственное свойство code типа Number, которое содержит номер исключения согласно следующей таблицы:
Поддержка: Нестандартная реализация. Поддерживается часть кодов ошибок. 4.2.4. Описание реализации: интерфейс DOMImplementationПоддержка: Только для XML-документов (XMLDOMImplementation). Соответствует DOM 1. Интерфейс DOMImplementation содержит методы, выполнение которых не зависит от конкретной объектной модели документа. Он доступен через свойство implementation объекта Document.
Метод createCSSStyleSheetСинтаксис: объект.createCSSStyleSheet(title, media) Аргументы: title, media выражения типа DOMString Результат: новый объект CSSStyleSheet Исключения: SYNTAX_ERR Поддержка: Не поддерживается. Не поддерживается. Метод createCSSStyleSheet создает новый объект CSSStyleSheet и возвращает указатель на него. Этот метод должен поддерживаться только теми реализациями DOM, которые поддерживают CSS. Объект создается вне контекста документа; DOM 2 не позволяет поключить вновь созданную таблицу стилей к документу. Аргумент title задает титул таблицы стилей, а media список устройств отображения через запятую. Метод createDocumentСинтаксис: объект.createDocument(namespaceURI, qualifiedName, doctype) Аргументы: namespaceURI, qualifiedName выражения типа DOMString doctype выражение типа DocumentType Результат: новый объект Document Исключения: INVALID_CHARACTER_ERR, NAMESPACE_ERR, WRONG_DOCUMENT_ERR Поддержка: Не поддерживается. Не поддерживается. Метод createDocument создает новый объект Document и возвращает указатель на него. Он предназначен для создания XML-документов и для HTML-документов может не поддерживаться. Аргумент namespaceURI задает URI пространства имен корневого элемента документа, qualifiedName его ограниченное имя, а doctype тип создаваемого документа (может иметь значение null). Метод createDocumentTypeСинтаксис: объект.createDocumentType(qualifiedName, publicId, systemId) Аргументы: qualifiedName, publicId, systemId выражения типа DOMString Результат: новый узел DocumentType Исключения: INVALID_CHARACTER_ERR, NAMESPACE_ERR Поддержка: Не поддерживается. Не поддерживается. Метод createDocumentType создает пустой узел типа DocumentType и возвращает указатель на него. Он предназначен для XML-документов и для HTML-документов может не поддерживаться. Аргумент qualifiedName задает ограниченное имя создаваемого типа документа, publicId публичный идентификатор внешнего раздела, а systemId системный идентификатор внешнего раздела. Метод hasFeatureСинтаксис: объект.hasFeature(feature, version) Аргументы: feature, version выражения типа DOMString Результат: логическое значение Исключения: нет Поддержка: Только для XML-документов. Соответствует стандарту. Метод hasFeature возвращает true, если реализация DOM поддерживает указанное свойство, и false в противном случае. Имя свойства (в любом регистре) задается аргументом feature; оно должно соответствовать правилам образования имен XML. Аргумент version задает имя версии проверяемого свойства. Если он не задан, то возвращается true, если поддерживается хотя бы какая-то версия данного свойства. DOM 2 рекомендует, чтобы реализации DOM возвращали true, если version равно "2.0", а feature принимает следующие значения:
В Gecko значениями feature могут быть строки "XML" и "HTML", а значением version строки "1.0" и "2.0". Пример: alert(document.implementation.hasFeature("HTML", "1.0")); alert(document.implementation.hasFeature("HTML", "2.0")); alert(document.implementation.hasFeature("HTML", "3.0")); Первые два оператора alert выведут строку true, а третий false. В Microsoft XMLDOM значениями feature могут быть строки "XML", "DOM" и "MS-DOM", а значением version строка "1.0". Пример: var objDoc = new ActiveXObject("Microsoft.XMLDOM"); alert(objDoc.implementation.hasFeature("XML", "1.0")); alert(objDoc.implementation.hasFeature("XML", "2.0")); Первый оператор alert выведет строку true, а второй false. 4.2.5. Фрагмент документа: интерфейс DocumentFragmentПоддержка: Только для XML-документов (XMLDOMDocumentFragment). Соответствует DOM 1. Интерфейс DocumentFragment является потомком интерфейса Node и наследует все его свойства и методы. Как и вытекает из его названия, он предназначен для операций с фрагментами документов (извлечение части дерева документа, создание нового фрагмента документа, вставка фрагмента в качестве сына какого-либо узла и т. п.). Отметим, что при вставке объекта типа DocumentFragment в узел Node, способный иметь детей, вставляются все дети этого объекта, но не сам объект. Примеры см. в описании интерфейса Node. 4.2.6. Документ: интерфейс DocumentПоддержка: Поддерживается для XML-документов (XMLDOMDocument); для HTML-документов поддерживается частично. DOM 1 полностью, DOM 2 частично. Интерфейс Document соответствует XML- или HTML-документу. Он является основой для доступа к содержанию документа и для создания его составляющих.
Свойство doctypeСинтаксис: документ.doctype Изменяемое: нет Поддержка: Соответствует стандарту. Соответствует стандарту. Свойство doctype возвращает тип данного документа (типа DocumentType). Для HTML-документов и для XML-документов, не имеющих декларации типа документа, возвращается null. Свойство documentElementСинтаксис: документ.documentElement Изменяемое: нет Поддержка: Соответствует стандарту. Соответствует стандарту. Свойство documentElement возвращает корневой элемент данного документа (типа Element). Для HTML-документов возвращается элемент HTML. Пример: оператор alert(document.documentElement.tagName); выведет на экран строку HTML. Свойство implementationСинтаксис: документ.implementation Изменяемое: нет Поддержка: Только для XML-документов. Соответствует DOM 1. Свойство implementation возвращает объект типа DOMImplementation, описывающий данную реализацию DOM. Свойство styleSheetsСинтаксис: документ.styleSheets Изменяемое: нет Поддержка: Только для HTML-документов. Соответствует стандарту. Свойство styleSheets возвращает объект типа StyleSheetList, соответствующий списку внешних и внутренних таблиц стилей документа. Это свойство поддерживается только теми реализациями DOM, которые поддерживают динамические таблицы стилей. Метод createAttributeСинтаксис: документ.createAttribute(name) Аргументы: name выражение типа DOMString Результат: новый объект Attr Исключения: INVALID_CHARACTER_ERR Поддержка: Только для XML-документов. Соответствует стандарту. Метод createAttribute создает новый объект типа Attr и возвращает указатель на него. Аргумент name задает имя создаваемого атрибута. У нового объекта атрибут nodeName имеет значение name, а атрибуты localName, prefix и namespaceURI равны null. В дальнейшем созданный атрибут может быть присвоен какому-либо элементу методом Element.setAttributeNode. Пример создания атрибута для элемента HTML: var myDiv = document.getElementById('idDiv'); var attr = document.createAttribute('temp'); attr.value = 'temporary'; myDiv.setAttributeNode(attr); alert(myDiv.getAttribute('temp')); Оператор alert выведет строку temporary. Пример создания атрибута в Microsoft XMLDOM: var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = false; xmlDoc.load("c:\My Documents\books.xml"); var root = xmlDoc.documentElement; var newAttr = xmlDoc.createAttribute("temp"); newAttr.value = 'temporary'; root.setAttributeNode(attr); alert(root.getAttribute('temp')); Здесь оператор alert также выведет строку temporary. Метод createAttributeNSСинтаксис: документ.createAttributeNS(namespaceURI, qualifiedName) Аргументы: namespaceURI, qualifiedName выражения типа DOMString Результат: новый объект Attr Исключения: INVALID_CHARACTER_ERR, NAMESPACE_ERR Поддержка: Не поддерживается. Не поддерживается. Метод createAttributeNS создает новый объект типа Attr и возвращает указатель на него. Он предназначен для XML-документов и для HTML-документов может не поддерживаться. Аргумент namespaceURI задает URI пространства имен, а qualifiedName ограниченное имя создаваемого атрибута в этом пространстве имен. Созданный объект типа Attr имеет следующие атрибуты:
В дальнейшем созданный атрибут может быть присвоен какому-либо элементу методом Element.setAttributeNode. Метод createCDATASectionСинтаксис: документ.createCDATASection(data) Аргументы: data выражение типа DOMString Результат: новый объект CDATASection Исключения: NOT_SUPPORTED_ERR Поддержка: Соответствует стандарту. Соответствует стандарту. Метод createCDATASection создает новый объект типа CDATASection и возвращает указатель на него. Он предназначен только для XML-документов; попытка вызвать его в HTML DOM генерирует исключение NOT_SUPPORTED_ERR. Аргумент data задает содержимое создаваемой секции CDATA. Пример создания секции CDATA в Microsoft XMLDOM: var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = false; xmlDoc.load("c:\My Documents\books.xml"); var root = xmlDoc.documentElement; var newSection = xmlDoc.createCDATASection("Hello World!"); root.appendChild(newSection); Метод createCommentСинтаксис: документ.createComment(data) Аргументы: data выражение типа DOMString Результат: новый объект Comment Исключения: нет Поддержка: Только для XML-документов. Соответствует стандарту. Метод createComment создает новый объект типа Comment и возвращает указатель на него. Аргумент data задает содержимое создаваемого комментария. Пример создания комментария в Gecko: var root = document.documentElement.firstChild; var comm = document.createComment('Это комментарий.'); root.appendChild(comm); Метод createDocumentFragmentСинтаксис: документ.createDocumentFragment() Результат: новый объект DocumentFragment Исключения: нет Поддержка: Только для XML-документов. Соответствует стандарту. Метод createDocumentFragment создает новый пустой объект типа DocumentFragment и возвращает указатель на него. Пример создания фрагмента документа в Gecko: var elem = document.documentElement.firstChild; var o = document.createDocumentFragment(); elem.appendChild(o); Метод createElementСинтаксис: документ.createElement(tagName) Аргументы: tagName выражение типа DOMString Результат: новый объект Element Исключения: INVALID_CHARACTER_ERR Поддержка: Соответствует стандарту (см. прим. 2). Соответствует стандарту. Метод createElement создает новый объект типа Element и возвращает указатель на него. Аргумент tagName задает тег создаваемого элемента. У нового объекта атрибут nodeName имеет значение tagName, а атрибуты localName, prefix и namespaceURI равны null. Если объект имеет атрибуты со значениями по умолчанию, то автоматически создаются соответствующие узлы Attr и присоединяются к элементу. Примечания:
Пример создания элемента P и включения его в документ: var text = document.createTextNode("Пример абзаца."); var elem = document.createElement("P"); elem.appendChild(text); document.body.appendChild(elem); Метод createElementNSСинтаксис: документ.createElementNS(namespaceURI, qualifiedName) Аргументы: namespaceURI, qualifiedName выражения типа DOMString Результат: новый объект Element Исключения: INVALID_CHARACTER_ERR, NAMESPACE_ERR Поддержка: Не поддерживается. Не поддерживается. Метод createElementNS создает новый объект типа Element и возвращает указатель на него. Он предназначен для XML-документов и для HTML-документов может не поддерживаться. Аргумент namespaceURI задает URI пространства имен, а qualifiedName ограниченное имя создаваемого элемента в этом пространстве имен. Созданный объект типа Element имеет следующие атрибуты:
Метод createEntityReferenceСинтаксис: документ.createEntityReference(name) Аргументы: name выражение типа DOMString Результат: новый объект EntityReference Исключения: INVALID_CHARACTER_ERR, NOT_SUPPORTED_ERR Поддержка: Соответствует стандарту. Соответствует стандарту. Метод createEntityReference создает новый объект типа EntityReference и возвращает указатель на него. Он предназначен только для XML-документов; попытка вызвать его в HTML DOM генерирует исключение NOT_SUPPORTED_ERR. Аргумент name задает имя раздела, на который должна быть создана ссылка. Пример создания ссылки на раздел в Microsoft XMLDOM: var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = false; xmlDoc.load("c:\My Documents\books.xml"); var root = xmlDoc.documentElement; var myEntity = xmlDoc.createEntityReference("newRef"); root.childNodes.item(0).appendChild(myEntity); Метод createEventСинтаксис: документ.createEvent(type) Аргументы: type выражение типа DOMString Результат: новый объект Event Исключения: NOT_SUPPORTED_ERR Поддержка: Не поддерживается. Не поддерживается. Метод createEvent создает новый объект Event и возвращает указатель на него. Этот метод должен поддерживаться только теми реализациями DOM, которые поддерживают события. Аргумент type задает тип создаваемого события:
Метод createProcessingInstructionСинтаксис: документ.createProcessingInstruction(target, data) Аргументы: target, data выражения типа DOMString Результат: новый объект ProcessingInstruction Исключения: INVALID_CHARACTER_ERR, NOT_SUPPORTED_ERR Поддержка: Соответствует стандарту. Соответствует стандарту. Метод createProcessingInstruction создает новый объект типа ProcessingInstruction
и возвращает указатель на него. Он предназначен только для XML-документов; попытка вызвать его
в HTML DOM генерирует исключение NOT_SUPPORTED_ERR. Аргумент target задает
имя создаваемой директивы, а аргумент data
ее содержимое, т. е. все что следует в директиве после ее имени до закрывающего тега "?>".
Пример создания директивы var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = false; xmlDoc.load("c:\My Documents\books.xml"); var root = xmlDoc.documentElement; var pi = xmlDoc.createProcessingInstruction("xml", ' version="1.0"'); xmlDoc.insertBefore(pi, xmlDoc.childNodes.item(0)); Метод createTextNodeСинтаксис: документ.createTextNode(data) Аргументы: data выражение типа DOMString Результат: новый объект Text Исключения: нет Поддержка: Соответствует стандарту. Соответствует стандарту. Метод createTextNode создает новый объект типа Text и возвращает указатель на него. Аргумент data задает текст, который присваивается атрибуту nodeValue объекта. Пример: var text = document.createTextNode("Пример абзаца."); var elem = document.createElement("P"); elem.appendChild(text); document.body.appendChild(elem); Метод getElementByIdСинтаксис: документ.getElementById(elementId) Аргументы: elementId выражение типа DOMString Результат: объект Element или null Исключения: нет Поддержка: Только для HTML-документов. Соответствует стандарту. Метод getElementById возвращает указатель на объект Element, имеющий заданный идентификатор elementId. Если такого объекта нет, то возвращается null; если же таких элементов несколько, то возвращается первый из них. Следующий пример <P id="id1">Абзац 1</P> <P id="id1">Абзац 2</P> <SCRIPT> var o = document.getElementById("id1"); alert(o.firstChild.nodeValue); </SCRIPT> ... выведет на экран текст Абзац 1. Примечание. В HTML идентификатором элемента является значение его атрибута id. В XML такого атрибута нет, поэтому данный метод имеет смысл только применительно к тем элементам, у которых в DTD определен атрибут-идентификатор. Для остальных элементов этот метод должен возвращать null. Метод getElementsByTagNameСинтаксис: документ.getElementsByTagName(tagname) Аргументы: tagname выражение типа DOMString Результат: объект NodeList Исключения: нет Поддержка: Соответствует стандарту (см. прим.). Соответствует стандарту. Метод getElementsByTagName создает новый объект NodeList, состоящий из всех объектов Element, имеющих заданное имя и возвращает указатель на него. Аргумент tagname задает искомый тег; если он равен "*", то возвращается список всех элементов документа. Примечание. Internet Explorer не поддерживает специальное имя
"*". В нем вместо Следующий пример выводит на экран количество графических образов в документе, а затем URI первого образа: var imgList = document.getElementsByTagName("IMG"); alert(imgList.length); alert(imgList[0].src); Метод getElementsByTagNameNSСинтаксис: документ.getElementsByTagNameNS(namespaceURI, localName) Аргументы: namespaceURI, localName выражения типа DOMString Результат: объект NodeList Исключения: нет Поддержка: Не поддерживается. Не поддерживается. Метод getElementsByTagNameNS создает новый объект NodeList, состоящий из всех объектов Element, имеющих заданное имя и возвращает указатель на него. Он предназначен для XML-документов и для HTML-документов может не поддерживаться. Аргумент namespaceURI задает URI пространства имен, а localName локальное имя искомого тега в этом пространстве имен; если он равен "*", то возвращается список всех элементов документа, относящихся к данному словарю. Метод importNodeСинтаксис: документ.importNode(node, deep) Аргументы: node выражение типа Node deep логическое выражение Результат: объект Node Исключения: NOT_SUPPORTED_ERR Поддержка: Не поддерживается. Соответствует стандарту. Метод importNode импортирует копию узла Node из другого документа в данный. При этом создается копия исходного узла, как описано ниже; исходный узел не изменяется. Метод возвращает указатель на созданный объект Node. Если аргумент deep равен true, то производится импорт поддерева исходного документа, начиная с данного узла; в противном случае импортируется только сам узел. Возвращаемый узел не имеет предка (его атрибут parentNode равен null). Значения атрибутов nodeName, nodeType, prefix, localName и namespaceURI копируются из исходного узла. Дополнительная информация, копируемая в новый узел, зависит от типа узла (см. Таблицу 4.3):
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||