Очень часто приходится сталкиваться с тем, что необходимо каким-либо образом обработать XML-данные в JavaScript. Для этого существуют специализированные функции. На первый взгляд, это достаточно сложно, но на самом-то деле всё очень легко, в чём мы сейчас и убедимся.
Методы
Для начала мы рассмотрим набор функций и свойств, которые предоставлены нам для оперирования XML-данными.
element.getElementsByTagName("element_name"); — получить массив (коллекцию) всех элементов с указанным именем element_name, находящихся в элементе element. Если element_name равен символу «*», то возвращаются рекурсивно все элементы, входящие в данный элемент и его потомков. Для обращения к конкретному элементу, мы можем использовать такую запись:
//Получаем все элементы div, находящиеся в element
var elements = element.getElementsByTagName("div");
//Получаем первый элемент в коллекции
var element_one = elements.item(0);
//Получаем количество элементов в коллекции
var element_count = elements.length;
//Проходимся по всем элементам коллекции
for (var i = 0; i < elements_count; i++)
{
//Производим необходимые действия
}
document.getElementById("element_id") — получение элемента (единственного) по его идентификатору element_id. Используется чаще в оперировании элементами в самом (X)HTML-документе.
document.getElementByName("element_name"); — получение элемента по атрибуту name. Используется достаточно редко, лучше использовать document.getElementById.
document.createElement("element_type") — создание элемента element_type (будет создан элемент с таким тегом). Возвращает вновь созданный элемент.
Свойства
documentElement — получение родительского элемента из XML-документа. Возвращает полноценный документ-объект, с которым можно производить различные операции.
node.nextSibling — получение следующего узла в коллекции.
node.nodeName — получение имени узла.
node.nodeValue — получение значения узла.
Практикуемся
Для практики мы возьмём не библиотеку музыки, книг или других обыденных примеров, а RSS, ведь он тоже является XML, только поверх него (XML) описаны необходимые элементы для унификации формата.
Берём наш RSS из примеров к спецификации формата (http://cyber.law.harvard.edu/rss/examples/rss2sample.xml):
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Liftoff News</title>
<link>http://liftoff.msfc.nasa.gov/</link>
<description>Liftoff to Space Exploration.</description>
<language>en-us</language>
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
<lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>Weblog Editor 2.0</generator>
<managingEditor>editor@example.com</managingEditor>
<webMaster>webmaster@example.com</webMaster>
<item>
<title>Star City</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
<description>How do Americans get ready to work with Russians aboard
the International Space Station? They take a crash course in culture, language
and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>.</description>
<pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
</item>
<item>
<description>Sky watchers in Europe, Asia, and parts of Alaska and Canada
will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">
partial eclipse of the Sun</a> on Saturday, May 31st.</description>
<pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid>
</item>
</channel>
</rss>
Это обычный XML-документ. Мы его будем препарировать, словно на анатомическом вскрытии.
Перед оперированием XML-документа необходимо убедиться в том, что он валиден: присутствует маркировка (самая верхняя строка с версией и кодировкой) и единственный корневой элемент (в нашем случае — RSS). Помимо этого, должны соблюдаться и другие правила оформления XML-документа.
Поместим этот документ в файл rss.xml, он нам пригодится.
Мы будем загружать наш XML-файл через объект XMLHttpRequest. Поместим различные рутины в файл ajax.js. В конечном итоге он будет выглядеть так:
/*
Функция создания XMLHttpRequest
*/
function CreateRequest()
{
var Request = false;
if (window.XMLHttpRequest)
{
//Gecko-совместимые браузеры, Safari, Konqueror
Request = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
//Internet explorer
Request = new ActiveXObject("Microsoft.XMLHTTP");
if (!Request)
{
HRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
}
if (!Request)
{
alert("Невозможно создать XMLHttpRequest");
}
return Request;
}
/*
Функция посылки запроса к файлу на сервере
r_method — тип запроса: GET или POST
r_path — путь к файлу
r_args — аргументы вида a=1&b=2&c=3...
r_handler — функция-обработчик ответа от сервера
*/
function SendRequest(r_method, r_path, r_args, r_handler)
{
//Создаём запрос
var Request = CreateRequest();
//Проверяем существование запроса еще раз
if (!Request)
{
return;
}
//Назначаем пользовательский обработчик
Request.onreadystatechange = function()
{
//Если обмен данными завершен
if (Request.readyState == 4)
{
//Передаем управление обработчику пользователя
r_handler(Request);
}
}
//Проверяем, если требуется сделать GET-запрос
if (r_method.toLowerCase() == "get" && r_args.length > 0)
r_path += "?" + r_args;
//Инициализируем соединение
Request.open(r_method, r_path, true);
if (r_method.toLowerCase() == "post")
{
//Если это POST-запрос
//Устанавливаем заголовок
Request.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=utf-8");
//Посылаем запрос
Request.send(r_args);
}
else
{
//Если это GET-запрос
//Посылаем нуль-запрос
Request.send(null);
}
}
/*
Функция для получения файла
filename — имя файла (относительный или абсолютный от корня Web-сайта)
handler — функция-обработчик результата
*/
function GetXMLFile(filename, handler)
{
//Посылаем запрос
SendRequest("GET",filename,"",handler);
}
Костяк теперь у нас есть. В нём мы создаём основную структуру документа (только структуру) и подключаем наш behavior (действия) — ajax.js. Теперь мы будем оперировать в теге body.
Теперь начинаем оформлять файл index.html. Создадим его скелет:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>XML Test</title>
<script type="text/javascript" src="ajax.js"></script>
</head>
<body>
</body>
</html>
Для начала мы создадим два элемента-контейнера. Элементы-контейнеры — это такие элементы, которые содержат в себе или будут содержать какую-либо информацию, чаще всего, динамическую.
<h2 id="get_title"></h2>
<div id="get_container"></div>
Контейнер с идентификатором get_title будет содеражть заголовок нашего RSS-документа, а контейнер get_container будет содержать его записи.
Сразу же после этих записей мы помещаем новый блок кода. В идеальном документе, он должен быть вынесен в отдельный файл и подключен через запись script. Это следует делать для соответствия модели «трёх китов»: разделение действий, структуры и репрезентации (представления) в документе.
<script type="text/javascript">
</script>
Приступим к работе с наши документом. Для начала, мы должны получить его через объект XMLHttpRequest.
//Функция-обработчик ответа
function ParseXML(Response)
{
}
//Получаем XML-файл
GetXMLFile("rss.xml", ParseXML);
Всё достатоно просто: мы загружаем с сервера документ rss.xml посредством XMLHttpRequest и в тот момент, когда загрузка уже завершена, вызываем нашу функцию ParseXML. Ей передаётся один аргумент — Response — это тот самый XMLHttpRequest, но уже с результатом его работы. В нём есть свойство responseXML, с помощью которого можно получить доступ к XML-части ответа от сервера. Оно-то нам и понадобится. Мы сделаем из неё полноценный документ, с которым и будем оперировать дальше (теперь мы работаем с телом функции ParseXML):
//Получаем документ
var doc = Response.responseXML.documentElement;
Переменная doc — это полноценный документ, над которы мы можем производить различные операции, такие как выборка тегов, либо поиск элемента по его идентификатору.
Для начала, мы заполним заголовок нашей RSS-ленты. Сделаем это таким образом:
//Получаем заголовок
//Заголовок — это первый элемент title в документе
$("get_title").innerHTML = doc.getElementsByTagName("title").item(0).firstChild.nodeValue;
Подробно рассмотрим приведённый выше код: Сначала мы получаем все элементы в документе с именем title, после этого выбираем первый из найденных элементов и получаем его значение. firstChild используется для того, чтобы получить доступ именно к текстовой части содержимого узла.
Теперь можно смело переходить к поиску и обработке элементы (items) нашей ленты.
//Проходимся по всем элементам-записям и составляем их репрезентацию
var items = doc.getElementsByTagName("item");
for (var i = 0; i < items.length; i++)
{
//Создаём элемент для записи
var item_record = document.createElement("div");
//item_record.innerHTML += "<p>" + items[i].firstChild.nextSibling.firstChild.nodeValue.toString() + "</p>";
//Отсчитываем с первого дочернего узла
var f_child = items[i].firstChild;
//Пока у нас есть соседние узлы, переключаем их
while (f_child.nextSibling)
{
//Выбираем имя узла и в соответствии с этим выполняем необходимое действие
switch (f_child.nodeName)
{
//Если это заголовок — оформляем как заголовок
case "title":
item_record.innerHTML += "<h3>" + f_child.firstChild.nodeValue + "</h3>";
break;
//Если это описание, оформляем как описание
case "description":
item_record.innerHTML += "<p>" + f_child.firstChild.nodeValue + "</p>";
break;
}
//Устанавливаем следующий узел
f_child = f_child.nextSibling;
}
//Добавляем запись в контейнер
$("get_container").appendChild(item_record);
}
Считаю, что код достаточно хорошо комментирован и разобраться в нём не будет большой проблемой.
Таким образом, мы на примере посмотрели процесс работы с XML-документом.
Итоги
На основе подобных возможностей JavaScript можно делать массу интересных приложений. Один из примеров — это аггрегация большого количества RSS-лент (сервер получает удалённую ленту, распределяет их особым образом по базе данных; репрезентация таких лент лежит уже на стороне клиента, что более естественно).
Можно задать вопрос: «Почему вы использовали AJAX?». На него есть довольно простой ответ: AJAX — это асинхронный JavaScript и XML, то есть мы поступили достаточно правильно, показав эти две технологии в связке, тем самым логично раскрыв суть технологии.