Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ASCII и latin.docx
Скачиваний:
3
Добавлен:
08.08.2019
Размер:
190.09 Кб
Скачать

15.4.2.2. Атрибуты

Атрибуты элемента (такие как src и width тега <img>) могут быть прочитаны, уста

новлены и удалены с помощью методов getAttribute(), setAttribute() и removeAt

tribute() интерфейса Element. Как уже говорилось, стандартные атрибуты HTML

тегов доступны в виде свойств узлов Element, представляющих эти теги.

Другой менее удобный способ работы с атрибутами предлагает метод getAttribute

Node(), который возвращает объект Attr, представляющий атрибут и его значение.

(Одной из причин выбора этой менее удобной технологии является наличие у ин

терфейса Attr свойства specified, позволяющего определять, указан ли данный

атрибут в документе явно или для него принимается значение по умолчанию.)

Интерфейс Attr на рис. 15.2 – отдельный тип узла. Однако следует отметить, что

объекты Attr отсутствуют в массиве childNodes[] элемента и непосредственно не

являются частью дерева документа, как узлы Element и Text. Спецификация DOM

позволяет обращаться к узлам Attr через массив attributes[] интерфейса Node, но

в Internet Explorer определяется другой несовместимый массив attributes[], что

делает невозможным использование этого массива переносимым образом

15.5. Обход документа

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

нию DOM API. В этом и следующих разделах демонстрируется, как можно орга

низовать обход дерева элементов документа, а также изменять содержимое до

кумента и добавлять новое содержимое.

Как уже отмечалось, DOM представляет HTMLдокумент в виде дерева объектов

Node. Для любой древовидной структуры наиболее частое выполняемое действие –

это обход дерева с поочередным просмотром каждого узла. Один из способов по

казан в примере 15.2. Это JavaScriptфункция, рекурсивно просматривающая

узел и все дочерние узлы и подсчитывающая количество HTMLтегов (т. е. узлов

Element), встреченных в процессе обхода. Обратите внимание на свойство childNo

des текущего узла. Значением этого свойства является объект NodeList, ведущий

себя (в JavaScript) как массив объектов Node. Поэтому функция может перечис

лить все дочерние узлы данного узла путем циклического перебора элементов

массива childNodes[]. Функция рекурсивно перечисляет не только все дочерние

узлы данного узла, но и все узлы в дереве узлов. Обратите внимание, что эта

функция демонстрирует также применение свойства nodeType для определения

типа каждого узла.

Пример 15.2. Обход узлов документа

<head>

<script>

// Этой функции передается DOMобъект Node. Функция проверяет, представляет

// ли этот узел HTMLтег, т. е. является ли узел объектом Element. Она рекурсивно

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

// Функция возвращает общее число найденных ею объектов Element. Если вы вызываете

// эту функцию, передавая ей DOMобъект, она выполнит обход всего DOMдерева.

function countTags(n) { // n – это Node

var numtags = 0; // Инициализируем счетчик тегов

if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) // Проверяем, является ли n

// объектом Element

numtags++; // Если это так, увеличиваем счетчик

var children = n.childNodes; // Теперь получаем все дочерние элементы n

for(var i=0; i < children.length; i++) { // Цикл по всем дочерним элементам

numtags += countTags(children[i]); // Рекурсия по всем дочерним элементам

}

return numtags; // Возвращаем общее число тегов

}

</script>

</head>

<! Это пример использования функции countTags() >

<body onload="alert('Количество тегов в документе: ' + countTags(document))">

Это <i>пример</i> документа.

</body>15.6. Поиск элементов в документе 335

Обратите внимание: определенная в примере 15.2 функция countTags() вызывает

ся из обработчика события onload, поэтому она вызвана не будет, пока документ

не загрузится целиком. Это обязательное требование при работе с DOM: нельзя

обходить дерево документа или манипулировать им до тех пор, пока документ

полностью не загружен. (В разделе 13.5.7 детально обсуждаются причины этого

ограничения. Дополнительно в примере 17.7 приводится функция, которая по

зволяет регистрировать обработчики события onload нескольких модулей.)

В дополнение к свойству childNodes интерфейс Node определяет несколько других

полезных свойств. Свойства firstChild и lastChild ссылаются на первый и послед

ний дочерние узлы, а свойства nextSibling и previousSibling – на ближайшие

смежные узлы. (Два узла называются смежными, если имеют один и тот же роди

тельский узел.) Эти свойства предоставляют еще один способ обхода дочерних уз

лов, который демонстрируется в примере 15.3. В этом примере приводится опре

деление функции getText(), которая отыскивает все узлы Text, вложенные в ука

занный узел. Она извлекает и объединяет текстовое содержимое узлов и возвра

щает полученный результат в виде JavaScriptстроки. Потребность в такой

функции при программировании с использованием DOMинтерфейсов возника

ет на удивление часто.

Пример 15.3.Получение текстового содержимого из всех вложенных DOM'узлов

/**

* getText(n): Отыскивает все узлы Text, вложенные в узел n.

* Объединяет х содержимое и возвращает результат в виде строки.

*/

function getText(n) {

// Операция объединения строк очень ресурсоемка, потому сначала

// содержимое текстовых узлов помещается в массив, затем выполняется

// операция конкатенации элементов массива в одну строку.

var strings = [];

getStrings(n, strings);

return strings.join("");

// Эта рекурсивная функция отыскивает все текстовые узлы

// и добавляет их содержимое в конец массива.

function getStrings(n, strings) {

if (n.nodeType == 3 /* Node.TEXT_NODE */)

strings.push(n.data);

else if (n.nodeType == 1 /* Node.ELEMENT_NODE */) {

// Обратите внимание, обход выполняется

// с использованием firstChild/nextSibling

for(var m = n.firstChild; m != null; m = m.nextSibling) {

getStrings(m, strings);

}

}

}

}

15.6. Поиск элементов в документе

Возможность обхода всех узлов в дереве документа дает нам средство поиска оп

ределенных узлов. При программировании с использованием DOM API довольно336 Глава 15. Работа с документами

часто возникает задача получения определенного узла из документа или списка

узлов определенного типа. К счастью, DOM API предоставляет функции, облег

чающие решение этой задачи.

Объект Document является корневым элементом для всего DOMдерева, но он не

представляет ни один из HTMLэлементов в этом дереве. Свойство document.docu

mentElement ссылается на тег <html>, который выступает в роли корневого элемента

документа. Свойство document.body соответствует тегу <body>, который в большин

стве случаев представляет больший интерес, чем его родительский тег <html>.

Свойство body объекта Document представляет собой особое удобное свойство, через

котрое предпочтительно обращаться к тегу <body> HTMLдокумента. Однако при

отсутствии такого специального свойства мы могли бы обратиться к тегу <body>

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

document.getElementsByTagName("body")[0]

Это выражение вызывает метод getElementsByTagName() и выбирает первый эле

мент полученного массива. Вызов getElementsByTagName() возвращает массив всех

элементов <body> в документе. HTMLдокументы могут содержать только один

тег <body>, поэтому мы знаем, что нас интересует первый элемент полученного

массива.1

Метод getElementsByTagName() может использоваться для получения списка HTML

элементов любого типа. Чтобы, например, найти все таблицы в документе, необ

ходимо сделать следующее:

var tables = document.getElementsByTagName("table");

alert("Количество таблиц в документе: " + tables.length);

Обратите внимание: поскольку HTMLтеги нечувствительны к регистру, строки,

передаваемые в getElementsByTagName(), также нечувствительны к регистру. То

есть предыдущий код находит теги <table>, даже если в коде они выглядят как

<TABLE>. Метод getElementsByTagName() возвращает элементы в том порядке, в ко

тором они расположены в документе. И наконец, если передать методу getEle

mentsByTagName() специальную строку "*", он вернет список всех элементов в по

рядке их присутствия в документе. (Этот особый вариант не поддерживается

в IE 5 и 5.5. См. описание специфического для IE массива Document.all[] в IV час

ти книги.)

Иногда требуется получить не список элементов, а один конкретный элемент до

кумента. Если о структуре документа известно многое, то можно прибегнуть

кметоду getElementsByTagName(). Так, сделать чтото с четвертым абзацем доку

мента можно при помощи следующего кода:

var myParagraph = document.getElementsByTagName("p")[3];

Однако, как правило, это не самый лучший (и не самый эффективный) прием, по

скольку он в значительной степени зависит от структуры документа – вставка но

вого абзаца в начало документа нарушит работу кода. Когда требуется манипули

1 Формально метод getElementsByTagName() возвращает подобный массиву объект No

deList. В этой книге для обращения к объектам NodeList используется нотация

массивов, и неформально я буду называть их массивами.15.6. Поиск элементов в документе 337

ровать определенными элементами документа, лучше идти другим путем и опре

делить для этих элементов атрибут id, задающий уникальное (в пределах доку

мента) имя элемента. Тогда элемент можно будет найти по его идентификатору.

Например, отметить специальный четвертый абзац документа тегом можно так:

<p id="specialParagraph">

Теперь легко найти узел этого абзаца посредством следующего JavaScriptкода:

var myParagraph = document.getElementById("specialParagraph");

Обратите внимание: метод getElementById() не возвращает массив элементов, как

метод getElementsByTagName(). Так как значение каждого атрибута id является

(или предполагается) уникальным, getElementById() возвращает только один эле

мент с соответствующим атрибутом id.

Метод getElementById() достаточно важен и довольно часто применяется в DOM

программировании. Обычно он служит для определения вспомогательной функ

ции с более коротким именем:

// Если x – это строка, предполагается, что это идентификатор элемента

// и требуется отыскать этот элемент.

// В противном случае предполагается, что x – это уже элемент,

// поэтому нужно просто вернуть его.

function id(x) {

if (typeof x == "string") return document.getElementById(x);

return x;

}

С помощью аналогичных функций можно реализовать такие методы манипули

рования DOMдеревом, которые будут принимать в качестве аргументов элемен

ты или идентификаторы элементов. Для каждого такого аргумента x перед его

использованием достаточно будет записать x = id(x). Один широко известный на

бор инструментальных средств для применения в сценариях1, написанных на

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

имеющий еще более короткое имя – $().

Оба метода, getElementById() и getElementsByTagName(), относятся к методам объек

та Document. Однако объект Element также определяет метод getElementsByTag

Name(). Этот метод объекта Element ведет себя так же, как метод объекта Document,

за исключением того, что возвращает только элементы, являющиеся потомками

того элемента, для которого он вызван. Благодаря этому можно, например, сна

чала использовать метод getElementById() для поиска определенного элемента,

а затем – метод getElementsByTagName() для поиска всех потомков данного типа

внутри найденного тега, например:

// Ищет определенный элемент Table внутри документа

// и подсчитывает количество строк в таблице.

var tableOfContents = document.getElementById("TOC");

var rows = tableOfContents.getElementsByTagName("tr");

var numrows = rows.length;

1 Имеется в виду библиотека Prototype, разработанная Сэмом Стефенсоном (Sam

Stephenson) и доступная на сайте http://prototype.conio.net.338 Глава 15. Работа с документами

И наконец, следует отметить, что для HTMLдокументов объект HTMLDocument оп

ределяет также метод getElementsByName(). Этот метод похож на getElementById(),

но выполняет поиск элементов по атрибуту name, а не по атрибуту id. Кроме того,

поскольку атрибут name не обязательно уникален в пределах документа (напри

мер, группы переключателей в HTMLформах обычно имеют одинаковый атри

бут name), getElementsByName() возвращает массив элементов, а не одиночный эле

мент. Пример:

// Ищем тег <a name="top">

var link = document.getElementsByName("top")[0];

// Ищем все элементы <input type="radio" name="shippingMethod">

var choices = document.getElementsByName("shippingMethod");

В дополнение к выбору элементов по названию тега и идентификатору очень час

то бывает удобно иметь возможность отбирать элементы по принадлежности

к определенному классу. Атрибуту class в HTML и соответствующему ему свой

ству className в JavaScript можно присваивать одно или более имен классов (раз

деленных пробелами). Эти классы предназначены для использования совместно

с таблицами CSSстилей (подробнее об этом см. в главе 16), но это – не единствен

ное их предназначение. Предположим, что в HTMLдокумент вставляются важ

ные предупреждения, например следующим образом:

<div class="warning">

Это предупреждение

</div>

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

которых задать цвет, отступы, рамки и другие атрибуты вывода предупрежде

ний этого класса. Но что делать, если необходимо написать JavaScriptсцена

рий, который мог бы отыскивать теги <div>, являющиеся членами класса «пре

дупреждений», и манипулировать этими тегами? Возможное решение приво

дится в примере 15.4. Здесь определяется метод getElements(), который позволя

ет отобрать элементы по имени класса и/или тега. Обратите внимание на

ухищрения, используемые при работе со свойством className, – они вызваны тем,

что в данном свойстве могут храниться имена нескольких классов. Метод getEle

ments() содержит вложенную функцию isMember(), которая проверяет принад

лежность HTMLэлемента к заданному классу.

Пример 15.4. Отбор HTML'элементов по имени класса или тега

/**

* getElements(classname, tagname, root):

* Возвращает массив DOMэлементов, которые являются членами заданного класса,

* соответствуют тегам с определенным именем и вложены в элемент root.

*

* Если аргумент classname не определен, отбор элементов производится

* без учета принадлежности к какомуто определенному классу.

* Если аргумент tagname не определен, отбор элементов производится без учета имени тега.

* Если аргумент root не определен, поиск производится в объекте document.

* Если аргумент root является строкой, он воспринимается как идентификатор

* элемента и поиск производится методом getElementsById()

*/

function getElements(classname, tagname, root) {15.7. Модификация документа 339

// Если корневой элемент не определен, произвести поиск по всему документу

// Если это строка, найти сам объект

if (!root) root = document;

else if (typeof root == "string") root = document.getElementById(root);

// Если имя тега не определено, искать без учета имени тега

if (!tagname) tagname = "*";

// Искать элементы, вложенные в элемент root и имеющие определенное имя тега

var all = root.getElementsByTagName(tagname);

// Если имя класса не определено, вернуть все теги без учета имени класса

if (!classname) return all;

// В противном случае отобрать элементы по имени класса

var elements = []; // Создается пустой массив

for(var i = 0; i < all.length; i++) {

var element = all[i];

if (isMember(element, classname)) // Метод isMember() определен далее

elements.push(element); // Добавлять члены класса в массив

}

// Обратите внимание: всегда возвращается массив, даже если он пустой

return elements;

// Определяет принадлежность элемента к заданному классу.

// Эта функция оптимизирована для случая, когда свойство

// className содержит единственное имя класса. Но учитывает возможность

// наличия нескольких имен классов, разделенных пробелами.

function isMember(element, classname) {

var classes = element.className; // Получить список класов

if (!classes) return false; // Класс не определен

if (classes == classname) return true; // Точное совпадение

// Нет точного совпадения, поэтому если в списке нет пробелов,

// то этот элемент не является членом класса.

var whitespace = /\s+/;

if (!whitespace.test(classes)) return false;

// В этой точке известно, что элемент принадлежит нескольким

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

var c = classes.split(whitespace); // Разбить по символам пробелов

for(var i = 0; i < c.length; i++) { // Цикл по всем классам

if (c[i] == classname) return true; // Проверить совпадение

}

return false; // Не обнаружено ни одного совпадения

}

}

15.8.2. Свойство innerHTML

Хотя консорциум W3C никогда официально не определял свойство innerHTML как

составную часть модели DOM, тем не менее это свойство узлов HTMLElement явля

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

При чтении из этого свойства вы в формате HTML получаете текст, который

представляет дочерние узлы элемента. При записи в это свойство броузер запус

кает синтаксический анализатор HTMLкода для разбора строки и замещает до

черние элементы теми, которые были получены от анализатора.

Описывать HTMLдокумент в виде строки с текстом в формате HTML обычно

удобнее и проще, чем использовать для этих же целей последовательность вызо

вов createElement() и appendChild(). Снова вернемся к той части примера 15.9, где

создается новый элемент <table> и затем к нему добавляется строка заголовка.

Благодаря свойству innerHTML этот относительно крупный фрагмент программно

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

var table = document.createElement("table"); // Создать элемент <table>

table.border = 1; // Установить атрибут

// Добавить в таблицу заголовок Имя|Тип|Значение

table.innerHTML = "<tr><th>Имя</th><th>Тип</th><th>Значение</th></tr>";

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

Оказывается, что использование свойства innerHTML гораздо эффективнее, осо

бенно при анализе больших фрагментов HTMLтекста. Однако следует отметить,

что операция добавления небольших фрагментов текста в свойство innerHTML

с помощью оператора += обычно не отличается эффективностью, поскольку тре

бует как сериализации, так и синтаксического анализа.

Свойство innerHML было введено компанией Microsoft в IE 4. Оно входит в квартет

наиболее важных и часто используемых свойств. Остальные три свойства, outer

HTML, innerText и outerText, описанные в конце этой главы, не поддерживаются

в Firefox и родственных броузерах.

CSS и DHTML

Каскадные таблицы стилей (Cascading Style Sheets, CSS) – это стандарт визуаль

ного представления HTMLи XMLдокументов. Теоретически структуру доку

мента следует задавать путем HTMLразметки, сопротивляясь искушению приме

нять устаревшие HTMLтеги, такие как <font>, поскольку для задания стилей су

ществуют CSSтаблицы, определяющие, как именно должны отображаться струк

турные элементы документа. Например, CSS позволяет указать, что заголовки

первого уровня, определяемые тегами <h1>, должны отображаться в верхнем реги

стре, шрифтом sansserif с полужирным начертанием и размером в 24 пункта, вы

равнивание по центру.

Технология CSS ориентирована на дизайнеров, а также всех тех, кто заботится

о точном визуальном отображении HTMLдокументов. Она интересна програм

мистам, пишущим на клиентском языке JavaScript, т. к. объектная модель до

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

ментам документа. Совместное применение технологий CSS и JavaScript обеспе

чивает получение разнообразных визуальных эффектов, не совсем точно назы

ваемых динамическим языком HTML (Dynamic HTML, DHTML).1

Способность манипулировать CSSстилями в сценариях позволяет динамически

изменять цвет, шрифт и прочие элементы оформления. Еще важнее, что CSS

стили дают возможность устанавливать и изменять размер элементов и даже

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

зоваться для создания анимированных переходов, когда, например, содержимое

документа «выплывает» изза границ экрана или структурированный список

разворачивается и сворачивается, благодаря чему пользователь может управ

лять объемом выводимой информации.

Эта глава начинается с обзора CSS. Затем рассказывается о том, как с помощью

CSSстилей задать позицию элементов документа и режим их видимости. После

этого описываются приемы манипулирования CSSстилями в сценариях. Наибо

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]