Модель W3C DOM уже описывалась в главе 15, но там основное внимание уделя_ лось ее использованию в JavaScript_сценариях для работы с HTML_документами на стороне клиента. Фактически консорциум W3C проектировал DOM API как не зависящий от языка прикладной интерфейс, предназначенный в первую оче_ редь для работы с XML_документами, а работа с HTML_документами реализова_
21.2. Манипулирование XML*данными cредствами DOM API
на в нем через необязательный модуль расширения. Обратите внимание: в чет_ вертой части книги есть отдельные разделы, посвященные интерфейсам Document
и HTMLDocument, а также объектам Element и HTMLElement. Интерфейсы HTMLDocument
и HTMLElement являются расширениями базовых XML_интерфейсов Document и Ele_ ment. Если вы привыкли применять DOM_интерфейс для манипулирования HTML_документами, то при работе с XML_документами следует избегать ис_ пользования прикладного интерфейса, специфичного для HTML.
Вероятно, самое существенное отличие между HTML и XML в модели DOM со_ стоит в использовании метода getElementById(), который обычно бесполезен для XML_документов. В DOM уровня 1 этот метод фактически относится только к HTML и определен исключительно в интерфейсе HTMLDocument. В DOM Level 2 этот метод повышен до интерфейса Document, но здесь есть одно препятствие. В XML_документах метод getElementById() ищет элемент с заданным значением атрибута, тип которого – «id». Здесь недостаточно определить в элементе атри_ бут с именем «id», поскольку имя атрибута не имеет никакого значения – важен только тип атрибута. Типы атрибутов объявляются в определении типа доку_ мента (Document Type Definition, DTD), а DTD документа указывается в объяв_ лении DOCTYPE. XML_документы, используемые веб_приложениями, часто не имеют этого объявления, потому метод getElementById() для таких документов всегда возвращает значение null. Обратите внимание: метод getElementsByTagNa_ me() интерфейсов Document и Element прекрасно работает с XML_документами. (Да_ лее в этой главе я покажу, как делать запросы к XML_документу с помощью XPath_выражений; язык запросов XPath может использоваться для извлечения элементов на основе значения любого атрибута.)
Еще одно отличие между HTML_ и XML_объектами Document заключается в нали_ чии свойства body, которое ссылается на тег <body> в документе. В случае с XML_ документами только свойство documentElement ссылается на элемент верхнего уровня в документе. Обратите внимание: этот элемент верхнего уровня доступен также через свойство childNodes[] документа, но указанный элемент может ока_ заться не первым и не единственным в этом массиве, поскольку XML_документ может также содержать объявление DOCTYPE, комментарии и исполняемые инст_ рукции верхнего уровня.
Существует еще одно очень важное отличие между XML_интерфейсом Element и интерфейсом HTMLElement, который является его расширением. В модели HTML DOM стандартные HTML_атрибуты доступны в виде свойств интерфейса HTMLEle_ ment. Например, атрибут src тега <img> доступен в виде свойства src объекта HTMLI_ mageElement, являющегося представлением тега <img>. Однако в модели XML DOM дело обстоит иначе: интерфейс Element имеет единственное свойство tagName. По_ лучение и изменение значения атрибута XML_элемента должно выполняться ме_ тодами getAttribute(), setAttribute() и другими родственными им методами.
В заключение следует отметить, что специальные атрибуты, которые являются значащими в любом HTML_теге, не имеют никакого значения для всех XML_эле_ ментов. Вспомните, что установка атрибута с именем «id» в XML_элементе не означает, что этот элемент может быть найден методом getElementById(). Анало_ гичным образом нельзя определить стиль XML_элемента, установив атрибут style. Точно так же невозможно ассоциировать XML_элемент с CSS_классом, уста_ новив атрибут class. Все эти атрибуты являются специфичными для HTML.
526 Глава 21. JavaScript и XML
Пример: создание HTML_таблицы на основе XML_данных
В примере 21.7 определяется функция с именем makeTable(), которая использует модели XML DOM и HTML DOM для извлечения данных из XML_документа и до_ бавления их в HTML_документ в виде таблицы. Функция ожидает получить в ви_ де аргумента литерал JavaScript_объекта, который указывает, какие элементы XML_документа содержат данные для таблицы и как эти данные должны распо_ лагаться в таблице.
Прежде чем перейти к программному коду функции makeTable(), рассмотрим пример ее применения. В примере 21.6 приводится образец XML_документа, ко_ торый мы используем в этом примере (а также в других примерах этой главы).
Следующий фрагмент HTML_документа демонстрирует, как может использо_ ваться функция makeTable() с этими XML_данными. Обратите внимание: объект schema ссылается на имена тегов и атрибутов из файла_образца.
<script>
// Эта функция обращается к makeTable() function displayAddressBook() {
var schema = {
rowtag: "contact", columns: [
{ tagname: "@name", label: "Name" },
{ tagname: "email", label: "Address" }
]
};
var xmldoc = XML.load("addresses.xml"); // Прочитать XML_данные makeTable(xmldoc, schema, "addresses"); // Преобразовать в HTML_таблицу
}
</script>
<button onclick="displayAddressBook()">Показать адресную книгу</button> <div id="addresses"><!__ таблица будет вставлена сюда __></div>
Реализация функции makeTable() приводится в примере 21.7.
Пример 21.7. Построение HTML'таблицы из данных в формате XML
/**
* Извлекает данные из указанного XML_документа и формирует из них HTML_таблицу.
* Добавляет таблицу в конец указанного HTML_элемента.
* (Если аргумент element _ строка, он интерпретируется как идентификатор,
* по которому выполняется поиск требуемого элемента.)
21.2. Манипулирование XML*данными cредствами DOM API
*
* Аргумент schema _ это JavaScript_объект, указывающий, какие данные должны
* извлекаться и как они должны отображаться. Объект schema должен иметь
* свойство с именем "rowtag", где указывается имя XML_тега, в котором
* содержатся данные для одной строки таблицы. Кроме того, объект
* schema должен иметь свойство с именем "columns", которое ссылается на массив.
* Элементы этого массива определяют порядок следования и содержимое
* столбцов таблицы. Каждый элемент массива может быть строкой или
* JavaScript_объектом. Если элемент _ строка, она интерпретируется как имя
* тега XML_элемента, где содержатся данные для столбца таблицы, а также как
* заголовок этого столбца. Если элемент массива columns[] _ объект,
* он должен иметь свойства с именами "tagname" и "label".
* Свойство tagname используется для извлечения данных из XML_документа,
* а свойство label _ в качестве текста для заголовка столбца. Если значение
* свойства tagname начинается с символа @, оно рассматривается как название
* атрибута элемента строки, а не как дочерний элемент строки.
*/
function makeTable(xmldoc, schema, element) { // Создать элемент <table>
var table = document.createElement("table");
// Создать строку заголовка из элементов <th> внутри элемента <tr> в элементе <thead> var thead = document.createElement("thead");
var header = document.createElement("tr"); for(var i = 0; i < schema.columns.length; i++) {
var c = schema.columns[i];
var label = (typeof c == "string")?c:c.label; var cell = document.createElement("th");
cell.appendChild(document.createTextNode(label));
header.appendChild(cell);
}
// Вставить заголовок в таблицу thead.appendChild(header); table.appendChild(thead);
// Остальные строки таблицы располагаются в теге <tbody> var tbody = document.createElement("tbody"); table.appendChild(tbody);
// Получить элементы XML_документа, в которых содержатся данные var xmlrows = xmldoc.getElementsByTagName(schema.rowtag);
// Цикл по всем этим элементам. Каждый из них содержит строку таблицы. for(var r=0; r < xmlrows.length; r++) {
// Этот XML_элемент содержит данные для всей строки var xmlrow = xmlrows[r];
// Создать HTML_элемент для отображения данных в строке var row = document.createElement("tr");
// Цикл по всем столбцам, указанным в объекте schema for(var c = 0; c < schema.columns.length; c++) {
var sc = schema.columns[c];
var tagname = (typeof sc == "string")?sc:sc.tagname; var celltext;
if (tagname.charAt(0) == '@') {
// Если значение tagname начинается с '@' _ это имя атрибута