11. Объектная модель документа. Ранняя упрощенная модель

Что такое DOM

HTML-документ браузер отображает в своём окне, которое описывается объектом window.

Объект window является родителем всех других объектов, ссылки на которые содержатся в соответствующих свойствах объекта window.

Объект Краткое описание объекта
window.navigator Свойства и методы для доступа к данным, описывающих браузер.
window.location Свойства и методы для работы с URL открытого окна.
window.history Свойства и методы для работы со списком URL документов, которые загружались в окно браузера.
window.screen Свойства и методы для доступа к параметрам и настройкам экрана монитора.
window.document Объектная модель документа — содержит иерархию объектов, построенных браузером по HTML-коду, загруженному в окно документа.

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

Когда браузер загружает страницу, он анализирует её и строит DOM. После того как документ полностью загружен и DOM построен, в окне возникает событие onload. Теперь можно обращаться к свойствам и методам объектов DOM.

Какой бывает DOM

Уровень 0 (DOM Level 0) == ранняя версия DOM

Ранняя упрощенная объектная модель документа сложилась де-факто в результате «военного» этапа развития браузеров (до того, как консорциумом W3C разработал стандарт DOM уровня 1).

Эта упрощенная версия DOM сохранилась для обратной совместимости, и её поддерживают (и будут поддерживать) все современные браузеры.

Уровень 1 (DOM Level 1)

В 1998 году консорциум W3C опубликовал стандарт DOM Level 1.

Уровень 2 (DOM Level 2)

В 2000 году была стандартизована модель DOM Level 2.

Уровень 3 (DOM Level 3)

В 2009 году был официально объявлен стандарт DOM Level 3, но новые функциональные возможности третьего уровня пока ещё не поддерживаются браузерами.

Поддержка браузерами

В момент написания этих строк все браузеры прекрасно поддерживают DOM Level 2, кроме, разумеется, IE, хотя последние версии этого браузера (7, 8 и 9) всё ближе подбираются к стандартам. Довольно популярный ещё IE-6 в основном совместим с DOM Level 1, но практически не поддерживает DOM Level 2.

К счастью IE-6 поддерживает важный метод document.getElementById(id) из набора DOM Level 2, который позволяет получить ссылку на объект, построенный для элемента с заданным идентификатором id. Этот метод, кажется, поддерживает даже IE-5, хотя этот браузер сегодня можно списать со счетов.

Рассмотрение DOM начнём с ранней версии (Level 0) — её поддерживают все браузеры, и работу с этой моделью можно совмещать с интерфейсами моделей от W3C (уровня 1 и выше).

Уровень 0 (DOM Level 0) == ранняя версия DOM

В рамках ранней модели объект document имеет свойства, которые ссылаются на объекты, подобные массивам (их часто называют коллекциями): anchors, applets, forms, images и links. В настоящее время практическую ценность представляют только последние три коллекции.

Объект Краткое описание коллекции
document.anchors Коллекция содержит объекты класса Anchor, представляющие якорные элементы документа, то есть элементы вида:
<A name="top"></A>

Эти элементы используются (вернее, использовались) для внутристраничных переходов по ссылкам вида:

<A href="#top">Переход на начало документа</A>

Таким образом, document.anchors[0] содержит ссылку на объект, соответствующий первому якорному элементу страницы.

document.applets Коллекция содержит объекты класса Applet, представляющие Java-апплеты документа.
document.forms Коллекция содержит объекты класса Form, представляющие элементы FORM документа. Каждый объект Form обладает собственным свойством-коллекцией elements, в котором представлены объекты, построенные для элементов формы (INPUT, SELECT и TEXTAREA).

Пусть первая форма задана в HTML следующим кодом:


<FORM name="forma">
  <INPUT name="key1" type="button" value="Кнопка 1">
  <INPUT name="key2" type="button" value="Кнопка 2">
</FORM>

Тогда:


document.forms[0] // доступ к первому объекту Form
document.forms[0].elements[0] // доступ к объекту,
                              // построенного для 1-го INPUT
document.forms[0].elements[1] // доступ к объекту,
                              // построенного для 2-го INPUT
document.forms[0].elements[0].value; // строка "Кнопка 1"
document.forms[0].elements[1].value; // строка "Кнопка 2"

Использовать индексы для доступа к объектам, построенным для элементов HTML-кода не слишком удобно. Но можно воспользоваться следующим правилом, которое действует для элементов FORM, IMG, APPLET (но не действует для элементов A):

Установка атрибута name в открывающих тегах элементов FORM, IMG и APPLET позволяет обращаться к соответствующим объектам, как к именованным свойствам объекта document.

Таким образом, возможны следующие формулы доступа:


document.forma // доступ к объекту, построенного для 
               // элемента <FORM name="forma">...</FORM>
document.forma.key1 // доступ к объекту, построенного для
                    // элемента с именем key1, расположенным
                    // в элементе с именем forma
document.forma.key1.value // доступ к значению атрибута value
alert(document.forma.key1.value); // "Кнопка 1" 
document.forma.key1.value = "Моя кнопка"; // новая надпись
document.images

Коллекция содержит объекты класса Image, представляющие элементы IMG документа. Свойство src объекта Image содержит значение одноименного атрибута элемента IMG и доступно для чтения/записи.


document.images[0].src  // доступ к свойству src 
                        // 1-ой картинки документа
document.images.pic.src // доступ к свойству src картинки,
                        // для которой установлен атрибут
                        // name="pic".

Можно опускать имя коллекции в формулах доступа, если используется значение атрибута name и обращаться к именованным элементам как к свойствам объекта document:


document.pic     // доступ к картинке, для которой 
                 // установлен атрибут name="pic".
document.pic.src // доступ к свойству src картинки, для
                 // которой установлен атрибут name="pic".
document.links Коллекция содержит объекты класса Link, представляющие гипертекстовые ссылки документа. Свойство href объекта Link соответствует значению одноименного атрибута элемента A. Например, document.links[0].href дает доступ к значению атрибута href первой ссылки на странице.

Замечание 1. Заметим ещё раз, что именованный доступ вида document.name возможен только для элементов FORM, IMG и APPLET.

Замечание 2. Пусть имеется несколько элементов с одинаковыми значениями атрибута name, например, name="n". Тогда свойство document.n будет представлять собой не один объект, а массив объектов, сформированный для этих элементов, в порядке следования их в HTML-коде.

Поработайте со следующими примерами:

Пример Комментарий
Пример 1 Запустите пример и разберитесь в устройстве его кода.
Пример 2 Этот пример работает аналогично первому, но обращение к элементам формы выполняется не по индексам, а по именам.
Пример 3 Пример демонстрирует работу с коллекцией images. В нём создается картинка, чувствительная к указателю мыши.

Обработка событий в ранней версии DOM

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

Привязка событий в ранней модели DOM выполняется:

  1. При помощи «событийных» атрибутов элементов HTML.
  2. При помощи соответствующих свойств объектов, построенных для элементов HTML.

Так, если нужно обработать щелчок мыши на картинке, привязать обработчик (например, функцию work) можно при помощи атрибута onclick в HTML-коде:


<IMG name="pic" onclick="work()"
     src="prav30.gif" width=140 height=150 border=1
     alt="Профессор" title="Профессор">

А можно воспользоваться свойством onclick объекта, построенного в DOM для элемента IMG с name="pic":


document.image["pic"].onclick = work;

Или так:


document.pic.onclick = work;

Заметим ещё раз, что именованный доступ к объекту, построенного для элемента с name="xyz" в виде document.xyz возможен только для элементов FORM, IMG и APPLET. Для других элементов в рамках ранней модели (например, для абзаца) остаётся только одна возможность привязать событие — при помощи событийного атрибута в открывающем теге самого элемента.

Ключевое слово this в обработчиках

Обработчик является методом объекта, построенного для некоторого элемента HTML.

Пусть, например, в HTML-коде есть элемент IMG:


<IMG name="pic" 
     src="prav30.gif" width=140 height=150 border=1
     alt="Профессор" title="Профессор">

Этому элементу в DOM соответствует объект класса Image. Доступ к этому объекту можно получить так:


document.pic // Это ссылка на объект Image, 
             // построенный для IMG с name="pic"

Обработчик является методом этого объекта. Если мы записываем инструкцию


document.pic.onclick = work;

то тем самым определяем метод объекта document.pic. Функция work будет вызываться как метод объекта document.pic, значит, ключевое слово this в ней будет относиться к этому объекту.

Получаем правило:

Ключевое слово this в обработчике события относится к объекту, построенному для HTML-элемента, к которому это событие привязано.

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

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


<IMG src="prav30.gif" width=140 height=150 border=1
     onmouseover = "this.src='prav31.gif'"
     onmouseout  = "this.src='prav30.gif'"
     alt="Профессор" title="Профессор">

В этом коде строки, заданные как значения событийных атрибутов, неявно преобразуются в безымянные функции, ссылки на которые автоматически записываются в свойства onmouseover и onmouseout соответствующего объекта класса Image.

Поэтому ключевое слово this, фигурирующее в атрибутах, ссылается на объект Image, построенного для текущего элемента IMG. Соответственно, this.src ссылается на свойство, соответствующее одноименному атрибуту.

Отметим, что все атрибуты преобразуются в одноименные свойства построенного объекта. Например, свойство this.width для данного IMG имеет числовое значение 140, а значение свойства this.alt равно строке "Профессор".

Метод document.getElementById

Как мы видели, в ранней модели DOM регистрировать обработчики для всех элементов, кроме FORM, IMG и APPLET, можно только при помощи событийных атрибутов в коде HTML.

На сегодняшний день — это плохая практика. Нужно стремиться отделить поведение страницы от её содержания и представления. То есть весь JavaScript-код нужно стараться записывать в отдельном файле.

В рамках ранней модели DOM так не получится, ибо обработчик события, как свойство объекта, мы можем задать только для элементов FORM, IMG и APPLET, а, скажем, прикрепить обработчик к абзацу таким способом нельзя.

Однако, если к программному интерфейсу ранней модели DOM добавить метод getElementById объекта document (относящийся к DOM уровня 2), ситуация исправляется.

Метод getElementById имеет следующий формат:

document.getElementById(id)

Метод возвращает ссылку на объект, построенный в DOM для элемента с указанным идентификатором, или null, если такого элемента в HTML-коде нет.

К счастью, метод getElementById понимают все современные браузеры (и даже IE).

Пример Комментарий
Пример 4 В этом примере обработчик привязан к заголовку H1 с использованием метода getElementById.

Свойство innerHTML

Это свойство не входит в стандарт W3C. Первоначально оно появилось в IE 4, а затем было реализовано во всех браузерах. То есть innerHTML понимают (и будут понимать) все современные браузеры.

При помощи метода document.getElementById(id) можно получить доступ к любому элементу на экране (вернее, к объекту, для него построенного, но это практически то же самое).


element = document.getElementById(id); // Доступ к элементу 
                                       // с идентификатором id

У каждого такого объекта есть свойство innerHTML, которое позволяет читать и менять содержимое элемента.


element.innerHTML // Строка, содержащая HTML-код внутри элемента 
                  // (за исключением открывающего и закрывающего тега
                  // самого элемента).
                  // Запись в это свойство строки, содержащей HTML-код,
                  // приводит к изменению объектной модели и, 
                  // соответственно, изменению представления документа
                  // на экране.

Метод document.getElementById и свойство element.innerHTML, добавленные к ранней модели DOM, позволяют достаточно комфортно работать, создавая интерактивные динамические документы.

Пример Комментарий
Пример 5 Исследуйте работу этой страницы и разберитесь в коде, который её обслуживает. Обратите внимание — заголовкам H1 и H2 назначены одни и те обработчики событий onmouseover и onmouseout. Обработчик определяет источник события по ключевому слову this.

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

Раздел события содержит справочник по событиям, возникающим при работе HTML-кода.

В разделе задания собраны контрольные задания (с решениями).