Главная страница   /   2.8. Объектная модель документа (DOM) (Pro jQuery

Pro jQuery

Pro jQuery

Адам Фриман

2.8. Объектная модель документа (DOM)

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

Примечание

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

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

Заметка 

Использование DOM подразумевает использование JavaScript. Если вам надо освежить в памяти основы языка JavaScript, смотрите главу 4.

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

Использование DOM

Объект JavaScript, определяющий базовый функционал, доступный в DOM для всех типов элементов, называется HTMLElement. Объект HTMLElement определяет свойства и методы, общие для всех типов HTML элементов, включая свойства, показанные в таблице 2-3.

Таблица 2-3: Основные свойства HTMLElement
 
Свойство Описание Возвращает
className получает или определяет список классов, к которым принадлежит элемент string
id получает или определяет значение атрибута id string
lang получает или определяет значение атрибута lang string
tagName возвращаем имя тега (указывая на тип элемента) string

Есть много различных свойств. Их точное число зависит от того, с какой версией HTML вы работаете, а этих четырех мне достаточно, чтобы показать базовые функции DOM.

DOM использует объекты, полученные из HTMLElement, чтобы представить уникальные характеристики каждого типа элементов. Например, объект HTMLImageElement используется в DOM для представления элемента img. И этот объект определяет свойство src, которое соответствует атрибуту src элемента img. Я сейчас не буду вдаваться в детали по всем объектам, определяющим конкретные элементы, но вы без сомнения можете работать со свойствами, соотносящимися к атрибутам элементов.

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

Таблица 2-4: Методы объекта Document для нахождения элементов
 
Свойство Описание Возвращает
getElementById(<id>) возвращает элемент с конкретным значением id HTMLElement
getElementsByClassName(<class>) возвращает элементы с конкретным значением class HTMLElement[]
getElementsByTagName(<tag>) возвращает элементы конкретного типа HTMLElement[]
querySelector(<selector>) возвращает элемент, который соответствует конкретному селектору CSS HTMLElement
querySelectorAll(<selector>) возвращает все элементы, которые соответствует конкретному селектору CSS HTMLElement[]

Еще раз повторю, что я выбрал лишь те методы, которые будут полезны для данной книги. Последние два метода, описанные в таблице, используют CSS селекторы, о которых я расскажу в главе 3. В листинге 2-15 показано, как в документе можно использовать объект Document для поиска элементов конкретного типа.

Листинг 2-15: Поиск элементов в DOM
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<style>
h1
{
width: 700px;
border: thick double black;
margin-left: auto;
margin-right: auto;
text-align: center;
font-size: x-large;
padding: .5em;
color: darkgreen;
background-image: url("border.png");
background-size: contain;
margin-top: 0;
}
.dtable
{
display: table;
}
.drow
{
display: table-row;
}
.dcell
{
display: table-cell;
padding: 10px;
}
.dcell > *
{
vertical-align: middle;
}
input
{
width: 2em;
text-align: right;
border: thin solid black;
padding: 2px;
}
label
{
width: 5em;
padding-left: .5em;
display: inline-block;
}
#buttonDiv
{
text-align: center;
}
#oblock
{
display: block;
margin-left: auto;
margin-right: auto;
width: 700px;
}
</style>
</head>
<body>
<h1>Jacqui"s Flower Shop</h1>
<form method="post">
<div id="oblock">
<div class="dtable">
<div class="drow">
<div class="dcell">
<img src="astor.png" /><label for="astor">Astor:</label>
<input name="astor" value="0" required>
</div>
<div class="dcell">
<img src="daffodil.png" /><label for="daffodil">Daffodil:</label>
<input name="daffodil" value="0" required>
</div>
<div class="dcell">
<img src="rose.png" /><label for="rose">Rose:</label>
<input name="rose" value="0" required>
</div>
</div>
<div class="drow">
<div class="dcell">
<img src="peony.png" /><label for="peony">Peony:</label>
<input name="peony" value="0" required>
</div>
<div class="dcell">
<img src="primula.png" /><label for="primula">Primula:</label>
<input name="primula" value="0" required>
</div>
<div class="dcell">
<img src="snowdrop.png" /><label for="snowdrop">Snowdrop:</label>
<input name="snowdrop" value="0" required>
</div>
</div>
</div>
</div>
<div id="buttonDiv"><button type="submit">Place Order</button></div>
</form>
<script>
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
console.log("Element: " + elements[i].tagName + " " + elements[i].src);
}
</script>
</body>
</html>

В этом примере я разместил элемент script в самом конце элемента body. Когда браузеры находят в документе script , они сразу же начинают выполнять выражения JavaScript, до того, как успеет загрузиться и будет обработана оставшаяся часть документа. Это может создать проблему, если вы работаете с DOM, это обозначает, что поиск элементов с использованием объекта Document осуществляется до того, как интересующие вас объекты были созданы и обработаны в DOM. Чтобы избежать этого, я разместил элемент script в самом конце документа. jQuery довольно изящно справляется с этой задачей, о чем я расскажу в главе 5.

В скрипте я использовал метод getElementsByTagName, чтобы найти в документе все элементы img. Этот метод возвращает массив объектов, которые я перечисляю и вывожу на консоль значения свойств tagName и src для каждого объекта. На консоли виден следующий результат:

Element: IMG http://www.jacquisflowershop.com/jquery/astor.png
Element: IMG http://www.jacquisflowershop.com/jquery/daffodil.png
Element: IMG http://www.jacquisflowershop.com/jquery/rose.png
Element: IMG http://www.jacquisflowershop.com/jquery/peony.png
Element: IMG http://www.jacquisflowershop.com/jquery/primula.png
Element: IMG http://www.jacquisflowershop.com/jquery/snowdrop.png

Изменение объектов в DOM

Объекты в DOM живые. Это обозначает, что изменения свойств объекта в DOM повлияют на вид документа в браузере. Листинг 2-16 показывает скрипт, который совершает данную операцию. (Чтобы избежать дублирования, тут показан только элемент script. Остальная часть документа точно такая же, как и в последнем примере).

Листинг 2-16: Изменение свойств DOM объектов
...
<script>
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
elements[i].src = "snowdrop.png";
}
</script>
...

В этом скрипте я изменил значение атрибута src на snowdrop.png для всех элементов img. Что из этого получилось, видно на рисунке 2-6.

Рисунок 2-6: Использование DOM для изменения HTML документа

Изменение стилей

DOM также можно использовать для изменения свойств CSS. (Вспомнить CSS вам поможет глава 3). Поддержка DOM API довольно обширна для CSS, но самый простой путь изменить CSS – это использовать свойства style объекта HTMLElement. Объект, возвращаемый свойством style, определяет свойства, которые соответствуют свойствам CSS. (Прошу прощения за обилие свойств в данном предложении).

Способы наименования свойств в CSS и для объектов, которые возвращает style, немного различаются. Например, свойство CSS background-color становится свойством style.backgroundColor объекта. В листинге 2-17 показано, как использовать DOM для управления стилями.

Листинг 2-17: Использование DOM для изменения стиля элементов
...
<script>
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i > 0) {
elements[i].style.opacity = 0.5;
}
}
</script>
...

Этим скриптом я изменил значение свойства opacity для всех элементов img, кроме первого. Я оставил один элемент нетронутым, чтобы вы увидели разницу. Это видно на рисунке 2-7.

Рисунок 2-7: Использование JavaScript для изменения свойств CSS

Обработка событий

События – это сигнал, посылаемый браузером для определения изменения в статусе одного или более элементов в DOM. Есть различные события, определяющие разные изменения статуса. Например, событие click срабатывает тогда, когда пользователь щелкает мышкой по элементу в документе, а событие submit срабатывает тогда, когда пользователь отправляет форму. Многие события связаны между собой. Например, событие mouseover срабатывает тогда, когда пользователь водит курсором мышки поверх элемента, а событие mouseout срабатывает тогда, когда пользователь выводит мышку из зоны элемента.

Можно реагировать на события, связывая функцию JavaScript с событием для элемента DOM. В листинге 2-18 показан пример этого.

Листинг 2-18: Обработка события
...
<script>
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
elements[i].onmouseover = handleMouseOver;
elements[i].onmouseout = handleMouseOut;
}

function handleMouseOver(e) {
e.target.style.opacity = 0.5;
}

function handleMouseOut(e) {
e.target.style.opacity = 1;
}
</script>
...

Этот скрипт определяет две функции обработчика, которые я назначаю значениями свойств onmouseover и onmouseout для DOM-объектов img. Действие этого скрипта заключается в том, что рисунки становятся частично прозрачными, если по ним двигается курсор мышки, и становятся нормального цвета, когда курсор выходит за область рисунка. Я не намереваюсь слишком пристально рассматривать механизм обработки событий DOM API, поскольку в главе 9 будет рассмотрена обработка событий в jQuery. И все же я хочу обратить внимание на объект, которому передаются функции обработки событий, – это объект Event. В таблице 2-5 показаны наиболее важные члены объекта Event.

Таблица 2-5: Функции и свойства объекта Event
Имя Описание Возвращает
type Название события, например mouseover. string
target Элемент, на который это событие направлено. HTMLElement
currentTarget Элемент, слушатели которого вызваны в настоящее время. HTMLElement
eventPhase Фаза жизненного цикла события. number
bubbles Возвращает true, если событие будет "пузыриться" по документу; и false, если нет. boolean
cancelable Возвращает true, если событию установлено действие по умолчанию, которое можно отменить; в противном случае возвращает false. boolean
stopPropagation() Останавливает поток событий по дереву элементов, после того как сработали слушатели события для данного элемента. void
stopImmediatePropagation() Незамедлительно останавливает поток событий по дереву элементов. Незадействованные событийные слушатели для текущего элемента игнорируются. void
preventDefault() Запрещает браузеру совершать действие по умолчанию, связанное с событием. void
defaultPrevented Возвращает true, если вызывалась функция preventDefault(). boolean

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

Поток событий

Событие имеет три фазы жизненного цикла: фаза перехвата (capture), нахождение целевого элемента (target), фаза всплывания (bubbling). Когда срабатывает триггер, браузер определяет элемент, к которому относится данное событие, эта фаза называется фаза перехвата. Браузер идентифицирует все элементы между элементом body и целевым элементом, и проверяет каждый из них, чтобы посмотреть, есть ли у них обработчики, которые должны получить информацию о событиях, происходящих в их узлах-потомках. Браузер запускает любой такой обработчик, прежде чем запустить обработчик для целевого элемента. (Я покажу вам, как "запрашивать информацию" о событиях узлов-потомков в главе 9.)

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

После завершения фазы нахождения целевого элемента, браузер начинает его обработку, двигаясь по цепочке элементов-предков обратно к элементу body. Для каждого элемента браузер делает проверку, есть ли такие обработчики для данного события, которые не были включены в фазу перехвата (об это я подробнее расскажу в главе 9). Не все события поддерживают пузырьковый эффект (баблинг). Чтобы проверить это, вы можете использовать свойство bubbles. Значение true оказывает, что баблинг имеет место быть, а false, что нет.

Действия по умолчанию

Некоторые события определяют действие по умолчанию, которое будет выполнено после запуска события. Например, действием по умолчанию для события click будет таковым, что браузер будет загружать документ с тем URL, который определен в атрибуте href. Если для события определено действие по умолчанию, значение его свойства cancelable будет true. Исполняемое действие по умолчанию можно остановить, вызвав метод preventDefault. Запомните, что вызов функции preventDefault не остановит действие на фазе перехвата, фазе нахождения целевого элемента и фазе всплывания. Эти фазы будут пройдены, но браузер не будет выполнять действие по умолчанию в конце фазы всплывания (баблинга). Вы можете посмотреть, была ли вызвана функция preventDefault более ранним обработчиком, используя свойство defaultPrevented. Если возвращается true, значит, функция preventDefault была вызвана.