Представление html в виде дерева. Структурирование данных с помощью JavaScript: Дерево. Другие типы узлов

Работа с DOM-моделью

Каждый объект Window имеет свойство document , ссылающееся на объект Document. Этот объект Document не является автономным объектом. Он является центральным объектом обширного API, известного как объектная модель документа (DOM), который определяет порядок доступа к содержимому документа.

Обзор модели DOM

Объектная модель документа (Document Object Model, DOM) - это фундаментальный прикладной программный интерфейс, обеспечивающий возможность работы с содержимым HTML и XML-документов. Прикладной программный интерфейс (API) модели DOM не особенно сложен, но в нем существует множество архитектурных особенностей, которые вы должны знать.

Прежде всего, следует понимать, что вложенные элементы HTML или XML-документов представлены в виде дерева объектов DOM. Древовидное представление HTML-документа содержит узлы, представляющие элементы или теги, такие как и

И узлы, представляющие строки текста. HTML-документ также может содержать узлы, представляющие HTML-комментарии. Рассмотрим следующий простой HTML-документ:

Пример документа Это HTML-документ

Пример простого текста.

DOM-представление этого документа приводится на следующей диаграмме:

Тем, кто еще не знаком с древовидными структурами в компьютерном программировании, полезно узнать, что терминология для их описания была заимствована у генеалогических деревьев. Узел, расположенный непосредственно над данным узлом, называется родительским по отношению к данному узлу. Узлы, расположенные на один уровень ниже другого узла, являются дочерними по отношению к данному узлу. Узлы, находящиеся на том же уровне и имеющие того же родителя, называются сестринскими . Узлы, расположенные на любое число уровней ниже другого узла, являются его потомками. Родительские, прародительские и любые другие узлы, расположенные выше данного узла, являются его предками.

Каждый прямоугольник на этой диаграмме является узлом документа, который представлен объектом Node . Обратите внимание, что на рисунке изображено три различных типа узлов. Корнем дерева является узел Document, который представляет документ целиком. Узлы, представляющие HTML-элементы, являются узлами типа Element, а узлы, представляющие текст, - узлами типа Text. Document, Element и Text - это подклассы класса Node. Document и Element являются двумя самыми важными классами в модели DOM.

Тип Node и его подтипы образуют иерархию типов, изображенную на диаграмме ниже. Обратите внимание на формальные отличия между обобщенными типами Document и Element, и типами HTMLDocument и HTMLElement. Тип Document представляет HTML и XML-документ, а класс Element представляет элемент этого документа. Подклассы HTMLDocument и HTMLElement представляют конкретно HTML-документ и его элементы:

На этой диаграмме следует также отметить наличие большого количества подтипов класса HTMLElement, представляющих конкретные типы HTML-элементов. Каждый из них определяет JavaScript-свойства, отражающие HTML-атрибуты конкретного элемента или группы элементов. Некоторые из этих специфических классов определяют дополнительные свойства или методы, которые не являются отражением синтаксиса языка разметки HTML.

Выбор элементов документа

Работа большинства клиентских программ на языке JavaScript так или иначе связана с манипулированием элементами документа. В ходе выполнения эти программы могут использовать глобальную переменную document, ссылающуюся на объект Document. Однако, чтобы выполнить какие-либо манипуляции с элементами документа, программа должна каким-то образом получить, или выбрать, объекты Element, ссылающиеся на эти элементы документа. Модель DOM определяет несколько способов выборки элементов. Выбрать элемент или элементы документа можно:

    по значению атрибута id;

    по значению атрибута name;

    по имени тега;

    по имени класса или классов CSS;

    по совпадению с определенным селектором CSS.

Все эти приемы выборки элементов описываются в следующих подразделах.

Выбор элементов по значению атрибута id

Все HTML-элементы имеют атрибуты id. Значение этого атрибута должно быть уникальным в пределах документа - никакие два элемента в одном и том же документе не должны иметь одинаковые значения атрибута id. Выбрать элемент по уникальному значению атрибута id можно с помощью метода getElementById() объекта Document:

Var section1 = document.getElementById("section1");

Это самый простой и самый распространенный способ выборки элементов. Если сценарию необходимо иметь возможность манипулировать каким-то определенным множеством элементов документа, присвойте значения атрибутам id этих элементов и используйте возможность их поиска по этим значениям.

В версиях Internet Explorer ниже IE8 метод getElementById() выполняет поиск значений атрибутов id без учета регистра символов и, кроме того, возвращает элементы, в которых будет найдено совпадение со значением атрибута name.

Выбор элементов по значению атрибута name

HTML-атрибут name первоначально предназначался для присваивания имен элементам форм, и значение этого атрибута использовалось, когда выполнялась отправка данных формы на сервер. Подобно атрибуту id, атрибут name присваивает имя элементу. Однако, в отличие от id, значение атрибута name не обязано быть уникальным: одно и то же имя могут иметь сразу несколько элементов, что вполне обычно при использовании в формах радиокнопок и флажков. Кроме того, в отличие от id, атрибут name допускается указывать лишь в некоторых HTML-элементах, включая формы, элементы форм и элементы и .

Выбрать HTML-элементы, опираясь на значения их атрибутов name, можно с помощью метода getElementsByName() объекта Document:

Var radiobuttons = document.getElementsByName("favorite_color");

Метод getElementsByName() определяется не классом Document, а классом HTMLDocument, поэтому он доступен только в HTML-документах и не доступен в XML-документах. Он возвращает объект NodeList , который ведет себя, как доступный только для чтения массив объектов Element.

В IE метод getElementsByName() возвращает также элементы, значения атрибутов id которых совпадает с указанным значением. Чтобы обеспечить совместимость с разными версиями браузеров, необходимо внимательно подходить к выбору значений атрибутов и не использовать одни и те же строки в качестве значений атрибутов name и id.

Выбор элементов по типу

Метод getElementsByTagName() объекта Document позволяет выбрать все HTML или XML-элементы указанного типа (или по имени тега). Например, получить подобный массиву объект, доступный только для чтения, содержащий объекты Element всех элементов в документе, можно следующим образом:

Var spans = document.getElementsByTagName("span");

Подобно методу getElementsByName(), getElementsByTagName() возвращает объект NodeList. Элементы документа включаются в массив NodeList в том же порядке, в каком они следуют в документе, т.е. первый элемент

В документе можно выбрать так:

Var firstParagraph = document.getElementsByTagName("p");

Имена HTML-тегов не чувствительны к регистру символов, и когда getElementsByTagName() применяется к HTML-документу, он выполняет сравнение с именем тега без учета регистра символов. Переменная spans, созданная выше, например, будет включать также все элементы , которые записаны как .

Можно получить NodeList, содержащий все элементы документа, если передать методу getElementsByTagName() шаблонный символ «*».

Кроме того, классом Element также определяет метод getElementsByTagName(). Он действует точно так же, как и версия метода в классе Document, но выбирает только элементы, являющиеся потомками для элемента, относительно которого вызывается метод. То есть отыскать все элементы внутри первого элемента

Можно следующим образом:

Var firstParagraph = document.getElementsByTagName("p"); var firstParagraphSpans = firstParagraph.getElementsByTagName("span");

По историческим причинам класс HTMLDocument определяет специальные свойства для доступа к узлам определенных типов. Свойства images , forms и links , например, ссылаются на объекты, которые ведут себя как массивы, доступные только для чтения, содержащие элементы , и (но только те теги , которые имеют атрибут href). Эти свойства ссылаются на объекты HTMLCollection, которые во многом похожи на объекты NodeList, но дополнительно могут индексироваться значениями атрибутов id и name.

Объект HTMLDocument также определяет свойства-синонимы embeds и plugins , являющиеся коллекциями HTMLCollection элементов . Свойство anchors является нестандартным, но с его помощью можно получить доступ к элементам , имеющим атрибут name, но не имеющим атрибут href. Свойство scripts определено стандартом HTML5 и является коллекцией HTMLCollection элементов .

Кроме того, объект HTMLDocument определяет два свойства, каждое из которых ссылается не на коллекцию, а на единственный элемент. Свойство document.body представляет элемент HTML-документа, а свойство document.head - элемент . Эти свойства всегда определены в документе: даже если в исходном документе отсутствуют элементы и , браузер создаст их неявно. Свойство documentElement объекта Document ссылается на корневой элемент документа. В HTML-документах он всегда представляет элемент .

Выбор элементов по классу CSS

Значением HTML-атрибута class является список из нуля или более идентификаторов, разделенных пробелами. Он дает возможность определять множества связанных элементов документа: любые элементы, имеющие в атрибуте class один и тот же идентификатор, являются частью одного множества. Слово class зарезервировано в языке JavaScript, поэтому для хранения значения HTML-атрибута class в клиентском JavaScript используется свойство className.

Обычно атрибут class используется вместе с каскадными таблицами стилей CSS, с целью применить общий стиль отображения ко всем членам множества. Однако кроме этого, стандарт HTML5 определяет метод getElementsByClassName() , позволяющий выбирать множества элементов документа на основе идентификаторов в их атрибутах class.

Подобно методу getElementsByTagName(), метод getElementsByClassName() может вызываться и для HTML-документов, и для HTML-элементов, и возвращает «живой» объект NodeList, содержащий все потомки документа или элемента, соответствующие критерию поиска.

Метод getElementsByClassName() принимает единственный строковый аргумент, но в самой строке может быть указано несколько идентификаторов, разделенных пробелами. Соответствующими будут считаться все элементы, атрибуты class которых содержат все указанные идентификаторы. Порядок следования идентификаторов не имеет значения. Обратите внимание, что и в атрибуте class, и в аргументе метода getElementsByClassName() идентификаторы классов разделяются пробелами, а не запятыми.

Ниже приводится несколько примеров использования метода getElementsByClassName():

// Отыскать все элементы с классом "warning" var warnings = document.getElementsByClassName("warning"); // Отыскать всех потомков элемента с идентификаторам "log" // с классами "error" и "fatal" var log = document.getElementById("log"); var fatal = log.getElementsByClassName("fatal error");

Выбор элементов с использованием селекторов CSS

Каскадные таблицы стилей CSS имеют очень мощные синтаксические конструкции, известные как селекторы, позволяющие описывать элементы или множества элементов документа. Наряду со стандартизацией селекторов CSS3 , другой стандарт консорциума W3C, известный как Selectors API , определяет методы JavaScript для получения элементов, соответствующих указанному селектору.

Ключевым в этом API является метод querySelectorAll() объекта Document. Он принимает единственный строковый аргумент с селектором CSS и возвращает объект NodeList, представляющий все элементы документа, соответствующие селектору.

В дополнение к методу querySelectorAll() объект документа также определяет метод querySelector() , подобный методу querySelectorAll(), - с тем отличием, что он возвращает только первый (в порядке следования в документе) соответствующий элемент или null, в случае отсутствия соответствующих элементов.

Эти два метода также определяются классом Elements. Когда они вызываются относительно элемента, поиск соответствия заданному селектору выполняется во всем документе, а затем результат фильтруется так, чтобы в нем остались только потомки использованного элемента. Такой подход может показаться противоречащим здравому смыслу, так как он означает, что строка селектора может включать предков элемента, для которого выполняется сопоставление.

Структура документа и навигация по документу

После выбора элемента документа иногда бывает необходимо отыскать структурно связанные части документа (родитель, братья, дочерний элемент). Объект Document можно представить как дерево объектов Node. Тип Node определяет свойства, позволяющие перемещаться по такому дереву. Существует еще один прикладной интерфейс навигации по документу, как дерева объектов Element.

Документы как деревья узлов

Объект Document, его объекты Element и объекты Text, представляющие текстовые фрагменты в документе - все они являются объектами Node. Класс Node определяет следующие важные свойства:

parentNode

Родительский узел данного узла или null для узлов, не имеющих родителя, таких как Document.

childNodes

Доступный для чтения объект, подобный массиву (NodeList), обеспечивающий представление дочерних узлов.

firstChild, lastChild

Первый и последний дочерние узлы или null, если данный узел не имеет дочерних узлов.

nextSibling, previousSibling

Следующий и предыдущий братские узлы. Братскими называются два узла, имеющие одного и того же родителя. Порядок их следования соответствует порядку следования в документе. Эти свойства связывают узлы в двусвязный список.

nodeType

Тип данного узла. Узлы типа Document имеют значение 9 в этом свойстве. Узлы типа Element - значение 1. Текстовые узлы типа Text - значение 3. Узлы типа Comments - значение 8 и узлы типа DocumentFragment - значение 11.

nodeValue

Текстовое содержимое узлов Text и Comment.

nodeName

Имя тега элемента Element, в котором все символы преобразованы в верхний регистр.

С помощью этих свойств класса Node можно сослаться на второй дочерний узел первого дочернего узла объекта Document, как показано ниже:

Document.childNodes.childNodes == document.firstChild.firstChild.nextSibling

Допустим, что рассматриваемый документ имеет следующий вид:

TestHello World!

Тогда вторым дочерним узлом первого дочернего узла будет элемент . В свойстве nodeType он содержит значение 1 и в свойстве nodeName - значение «BODY».

Однако, обратите внимание, что этот прикладной интерфейс чрезвычайно чувствителен к изменениям в тексте документа. Например, если в этот документ добавить единственный перевод строки между тегами и , этот символ перевода строки станет первым дочерним узлом (текстовым узлом Text) первого дочернего узла, а вторым дочерним узлом станет элемент , а не .

Документы как деревья элементов

Когда основной интерес представляют сами элементы документа, а не текст в них (и пробельные символы между ними), гораздо удобнее использовать прикладной интерфейс, позволяющий интерпретировать документ как дерево объектов Element, игнорируя узлы Text и Comment, которые также являются частью документа.

Первой частью этого прикладного интерфейса является свойство children объектов Element. Подобно свойству childNodes, его значением является объект NodeList. Однако, в отличие от свойства childNodes, список children содержит только объекты Element.

Обратите внимание, что узлы Text и Comment не имеют дочерних узлов. Это означает, что описанное выше свойство Node.parentNode никогда не возвращает узлы типа Text или Comment. Значением свойства parentNode любого объекта Element всегда будет другой объект Element или корень дерева - объект Document или DocumentFragment.

Второй частью прикладного интерфейса навигации по элементам документа являются свойства объекта Element, аналогичные свойствам доступа к дочерним и братским узлам объекта Node:

firstElementChild, lastElementChild

Похожи на свойства firstChild и lastChild, но возвращают дочерние элементы.

nextElementSibling, previousElementSibling

Похожи на свойства nextSibling и previousSibling, но возвращают братские элементы.

childElementCount

Количество дочерних элементов. Возвращает то же значение, что и свойство children.length.

Эти свойства доступа к дочерним и братским элементам стандартизованы и реализованы во всех текущих браузерах, кроме IE.

Согласно модели DOM:

  • Весь документ представляется узлом документа;
  • Каждый HTML тэг является узлом элемента;
  • Текст внутри HTML элементов представляется текстовыми узлами;
  • Каждому HTML атрибуту соответствует узел атрибута;
  • Комментарии являются узлами комментариев.

HTML документ

Заголовок

Просто текст

Пример 6.2

В этом примере корневым узлом является тэг . Все остальные узлы содержатся внутри . У этого узла имеется два дочерних узла: и . Узел содержит узел , а узел содержит узлы и

Следует обратить особое внимание на то, что текст, расположенный в узле элемента соответствует текстовому узлу. В примере HTML документ узел элемента содержит текстовый узел " HTML документ ", то есть " HTML документ " не является значением элемента . Тем не менее, в рамках HTML DOM значение текстового узла может быть доступно посредством свойства innerHTML.

Все узлы HTML документа могут быть доступны посредством дерева, при этом их содержимое может быть изменено или удалено, а также можно добавить новые элементы.

Все узлы дерева находятся в иерархических отношениях между собой. Для описания этих отношений используются термины родитель , дочерний элемент и потомок . Родительские узлы имеют дочерние узлы, а дочерние элементы одного уровня называются потомками (братьями или сестрами).

В отношении узлов дерева соблюдаются следующие принципы:

  • Самый верхний узел дерева называется корневым;
  • Каждый узел, за исключением корневого, имеет ровно один родительский узел;
  • Узел может иметь любое число дочерних узлов;
  • Конечный узел дерева не имеет дочерних узлов;
  • Потомки имеют общего родителя.

Программный интерфейс HTML DOM

В рамках DOM модели HTML можно рассматривать как множество узловых объектов . Доступ к ним осуществляется с помощью JavaScript или других языков программирования. Программный интерфейс DOM включает в себя набор стандартных свойств и методов .



Свойства представляют некоторые сущности (например, ), а методы - действия над ними (например, добавить ).

К типичным свойствам DOM относятся следующие:

  • x.innerHTML – внутреннее текстовое значение HTML элемента x ;
  • x. nodeName – имя x ;
  • x.nodeValue – значение x ;
  • x.parentNode – родительский узел для x ;
  • x.childNodes – дочерний узел для x ;
  • x.attributes – узлы атрибутов x.

Узловой объект, соответствующий HTML элементу поддерживает следующие методы:

  • x.getElementById(id) – получить элемент с указанным id ;
  • x.getElementsByTagName(name) – получить все элементы с указанным именем тэга (name);
  • x.appendChild(node) – вставить дочерний узел для x ;
  • x.removeChild(node) – удалить дочерний узел для x.

Для получения текста из элемента

Со значением атрибута id "demo" в HTML документе можно использовать следующий код:

txt = document.getElementById("demo").innerHTML

Тот же самый результат может быть получен по-другому:

txt=document.getElementById("demo").childNodes.nodeValue

В рамках DOM возможны 3 способа доступа к узлам:

  • С помощью метода getElementById(ID). При этом возвращается элемент с указанным ID.
  • С помощью метода getElementsByTagName(name). При этом возвращаются все узлы с указанным именем тэга (в виде индексированного списка). Первый элемент в списке имеет нулевой индекс.
  • Путем перемещения по дереву с использованием отношений между узлами.
  • Для определения длины списка узлов используется свойство length.

    x = document.getElementsByTagName("p");

    for (i = 0; i < x.length; i++)

    document.write(x[i].innerHTML);

    document.write("
    ");

    Присер 6.4

    В данном примере внутрь HTML документа вставляется в виде списка текстовое содержимое всех элементов соответствующих тэгу

    Для навигации по дереву в ближайших окрестностях текущего узла можно использовать следующие свойства:

    • parentNode ;
    • firstChild ;
    • lastChild.

    Для непосредственного доступа к тэгам можно использовать 2 специальных свойства:

    • document.documentElement – для доступа к корневому узлу документа;
    • document.body – для доступа к тэгу .

    Свойства узлов

    В HTML DOM каждый узел является объектом, который может иметь методы (функции) и свойства. Наиболее важными являются следующие свойства:

    • nodeName ;
    • nodeValue ;
    • nodeType.

    Свойство nodeName указывает на имя узла. Это свойство имеет следующие особенности:

  • Свойство nodeName предназначено только для чтения;
  • Свойство nodeName узла элемента точно соответствует имени тэга;
  • Свойство nodeName узла атрибута соответствует имени атрибута;
  • Свойство nodeName текстового узла всегда равно #text
  • Свойство nodeName узла документа всегда равно #document
  • Замечание : nodeName всегда содержит имя тэга HTML элемента в верхнем регистре.

    Свойство nodeValue указывает на значение узла. Это свойство имеет следующие особенности:

    • Свойство nodeValue узла элемента не определено;
    • Свойство nodeValue текстового узла указывает на сам текст;
    • Свойство nodeValue узла атрибута указывает на значение атрибута.

    Свойство nodeType возвращает тип узла. Это свойство предназначено только для чтения:

    Наиболее важными типами узлов являются следующие:

    Изменение HTML элементов

    HTML элементы могут быть изменены с посредством использования JavaScript, HTML DOM и событий.

    В примере 6.5 показано, как можно динамически изменять текстовое содержимое тэга

    Hello World!

    document.getElementById("p1").innerHTML="New text!";

    Пример 6.5.

    Диалоговые элементы

    В JavaScript поддерживается работа со следующими диалоговыми элементами интерфейса:

  • Alert. Применяется для уведомления пользователя, работающего с веб-браузером.
  • 2. Синтаксис:

    alert("сообщение");

  • Confirm. Применяется для выбора пользователем одного из двух вариантов ответа "Да/Нет". Соответственно Confirm возвращает значение true/false.
  • 4. Синтаксис:

    confirm("вопрос");

  • Prompt. Применяется для ввода пользователем значения. При нажатии "OK" возвращается введенное значение, в случае "Cancel" возвращается значение null.
  • 7. Синтаксис:

    prompt("вопрос/запрос","значение по умолчанию");

    Ниже приводится код веб-страницы, в которой пользователь имеет возможность выбрать цвет текста с помощью диалогового элемента

    // здесь будет отображаться текст

    Вы выбрали цвет текста: черный

    // пользователь выбирает цвет текста

    var tcolor = prompt("Выберите цвет текста: red, blue, green, yellow, black","black");

    // задается текст

    document.getElementById("c").innerHTML = "Вы выбрали цвет текста: " + tcolor;

    // задается цвет текста

    document.getElementById("c").style.color = tcolor;

    Сегодня статья опять будет про PHP Simple Html DOM Parser . Даже несмотря на то, что некоторым читателям эта тема могла хорошенько поднадоесть. :) Просто хочется собрать на блоге достаточное количество материала, к которому можно было бы отсылать вопрошающих по емэйлу.

    Итак, навигация по DOM-дереву . Прямо здесь. Прямо сейчас. На примерах. (Так как теоретически она и так описана в инструкции к библиотеке).

    Если вы читаете эту статью, то вам уже известно, что такое DOM-структура, древовидное представление данных, узлы дерева, родитель, потомок и т.д.. Структуру html-документа в виде дерева можно наглядно посмотреть в Firebug-е.

    Там же есть закладка DOM, с содержимым которой советую ознакомиться новичкам. Из структуры, которая там раскрывается, вы наглядно увидите результаты обращения к дочерним элементам, отдельным узлам, свойствам, атрибутам и т.д..


    Но вернемся к PHP Simple Html DOM Parser.

    Для примера возьмем простенький html-код с таблицей из трех столбцов и трех строк.


















    1.1 1.2 1.3
    2.1 2.2 2.3
    3.1 3.2 3.3


    Для тех, кто не любит читать на английском, привожу перевод описания функций:
    mixed $e->children () - возвращает N-ого потомка, если $index указан, или массив всех потомков, если индекс не указан.
    element $e->parent () - возвращает родителя элемента.
    element $e->first_child () - возвращает первого потомка элемента или null, если потомков нет.
    element $e->last_child () - возвращает последнего потомка элемента или null, если потомков нет.
    element $e->next_sibling () - возвращает следующего потомка элемента или null, если таковой не найден.
    element $e->prev_sibling () - возвращает предыдущего потомка элемента или null, если таковой не найден.

    children(N) эквивалентно childNodes(N).

    Следующий код пройдет позволит обойти все ряды и столбцы таблицы:

    $html = str_get_html($html_str);
    foreach ($html->find("table", 0)->children() as $tr) {
    foreach ($tr->children() as $td) {
    echo $td->innertext."; ";
    }
    echo "
    ";
    }

    Если в переменной $html_str приведенный выше код таблицы, то результат будет:
    1.1; 1.2; 1.3;
    2.1; 2.2; 2.3;
    3.1; 3.2; 3.3;
    Тут, думаю, все понятно, проблем возникнуть не должно. Просто проходимся по массивам.
    Следующий способ навигации - с помощью next_sibling. next_sibling и prev_sibling используют для навигации по элементам, находящимся на одном уровне (т.е. имеющих общего родителя). Для примера пройдемся по всем ячейкам первой строки таблицы. Код этой нехитрой операции будет выглядеть так:
    $element = $html->find("table tr td",0);
    while($element) {
    echo $element->innertext."; ";
    $element = $element->next_sibling();
    }
    Результатом будет:
    1.1; 1.2; 1.3;

    Вот, в принципе, и все. Постаралась коротко и ясно, без всякой воды.
    Удачных разработок!
    ___

    Чтобы быть в курсе обновлений блога, можно

    Дерево — одна из наиболее часто используемых в веб-разработке структур данных. Каждый веб-разработчик, который писал HTML -код и загружал его в браузер, создавал дерево, которое называют объектной моделью документа (DOM ).

    Например, статья, которую вы читаете в данный момент, отображается в браузере в виде дерева. Абзацы представлены в виде элементов

    ; элементы

    Вложены в элемент ; а вложен в элемент .

    Вложение данных похоже на генеалогическое древо. Элемент является родительским, является дочерним от него, а элемент является дочерним от элемента .

    В этой статье мы используем два различных метода прохождения дерева: поиск в глубину (DFS ) и поиск в ширину (BFS ). Оба этих типа прохождения подразумевают различные способы взаимодействия с деревом и включают в себя использование структур данных, которые мы рассмотрели в этой серии статей. DFS использует стек, а BFS использует очередь.

    Дерево (поиск в глубину и поиск в ширину)

    В информатике дерево представляет собой структуру, которая задает иерархические данные с узлами. Каждый узел дерева содержит собственные данные и указатели на другие узлы.

    Давайте сравним дерево со структурой организации. Эта структура имеет должность верхнего уровня (корневой узел ), например генерального директора. Ниже этой должности располагаются другие должности, такие как вице-президент (VP ).

    Для представления их подчиненности мы используем стрелки, указывающие от генерального директора на вице-президента. Такие должности, как генеральный директор, является узлами; связи, которые мы обозначили от генерального директора к вице-президенту, являются указателями. Для создания других связей в нашей структуре организации мы повторяем этот процесс — добавляем указатели на другие узлы.

    Давайте рассмотрим DOM . DOM содержит элемент , который является элементом верхнего уровня (корневой узел ). Этот узел указывает на элементы и . Этот процесс повторяется для всех узлов в DOM .

    Одним из преимуществ этой конструкции является возможность вкладывать узлы: элемент

      , например, может содержать множество элементов
    • , вложенных в него; кроме того, каждый элемент
    • может иметь узлы
    • того же уровня.

      Операции дерева

      Любое дерево содержит узлы, которые могут являться отдельными конструкторами дерева, и мы определим операции для обоих конструкторов: Node и Tree .

      Node
      • data — здесь хранятся значения;
      • parent — указывает на родительский элемент узла;
      • children — указывает на следующий узел в списке.
      Tree
      • _root — указывает на корневой узел дерева;
      • traverseDF(callback) — проходит узлы дерева с помощью метода DFS ;
      • traverseBF(callback) — проходит узлы дерева с помощью метода BFS ;
      • contains(data, traversal) — ищет узел дерева;
      • add(data, toData, traverse) — добавляет узел к дереву;
      • remove(child, parent) — удаляет узел дерева.
      Реализация дерева

      Теперь давайте напишем код дерева.

      Свойства Node

      Для реализации мы сначала определим функцию с именем Node , а затем конструктор с именем Tree :

      function Node(data) { this.data = data; this.parent = null; this.children = ; }

      Каждый экземпляр Node содержит три свойства: data , parent и children . Первое свойство используется для хранения данных, связанных с узлом. Второе свойство указывает на один узел. Третье свойство указывает на дочерние узлы.

      Свойства Tree

      Определим наш конструктор для Tree , который в своем теле содержит конструктор Node :

      function Tree(data) { var node = new Node(data); this._root = node; }

      Tree содержит две строки кода. Первая строка создает новый экземпляр Node ; вторая строка назначает node в качестве корневого элемента дерева.

      Для определения Tree и Node требуется лишь несколько строк кода. Но этого достаточно, чтобы помочь нам задать иерархические данные. Чтобы доказать это, давайте используем несколько примеров для создания экземпляра Tree :

      var tree = new Tree("CEO"); // {data: "CEO", parent: null, children: } tree._root;

      Благодаря parent и children мы можем добавлять узлы в качестве дочерних элементов для _root , а также назначать _root в качестве родительского элемента для этих узлов. Другими словами, мы можем задать иерархию данных.

      Методы дерева

      Мы создадим следующие пять методов:

      • traverseDF(callback) ;
      • traverseBF(callback) ;
      • contains(data, traversal) ;
      • add(child, parent) ;
      • remove(node, parent) .

      Так как для каждого метода требуется прохождение дерева, мы сначала реализуем методы, которые определяют различные типы прохождения дерева.

      Метод traverseDF(callback)

      Метод для прохождения дерева с помощью поиска в глубину:

      Tree.prototype.traverseDF = function(callback) { // это рекурсивная и мгновенно вызываемая функция (function recurse(currentNode) { // шаг 2 for (var i = 0, length = currentNode.children.length; i < length; i++) { // шаг 3 recurse(currentNode.children[i]); } // шаг 4 callback(currentNode); // шаг 1 })(this._root); };

      traverseDF(callback) содержит параметр с именем обратного вызова. (callback) — функция, которая будет вызываться позже в traverseDF(callback) .

      Тело traverseDF(callback) включает в себя еще одну функцию с именем recurse . Эта рекурсивная функция, ссылающаяся сама на себя и заканчивающаяся автоматически. Используя шаги, описанные в комментариях к функции recurse , я опишу весь процесс, который использует recurse, чтобы пройти все дерево.

      Вот эти шаги:

      • Мы вызываем recurse с корневым узлом дерева в качестве аргумента. На данный момент currentNode указывает на текущий узел;
      • Входим в цикл for и повторяем его один раз для каждого дочернего узла для currentNode , начиная с первого;
      • Внутри тела цикла for вызываем рекурсивную функцию с дочерним узлом для узла currentNode . Какой конкретно это узел, зависит от текущей итерации цикла for ;
      • Когда currentNode больше не имеет дочерних элементов, мы выходим из цикла for и вызываем (callback ), который мы передали во время вызова traverseDF(callback) .

      Шаги 2 (завершающий себя ), 3 (вызывающий себя ) и 4 (обратный вызов ) повторяются до прохождения каждого узла дерева.

      Рекурсия — это очень сложная тема. Для ее понимания можно поэкспериментировать с нашей текущей реализацией traverseDF(callback ) и попытаться понять, как это работает.

      Следующий пример демонстрирует проход по дереву с помощью traverseDF(callback) . Для этого примера я сначала создам дерево. Подход, который я буду использовать, не является идеальным, но он работает. Лучше было бы использовать метод add(value) , который мы реализуем в шаге 4:

      var tree = new Tree("one"); tree._root.children.push(new Node("two")); tree._root.children.parent = tree; tree._root.children.push(new Node("three")); tree._root.children.parent = tree; tree._root.children.push(new Node("four")); tree._root.children.parent = tree; tree._root.children.children.push(new Node("five")); tree._root.children.children.parent = tree._root.children; tree._root.children.children.push(new Node("six")); tree._root.children.children.parent = tree._root.children; tree._root.children.children.push(new Node("seven")); tree._root.children.children.parent = tree._root.children; /* создаем следующее дерево one ├── two │ ├── five │ └── six ├── three └── four └── seven */

      Теперь, давайте вызовем traverseDF(callback) :

      tree.traverseDF(function(node) { console.log(node.data) }); /* выводим следующие строки на консоль "five" "six" "two" "three" "seven" "four" "one" */

      Метод traverseBF(callback)

      Метод для прохождения дерева по ширине. Разница между поиском в глубину и поиском в ширину заключается в последовательности прохождения узлов дерева. Чтобы проиллюстрировать это, давайте используем дерево, которое мы создали для реализации метода traverseDF(callback) :

      /* tree one (depth: 0) ├── two (depth: 1) │ ├── five (depth: 2) │ └── six (depth: 2) ├── three (depth: 1) └── four (depth: 1) └── seven (depth: 2) */

      Теперь давайте передадим в traverseBF(callback) тот же обратный вызов, который мы использовали для traverseDF(callback) :

      tree.traverseBF(function(node) { console.log(node.data) }); /* выводим следующие строки на консоль "one" "two" "three" "four" "five" "six" "seven" */

      Выводим строки на консоль, и диаграмма нашего дерева покажет нам картину, отображающую принцип поиска в ширину. Начните с корневого узла; затем пройдите один уровень и посетите каждый узел этого уровня слева направо. Повторите этот процесс до тех пор, пока все уровни не будут пройдены. Реализуем код, с помощью которого этот пример будет работать:

      Tree.prototype.traverseBF = function(callback) { var queue = new Queue(); queue.enqueue(this._root); currentTree = queue.dequeue(); while(currentTree){ for (var i = 0, length = currentTree.children.length; i < length; i++) { queue.enqueue(currentTree.children[i]); } callback(currentTree); currentTree = queue.dequeue(); } };

      Определение traverseBF(callback) я объясню пошагово:

      • Создаем экземпляр Queue ;
      • Добавляем узел, который вызывается traverseBF(callback) для экземпляра Queue ;
      • Объявляем переменную currentNode и инициализируем ее для node , который мы только что добавили в очередь;
      • Пока currentNode указывает на узел, выполняем код внутри цикла while ;
      • Используем цикл for для прохождения дочерних узлов currentNode ;
      • В теле цикла for добавляем каждый дочерний узел в очередь;
      • Принимаем currentNode и передаем его в качестве аргумента для callback ;
      • Устанавливаем в качестве currentNode узел, удаляемый из очереди;
      • До тех пор, пока currentNode указывает на узел, должен быть пройден каждый узел дерева. Для этого повторяем шаги с 4 по 8.
      Метод contains(callback, traversal)

      Определим метод, который позволит нам находить конкретное значение в дереве. Чтобы использовать любой из методов прохождения дерева, я устанавливаю для contains(callback, traversal) два принимаемых аргумента: данные, которые мы ищем, и тип прохождения:

      Tree.prototype.contains = function(callback, traversal) { traversal.call(this, callback); };

      В теле contains(callback, traversal) для передачи this и callback мы используем метод с именем call . Первый аргумент связывает traversal с деревом, для которого вызывается contains(callback, traversal) ; второй аргумент — это функция, которая вызывается на каждом узле дерева.

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

      // дерево - это пример корневого узла tree.contains(function(node){ if (node.data === "two") { console.log(node); } }, tree.traverseBF);

      Метод add(data, toData, traversal)

      Теперь у нас есть метод для поиска узла в дереве. Давайте определим метод, который позволит нам добавить узел к конкретному узлу:

      Tree.prototype.add = function(data, toData, traversal) { var child = new Node(data), parent = null, callback = function(node) { if (node.data === toData) { parent = node; } }; this.contains(callback, traversal); if (parent) { parent.children.push(child); child.parent = parent; } else { throw new Error("Cannot add node to a non-existent parent."); } };

      add(data, toData, traversal) определяет три параметра. data используется для создания нового экземпляра узла. toData — используется для сравнения каждого узла в дереве. Третий параметр, traversal — это тип прохождения дерева, используемый в этом методе.

      В теле add(data, toData, traversal) мы объявляем три переменные. Первая переменная, child , инициализируется как новый экземпляр Node . Вторая переменная, parent , инициализируется как null ; но позже она будет указывать на любой узел в дереве, который соответствует значению toData . Переназначение parent выполняется в третьей переменной — callback .

      callback — это функция, которая сравнивает toData со свойством data каждого узла. Если узел удовлетворяет условию оператора if , он назначается в качестве parent .

      Само сравнение каждого узла с toData осуществляется внутри add(data, toData, traversal) . Тип прохождения и callback должны передаваться в качестве аргументов add(data, toData, traversal) .

      Если parent в дереве не существует, мы помещаем child в parent.children ; мы также назначаем в качестве parent родительский элемент для child , иначе выдается ошибка.

      Давайте используем add(data, toData, traversal) в нашем примере:

      var tree = new Tree("CEO"); tree.add("VP of Happiness", "CEO", tree.traverseBF); /* наше дерево "CEO" └── "VP of Happiness" */

      Вот более сложный пример использования add(data, toData, traversal) :

      var tree = new Tree("CEO"); tree.add("VP of Happiness", "CEO", tree.traverseBF); tree.add("VP of Finance", "CEO", tree.traverseBF); tree.add("VP of Sadness", "CEO", tree.traverseBF); tree.add("Director of Puppies", "VP of Finance", tree.traverseBF); tree.add("Manager of Puppies", "Director of Puppies", tree.traverseBF); /* дерево "CEO" ├── "VP of Happiness" ├── "VP of Finance" │ ├── "Director of Puppies" │ └── "Manager of Puppies" └── "VP of Sadness" */

      Метод remove(data, fromData, traversal)

      Для полной реализации Tree нам нужно добавить метод с именем remove(data, fromData, traversal) . Аналогично удалению узла из DOM этот метод будет удалять узел и все его дочерние узлы:

      Tree.prototype.remove = function(data, fromData, traversal) { var tree = this, parent = null, childToRemove = null, index; var callback = function(node) { if (node.data === fromData) { parent = node; } }; this.contains(callback, traversal); if (parent) { index = findIndex(parent.children, data); if (index === undefined) { throw new Error("Node to remove does not exist."); } else { childToRemove = parent.children.splice(index, 1); } } else { throw new Error("Parent does not exist."); } return childToRemove; };

      Так же, как add(data, toData, traversal) , метод удаления проходит все дерево, чтобы найти узел, который содержит второй аргумент, равный в настоящее время fromData . Если этот узел найден, то parent указывает на него в первом операторе if .

      — младшим ). Все элементы дерева являются потомками корня, а тот является их предком . При этом все элементы и тексты, образующие их содержимое, являются узлами дерева документа.

      Каждый элемент данного дерева соответствует элементу 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 1 описывает базовое представление XML- и HTML-документов в виде деревьев объектов;
      • DOM 2 расширяет базовые интерфейсы DOM 1 и добавляет к ним поддержку событий и стилей;
      • DOM 3 описывает загрузку и синтаксический анализом документов, а также их отображение и форматирование.

      Учитывая текущее состояние вещей, мы рассматриваем здесь только DOM 2 (и содержащуюся в нем DOM 1). DOM 2 состоит из следующих групп взаимосвязанных интерфейсов:

      • Core — базовые интерфейсы, определяющие представление любого XML-документа в виде дерева;
      • View — интерфейсы, описывающие возможные отображения документа;
      • Event — интерфейсы, определяющие порядок генерации и обработки событий;
      • Style — интерфейсы, определяющие применение к документам таблиц стилей;
      • Traversal & Range — интерфейсы, определяющие прохождение дерева документа и манипулирование областями его содержимого;
      • HTML — интерфейсы, определяющие представление HTML-документа в виде дерева.

      Начнем с определения базовых интерфейсов, лежащих в основе всех дальнейших спецификаций.

      4.2.2. Основные понятия

      DOM 2 Core представляет XML-документы в виде деревьев, состоящих из узлов, которые, в свою очередь, также являются объектами и реализуют более специализированные интерфейсы. Одни типы узлов могут иметь детей, т. е. сами являться поддеревьями, другие являются листьями, т. е. детей не имеют. В следующей таблице сведены все возможные типы узлов абстрактного документа; для каждого типа узлов перечислены те узлы, которые могут быть его детьми. О понятиях, соответствующих перечисленным узлам, см. описание структуры XML-документа .

      Таблица 4.1. Структура дерева документаИнтерфейс Описание Дети
      Документ Element (не более одного), ProcessingInstruction , Comment , DocumentType (не более одного)
      Фрагмент документа Element , ProcessingInstruction , Comment , Text , CDATASection , EntityReference
      DocumentType Тип документа детей не имеет
      EntityReference Ссылка на раздел Element , ProcessingInstruction , Comment , Text , CDATASection , EntityReference
      Element Элемент Element , ProcessingInstruction , Comment , Text , CDATASection , EntityReference
      Attr Атрибут Text , EntityReference
      ProcessingInstruction Директива XML детей не имеет
      Comment Комментарий детей не имеет
      Text Текст детей не имеет
      CDATASection Секция CDATA детей не имеет
      Entity Раздел Element , ProcessingInstruction , Comment , Text , CDATASection , EntityReference
      Notation Нотация детей не имеет

      Кроме того, DOM 2 Core содержит спецификацию интерфейсов NodeList (упорядоченные списки узлов, доступных по номеру в списке) и NamedNodeMap (неупорядоченные списки узлов, доступных по своему имени). Эти объекты являются живыми , т. е. любое изменение документа автоматически влечет изменение всех связанных с ним списков.

      Следует подчеркнуть, что DOM 2 Core содержит два набора интерфейсов, каждый из которых обеспечивает полный доступ ко всем элементам документа. Первый набор представляет объектно-ориентированный подход со следующей иерархией наследования: документ — составляющие его элементы — их атрибуты и текстовое содержимое. При таком рассмотрении дерева документа мы говорим о иерархии объектов . Второй подход построен по принципу "все есть узлы (Nodes)". Здесь все составляющие документа рассматриваются как равноправные узлы его дерева, и мы можем говорить только о иерархии узлов . Таким образом, DOM 2 по своей сути является избыточной, но предоставляет нам возможность в зависимости от задачи рассматривать документ тем или иным способом.

      Все интерфейсы DOM 2 Core подразделяются на основные (fundamental) и дополнительные (extended). Основными являются интерфейсы , , , , Node , NodeList , NamedNodeMap , CharacterData , Attr , Element , Text и Comment . Эти интерфейсы должны поддерживаться всеми реализациями DOM, как для XML-, так и для HTML-документов. Дополнительные интерфейсы ориентированы на XML-документы, поэтому реализации DOM для HTML могут их не поддерживать. К ним относятся CDATASection , DocumentType , Notation , Entity , EntityReference и ProcessingInstruction .

      В целях независимости от языка и платформы DOM определяет следующие типы:

      DOMString Текстовая строка, состоящая из символов Unicode в формате UTF-16 . В JavaScript и в Java реализуется типом String. DOMTimeStamp Дата и время в приемлемом для конкретного языка формате. Например, в JavaScript это будет объект Date , а в Java — целое число типа long , содержащее количество миллисекунд.

      Ниже приведено краткое описание всех интерфейсов 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. Стандартные исключения DOMНазвание Значение Описание Модель
      INDEX_SIZE_ERR 1 Выход индекса за пределы допустимого диапазона. DOM 1
      DOMSTRING_SIZE_ERR 2 Заданный текст не может быть приведен к типу . DOM 1
      HIERARCHY_REQUEST_ERR 3 Попытка вставить узел в ненадлежащее место дерева. DOM 1
      WRONG_DOCUMENT_ERR 4 Недопустимый тип документа. DOM 1
      INVALID_CHARACTER_ERR 5 Встречен недопустимый символ. DOM 1
      NO_DATA_ALLOWED_ERR 6 Узел не содержит данных. DOM 1
      NO_MODIFICATION_ALLOWED_ERR 7 Попытка недопустимой модификации объекта. DOM 1
      NOT_FOUND_ERR 8 Обращение к несуществующему узлу. DOM 1
      NOT_SUPPORTED_ERR 9 Параметр или операция не реализованы. DOM 1
      INUSE_ATTRIBUTE_ERR 10 Попытка добавления атрибута, который уже существует. DOM 1
      INVALID_STATE_ERR 11 Обращение к несуществующему объекту. DOM 1
      SYNTAX_ERR 12 Синтаксическая ошибка. DOM 2
      INVALID_MODIFICATION_ERR 13 Попытка изменения типа объекта. DOM 2
      NAMESPACE_ERR 14 Попытка создания или изменения объекта, не соответствующая пространству имен XML . DOM 2
      INVALID_ACCESS_ERR 15 Параметр или операция не поддерживаются объектом. DOM 2
      Поддержка : Нестандартная реализация. Поддерживается часть кодов ошибок. 4.2.4. Описание реализации: интерфейс DOMImplementation Поддержка : Только для XML-документов (XMLDOMImplementation). Соответствует DOM 1.

      Интерфейс DOMImplementation содержит методы, выполнение которых не зависит от конкретной объектной модели документа. Он доступен через свойство объекта .

      Метод createCSSStyleSheet Синтаксис : объект .createCSSStyleSheet(title, media) Аргументы : title, media — выражения типа Результат : новый объект CSSStyleSheet Исключения : SYNTAX_ERR Поддержка

      Метод createCSSStyleSheet создает новый объект CSSStyleSheet и возвращает указатель на него. Этот метод должен поддерживаться только теми реализациями DOM, которые поддерживают CSS. Объект создается вне контекста документа; DOM 2 не позволяет поключить вновь созданную таблицу стилей к документу. Аргумент title задает титул таблицы стилей, а media — список устройств отображения через запятую.

      Метод createDocument Синтаксис : объект .createDocument(namespaceURI, qualifiedName, doctype) Аргументы : namespaceURI, qualifiedName — выражения типа doctype — выражение типа DocumentType Результат : новый объект Исключения : INVALID_CHARACTER_ERR, NAMESPACE_ERR, WRONG_DOCUMENT_ERR Поддержка : Не поддерживается. Не поддерживается.

      Метод createDocument создает новый объект и возвращает указатель на него. Он предназначен для создания XML-документов и для HTML-документов может не поддерживаться. Аргумент namespaceURI задает URI пространства имен корневого элемента документа, qualifiedName — его ограниченное имя , а doctype — тип создаваемого документа (может иметь значение null ).

      Метод createDocumentType Синтаксис : объект .createDocumentType(qualifiedName, publicId, systemId) Аргументы : qualifiedName, publicId, systemId — выражения типа Результат : новый узел DocumentType Исключения Поддержка : Не поддерживается. Не поддерживается.

      Метод createDocumentType создает пустой узел типа DocumentType и возвращает указатель на него. Он предназначен для XML-документов и для HTML-документов может не поддерживаться. Аргумент qualifiedName задает ограниченное имя создаваемого типа документа, publicId — публичный идентификатор внешнего раздела , а systemId — системный идентификатор внешнего раздела .

      Метод hasFeature Синтаксис : объект .hasFeature(feature, version) Аргументы : feature, version — выражения типа Результат : логическое значение Исключения : нет Поддержка

      Метод hasFeature возвращает true , если реализация DOM поддерживает указанное свойство, и false в противном случае. Имя свойства (в любом регистре) задается аргументом feature ; оно должно соответствовать правилам образования имен XML . Аргумент version задает имя версии проверяемого свойства. Если он не задан, то возвращается true , если поддерживается хотя бы какая-то версия данного свойства.

      В 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-документу. Он является основой для доступа к содержанию документа и для создания его составляющих.

      Методы интерфейса Document Свойство Модель Описание
      DOM 1 Создает атрибут.
      DOM 2 Создает атрибут с учетом пространства имен.
      DOM 1 Создает секцию CDATA.
      DOM 1 Создает комментарий.
      DOM 1 Создает новый фрагмент документа.
      DOM 1 Создает новый элемент.
      DOM 2 Создает элемент с с учетом пространства имен.
      DOM 1 Создает ссылку на раздел.
      DOM 2 Создает новый объект Event .
      DOM 1 Создает директиву.
      DOM 1 Создает новый текстовый узел.
      DOM 2 Возвращает элемент с заданным идентификатором.
      DOM 1 Возвращает коллекцию всех элементов, имеющих данный тег.
      DOM 2 Возвращает коллекцию всех элементов, имеющих данный тег с учетом пространства имен.
      DOM 2 Импортирует узел из другого документа.
      Свойство doctype Синтаксис : документ .doctype Изменяемое : нет Поддержка

      Свойство doctype возвращает тип данного документа (типа DocumentType). Для HTML-документов и для XML-документов, не имеющих декларации типа документа , возвращается null .

      Свойство documentElement Синтаксис : документ .documentElement Изменяемое : нет Поддержка : Соответствует стандарту. Соответствует стандарту.

      Свойство documentElement возвращает корневой элемент данного документа (типа Element). Для HTML-документов возвращается элемент HTML . Пример: оператор

      Alert(document.documentElement.tagName);

      выведет на экран строку HTML .

      Свойство implementation Синтаксис : документ .implementation Изменяемое : нет Поддержка : Только для XML-документов. Соответствует DOM 1.

      Свойство implementation возвращает объект типа , описывающий данную реализацию DOM.

      Свойство styleSheets Синтаксис : документ .styleSheets Изменяемое : нет Поддержка : Только для HTML-документов. Соответствует стандарту.

      Свойство styleSheets возвращает объект типа StyleSheetList , соответствующий списку внешних и внутренних таблиц стилей документа. Это свойство поддерживается только теми реализациями DOM, которые поддерживают динамические таблицы стилей .

      Метод createAttribute Синтаксис : документ .createAttribute(name) Аргументы : name — выражение типа Результат : новый объект 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 — выражения типа Результат : новый объект Attr Исключения : INVALID_CHARACTER_ERR, NAMESPACE_ERR Поддержка : Не поддерживается. Не поддерживается.

      Метод createAttributeNS создает новый объект типа Attr и возвращает указатель на него. Он предназначен для XML-документов и для HTML-документов может не поддерживаться. Аргумент namespaceURI задает URI пространства имен , а qualifiedName — ограниченное имя создаваемого атрибута в этом пространстве имен. Созданный объект типа Attr имеет следующие атрибуты:

      В дальнейшем созданный атрибут может быть присвоен какому-либо элементу методом Element.setAttributeNode .

      Метод createCDATASection Синтаксис : документ .createCDATASection(data) Аргументы : data — выражение типа Результат : новый объект CDATASection Исключения : NOT_SUPPORTED_ERR Поддержка : Соответствует стандарту. Соответствует стандарту.

      Метод createCDATASection создает новый объект типа CDATASection и возвращает указатель на него. Он предназначен только для XML-документов; попытка вызвать его в HTML DOM генерирует исключение NOT_SUPPORTED_ERR . Аргумент data задает содержимое создаваемой. Пример создания секции 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 — выражение типа Результат : новый объект Comment Исключения : нет Поддержка : Только для XML-документов. Соответствует стандарту.

      Метод createComment создает новый объект типа Comment и возвращает указатель на него. Аргумент data задает содержимое создаваемого комментария. Пример создания комментария в Gecko:

      Var root = document.documentElement.firstChild; var comm = document.createComment("Это комментарий."); root.appendChild(comm);

      Метод createDocumentFragment Синтаксис : документ .createDocumentFragment() Результат : новый объект Исключения : нет Поддержка : Только для XML-документов. Соответствует стандарту.

      Метод createDocumentFragment создает новый пустой объект типа и возвращает указатель на него. Пример создания фрагмента документа в Gecko:

      Var elem = document.documentElement.firstChild; var o = document.createDocumentFragment(); elem.appendChild(o);

      Метод createElement Синтаксис : документ .createElement(tagName) Аргументы : tagName — выражение типа Результат : новый объект Element Исключения : INVALID_CHARACTER_ERR Поддержка : Соответствует стандарту (см. прим. 2). Соответствует стандарту.

      Метод createElement создает новый объект типа Element и возвращает указатель на него. Аргумент tagName задает тег создаваемого элемента. У нового объекта атрибут nodeName имеет значение tagName , а атрибуты localName , prefix и namespaceURI равны null . Если объект имеет атрибуты со значениями по умолчанию, то автоматически создаются соответствующие узлы Attr и присоединяются к элементу.

      Примечания :

    • Для XML названия тегов элементов чувствительны к регистру. Для HTML они могут быть заданы в любом регистре, но при создании элемента преобразуются в прописные буквы.
    • Попытка создания в Internet Explorer элемента FRAME или IFRAME приводит либо к неустранимой ошибке обозревателя, либо, как минимум, к полному разрушению объектного дерева документа.
    • Тип узла Поведение
      ATTRIBUTE_NODE Атрибут ownerElement равен null , specified равен true . Все потомки исходного узла рекурсивно копируются в новый узел Attr , независимо от значения deep .
      DOCUMENT_FRAGMENT_NODE Если deep равен true , то производится импорт заданного фрагмента документа; в противном случае создается пустой узел .
      DOCUMENT_NODE , DOCUMENT_TYPE_NODE Не могут быть импортированы.
      ELEMENT_NODE В новый узел Element копируются все атрибуты исходного узла, кроме заданных по умолчанию в исходном документе. Затем создаются атрибуты по умолчанию, принятые в данном документе для элементов с этим именем. Если deep равен true , то производится импорт всего поддерева исходного элемента.
      ENTITY_NODE Узлы Entity DocumentType
      ENTITY_REFERENCE_NODE Копируется только сам узел EntityReference , независимо от значения deep . Если в нашем документе есть определение раздела с данным именем, то оно заносится в новый узел.
      NOTATION_NODE Узлы Notation могут быть импортированы, но в DOM 2 объект DocumentType доступен только для чтения, поэтому импорт таких узлов не имеет смысла.
      PROCESSING_INSTRUCTION_NODE Копируются значения атрибутов target и data исходного узла.
      TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE Копируются значения атрибутов data и length исходного узла.