
Ajax в действии
.pdf532 Часть IV. Ajax в примерах
Переменная timerSwitch содержит таймер, определяющий, когда приходит время загрузки следующего фрейма. Если пользователь щелкает на кнопке паузы, таймер сбрасывается и изменяется состояние переменной bPaused. Булево значение этой переменной позволяет определять состояние таймера: true — пауза, false — запущен.
Глобальная переменная arrayMessage содержит отформатированные сообщения, извлекаемые из элементов RSS. Этот многомерный массив хранит всю информацию, которую мы желаем отобразить на экране. Как отмечалось ранее, элементы статей содержат больше информации, чем нам может потребоваться, поэтому мы выбираем только несколько интересующих нас позиций и отображаем их в переменной arrayMessage.
Последняя переменная, intLoadFile, приводит нас к следующему блоку кода. Она представляет собой счетчик, содержащий число текущих файлов, извлекаемых из массива arrayRSSFeeds в процессе предварительной загрузки.
Объявив все глобальные переменные, мы уже видим, к чему идет данный проект. Мы предварительно загружаем RSS-ленты из массива URL. Для отслеживания состояния этого процесса используется счетчик. В ходе предварительной загрузки из каждого XML-файла мы выбираем только необходимую информацию. После завершения процесса сообщения отображаются попеременно, плавно переходя друг в друга, — создается слайд-шоу, которым мы можем управлять (в частности, приостанавливать). Используя объявленные глобальные переменные, мы можем контролировать функциональные возможности сценария. Таким образом, мы логически подходим к разработке функции предварительной загрузки RSS-лент.
13.3.2. Предварительная загрузка средствами Ajax
Одной из проблем, с которой сталкиваются разработчики Ajax-приложений, является предварительная загрузка нескольких файлов, не требующая отправки слишком большого числа запросов к внешним Web-сайтам и решающая проблему приоритетов. Одним из решений является реализация очере- д и — и именно за это отвечает наш объект Ajax ContentLoader.
Формирование запросов
Объект ContentLoader позволяет механизму очереди упорядоченно обрабатывать запросы. В листинге 13.7 показано, что мы берем массив (заполненный в момент загрузки страницы; см. листинг 13.5), который содержит URL лент, и подготавливаем их для объекта ContentLoader.
Листинг 13.7. Функция JavaScript предварительной загрузки
window.onload = function(){ var loader= new Array() for(i=O;i<arrayRSSFeeds.length;i++){
loader[loader.length] = new net.ContentLoader(arrayRSSFeeds[i], BuildXMLResults,BuildError);}}
Глава 13. Создание приложений Ajax, не использующих сервер 533
Код, приведенный в листинге 13.7, срабатывает согласно обработчику событий onload. При загрузке страницы мы подготавливаем переменную массива loader, которая будет содержать все запросы к серверу. Мы циклически проходим по массиву arrayRSSFeeds и получаем все указатели URL RSSлент, из которых желаем получать информацию. При каждой итерации мы увеличиваем массив loader, включая в него новый запрос ContentLoader. Затем передаем массиву URL ленты, функцию BuildXMLResults(), форматирующую содержимое, и функцию BuildError (), которая будет вызываться в случае ошибки. Разобравшись с запуском процесса загрузки, мы можем переходить к форматированию возвращаемых XML-лент.
Адаптация функции
В процессе запроса мы должны вызвать либо функцию BuildXMLResults() (если запрос прошел успешно), либо BuildError () (если возникли какие-либо проблемы). Функция BuildXMLResults О принимает XML-ленту и форматирует ее в удобный формат. Функция BuildError () записывает ошибку в список ошибок. Обе функции обновляют состояние, чтобы мы могли видеть ход процесса загрузки. Реализация описанной логики приведена в листинге 13.8.
Листинг 13.8. Форматирование XML-результатов в форме массива JavaScript
f -unction BuildXMLResults () {
var xmlDoc = this.req.responseXML.documentElement; var RSSTitle =
xmlDoc.getElementsByTagName('title')[0].firstChild.nodeValue; var xRows = xmlDoc.getElementsByTagName('item') ;
for (iOO; iCXxRows . length; iC++) { intMessage = arrayMessage.length; arrayMessage[intMessage] = new Array( RSSTitle,
xRowsfiC] .getElementsByTagName{'title') {0]
.firstChild.nodeValue, xRows[iC].getElementsByTagName('link' ) [0]
.firstChild,nodeValue,
xRowsfiC].getElementsByTagName('description')[0]
.firstChild.nodeValue);
}
UpdateStatusO;
Функция BuildXMLResults (), приведенная в листинге 13.8, извлекает документ XML, обращаясь к свойству responseXML объекта запроса. Сохранив документ XML в локальной переменной xmlDoc, мы можем получить для ленты информацию о названии RSS-ленты. Для этого мы обращаемся к дескриптору элемента названия и берем значение первого дочернего узла.
Далее мы получаем элементы статей и подготавливаем цикл для прохода по полученному массиву, записанному в xRows. Последовательно проходя данный массив, мы можем создать многомерный массив, сохранив его в следующей ячейке нашего глобального массива arrayMessage. Этот глобальный массив содержит название RSS-ленты, а также заголовок, ссылку и описание

534 Часть IV Ajax в примерах
статьи. Указанный многомерный массив создается для каждого элемента стагьи, записанного в xRows. Завершив обход документа, мы вызываем функцию UpdateStatus () (листинг 13 9) для показа пользователю текущего состояния процесса .
Листинг 13,9. Функция, сообщающая о ходе предварительной загрузки
function UpdateStatus(){ intLoadFile++;
if{intLoadFile < arrayRSSFeeds.length){ document.get£lementById("divNews2").innerHTML -
"Loaded File " + intLoadFile + " of " + arrayRSSFeeds-length + strErrors;
Jelse if(intLoadFile >= arrayRSSFeeds.length && 'bLoadedOnce){ document.getEleraentByldf"divNews2").innerHTML =
"Loading Completed" + strErrors; if{arrayMessage.length == 0){
alert("No RSS information was collected."); return false; |
|
} |
|
bLoadedOnce « true; |
|
var timerX = setTimeout("ChangeView()",1500); |
|
} |
|
} |
m |
Как показано в листинге 13.9, функция UpdateStatus () |
выполняет сле- |
дующее. Во-первых, отображает пользователю состояние объекта предварительной загрузки; во-вторых, определяет, требуется ли начать слайд-шоу. Итак, прежде всего мы увеличиваем на единицу значение глобальной переменной intLoadFile, обновляя счетчик файлов. Если значение intLoadFile меньше общего числа загружаемых файлов, для отображения состояния загрузки мы задаем свойство innerHTML верхнего слоя элемента divNews2 равным выходной строке.
Если счетчик файлов больше или равен числу файлов в массиве (а слайдшоу еще не запущено), тогда мы можем запускать переходы между лентами. Однако перед началом слайд-шоу необходимо проверить, есть ли у нас вообще данные для отображения. Для этого проверяется длина отформатированного массива сообщений arrayMessage. Если сообщений нет, мы сообщаем об этом пользователю и завершаем выполнение функции, возвращая значение false.
Если имеются данные для отображения, значение bLoadedOnce устанавливается равным true и после небольшой паузы вызывается функция Changeview(). Пауза нужна для того, чтобы пользователь успел прочесть возможные сообщения об ошибках. Как отмечалось ранее, если функциязагрузчик столкнется с проблемами в процессе загрузки XML-документа, она вызовет функцию BuildError() (листинг 13.10).
Листинг 13.10. Обработка ошибок, сгенерированных XMLHttpRequest
function BuildError(){
} strErrors += "Error:" + "" + "<br/>"; UpdateStatus();

Глава 13 Создание приложений Ajax, не использующих сервер 535
Рис. 13.8. Функция предварительной загрузки загружает второй файл из четырех, что видно по сообщению о состоянии и отсутствию ошибок
Функция BuildError() позволяет отображать на экране ошибку. Таким образом, пользователь узнает, что были загружены не все файлы. Возникающая ошибка просто добавляется к глобальной переменной strErrors, после чего вызывает функция UpdateStatus {), информирующая пользователя о текущем состоянии загрузки. Чтобы проверить, как работает функция предварительной загрузки, мы можем записать документ и открыть Web-страницу в браузере (рис. 13.8).
Тестируя приложение, мы должны наблюдать на экране процесс обновления состояния. На рис. 13.8 показан момент, когда функция предварительной загрузки загружает второй файл (из четырех) при отсутствии сообщений об ошибках. После загрузки всех файлов мы должны увидеть, что все они загружены успешно, а список ошибок пуст. Однако в строке состояния мы наблюдаем ошибку JavaScript, поскольку еще не создали функцию ChangeView{). Мы займемся этим в следующем разделе, однако вначале создадим эффект затухающего перехода, работающий во всех браузерах.
73.4. Богатый эффект перехода
Итак, мы написали код, загружающий файлы в массив. Теперь можно взять данные, хранящиеся в массиве, и создать слайд-шоу, основанное на DHTML. Изменяя содержимое элементов div с помощью inner HTML, МОЖНО отображать различные статьи, загруженные функцией предварительной загрузки. Изменяя CSS-классы элементов и значение параметра z-lndex слоев, можно создавать затухающие переходы между элементами div. Объединив все это, мы сможем создать динамическое слайд-шоу с затухающими переходами.
536 Часть IV. Ajax в примерах
13.4.1. Правила прозрачности, учитывающие индивидуальность браузеров
При создании эффекта затенения необходимо изменять прозрачность верхнего слоя. Таким образом, мы сможем увидеть содержимое слоя, находящегося под текущим. Когда степень непрозрачности равна 0%, будет видно все содержимое нижнего слоя. При 100%-ной непрозрачности нижний слой совершенно не просматривается.
Далее мы, как всегда, должны рассмотреть вопрос совместимости нашего кода с браузерами Internet Explorer и Mozilla. В указанных браузера! прозрачность реализована по-разному, поэтому мы должны учесть эти различия. В Mozilla применяется параметр непрозрачности (opacity), тогда как в Internet Explorer используется фильтр, задающий прозрачность через значение параметра альфа (листинг 13.11).
Листинг 13.11. Классы CSS фильтра прозрачности
.орасО{opacity: .0;filter: alpha(opacity=0);}
.opacl{opacity: .2;filter: alpha{opacity=20);}
.opac2{opacity: .4;filter: alpha(opacity=40);}
.орасЗ{opacity: .6;filter: alpha(opacity=60);}
.opac4{opacity: .8;filter: alpha(opacity=80);}
В листинге 13.11 показано, что мы создали ряд правил, определяющих стили с различными уровнями прозрачности. Использование для изменения значений правил CSS, а не JavaScript, —- вопрос личных предпочтений. Применяя CSS, мы можем изменять и другие свойства; возможно, вместе с модификацией степени прозрачности нам захочется изменить цвета или увеличить размер шрифта. Благодаря классам CSS мы можем обойтись без дополнительного JavaScript-кода, а также свести в одном месте весь код, учитывающий отличия браузеров.
Завершив разработку классов, мы можем начинать процесс загрузки информации RSS-ленты в элементы div.
13.4.2. Реализация затухающего перехода
Тестируя код в разделе 13.3.2, мы получили сообщение об ошибке, поскольку не создали функцию ChangeView(). Эта функция инициирует процесс затухающих переходов элементов div с текстами статей друг в друга. Чтобы процесс затухания проходил верно, мы изменяем классы CSS и размещаем элементы div на уровнях с разными значениями параметра z-lndex. Реализация сказанного показана в листинге 13.12.
Листинг 13.12. Функция ChangeView()
// |
О Объявить ChangeView() |
f u n c t i o n ChangeView(){ |
|
// |
© Отобразить название RSS-ленты |
|
s t r D i s p l a y - "<span class= h RSSFeed'> " + |
|
a r r a y M e s s a g e [ c u r r e n t M e s s a g e ] [ 0 ] + "</span>: " |
// |
© Показать заголовок элемента |

Глава 13 Создание приложений Ajax, не использующих сервер 537
strDxsplay += "<span class='itemTitle'V + arrayMessage[currentMessage][1] + "</spanxhr>";
// О Вставить описание статьи
strDisplay += arrayMessage[currentMessage][3]
+"<hr>";
//© Выдать URL ленты
strDisplay += "<a href= 1 " + arrayMessage[currentMessage][2] +
111title='View Page'>View Page</a>";
//0 Изменить состояние ленты
document.getElementById("spanCount").innerHTML = "Message " + (currentMessage+1) +
" of " + arrayMessage.length;
var objDivl — document.getElementById("divNewsl"); var objDiv2 = document.getElementByldf"divNews2"); // © Подготовить переход
if(layerFront == 1){
objDiv2.className = "opacO"; objDivl.style.zlndex = 1; objDiv2.style.zlndex = 2; objDiv2.innerHTML = strDisplay; layerFront = 2;
}
else{
objDivl.className = "opacO"; objDiv2.style.zlndex = 1; objDivl.style.zlndex = 2; objDivl.innerHTML = strDisplay; layerFront = 1;
}
// © Начать переход _JSetClass(O);
m
Функция ChangeView() О служит двум основным целям. Во-первых, она создает HTML-документ для отображения данных, полученных из RSSлент; во-вторых, подготавливает элементы div к проявлению. Создание HTML-доку мента выполняется просто, поскольку мы используем стандартную структуру. Сложнее всего в этом деле гарантировать, что мы правильно отследили все кавычки и апострофы и не допустили ошибок.
Первой строкой текста, которую мы собираемся отобразить, является название канала RSS ®, которое хранится в первом элементе массива arrayMessage. Это название необходимо поместить в элемент span и соотнести с этим элементом имя класса CSS RSSFeed. Далее требуется отобразить заголовок статьи ©, обратившись ко второму элементу массива. Помещая заголовок в элемент span и сопоставляя с этим элементом имя класса CSS itemTitle, мы допускаем для заголовков определение различных стилей. Чтобы разделить заголовок и тело сообщения, между ними проводится горизонтальная черта.
Описание статьи О было записано в четвертом элементе массива arrayMessage. Мы отделяем это описание от следующего раздела, в котором
538 Часть IV. Ajax в примерах
находится последний собранный элемент статьи — ссылка ©; атрибуту HREF данной ссылки присваивается значение элемента URL. Вследствие этого пользователь видит текст '"View Page" (''Посмотреть страницу"), на котором он может щелкнуть мышью. Ссылка, привязанная к этому тексту, направит пользователя на Web-сайт RSS-ленты.
Далее мы хотим обновить отображаемый счетчик текущего сообщения, встроенный в наше RSS-приложение. Для этого мы изменяем свойство innerHTML 0 элемента spanCount, используя длину arrayMessage и счетчик текущего сообщения. Затем требуется подготовить элементы div ® к демонстрации переходов. Для инициализации элемента div значение zlndex задается так, чтобы данный элемент располагался поверх текущего, а класс соответствовал первому правилу CSS, определяющему степень прозрачности.
Загрузив текущее сообщение в элемент div, мы начинаем процесс проявления этого элемента. Для этого необходимо создать функцию, последовательно загружающую классы CSS; следовательно, мы вызываем функцию SetClassO ©.
13.4.3, Интеграция таймеров JavaScript
Процесс загрузки элемента div в поле зрения создает не резкую смену сообщений, а плавный переход между ними. Данный эффект достигается посредством изменения степени прозрачности слоя с помощью созданных ранее классов CSS. Благодаря этому мы можем наблюдать слой, располагающийся ниже данного (мы как бы смотрим сквозь тонированное стекло). Чтобы убрать из поля зрения все содержимое нижнего слоя, мы уменьшаем степень прозрачности верхнего.
Как отмечалось в разделе 13.4.1, для обработки затухания/проявления слоев мы применяем пять классов CSS. Использование классов позволит нам впоследствии добавить к переходу изменение цвета или любую другую модификацию стиля. В данном случае, как показано в листинге 13.13, мы просто циклически проходим по классам.
Листинг 13.13. Задание класса CSS и эффекта перехода
// |
О |
Объявить |
S e t C l a s s O |
f u n c t i o n S e t C l a s s ( x C l a s s ) { |
|||
// |
© |
Проверить |
шаг перехода |
if(xClass<5) {
// © Установить следующее значение className document . getEleraentById("divNews" +
// О Инициализировать таймер перехода
l a y e r F r o n t ) . c l a s s N a m e = " o p a c " + x C l a s s ;
timerAmt = |
s e t T i m e o u t ( " S e t C l a s s { " + |
(xClass+1) |
+ " ) " , t i m e C o l o r ) ; |
}
else{
// © Убрать класс CSS
document . getElementByld("divNews" + l a y e r F r o n t ) . c l a s s N a m e = " " ;
// О Увеличить счетчик
Глава 13 Создание приложений Ajax, не использующих сервер 539
currentMessage++;
/ / © Проверить следующее сообщение if(currentMessage>=arrayMessage.length)
currentMessage — 0;
//© Запустить таймер if(IbPaused)
timerSwitch = setTimeout( "ChangeView()",flipLength);
}
_ J |
|
- |
В листинге 13.13 показана функция Setciass() О, которой передается параметр xClass. Этот параметр позволяет отслеживать текущее состояние перехода, не используя никаких других глобальных переменных. Указанная функция вызывается на каждом шаге перехода и обновляет состояние, пока переход не будет завершен.
Поскольку мы работаем с пятью классами CSS, необходимо убедиться, что текущий шаг процесса перехода © не является пятым. Если это так, значит, для завершения перехода требуется применить еще один или несколько классов CSS, и мы применяем к элементу следующий класс CSS, обращаясь к атрибуту className ©.
Установив новый класс, необходимо создать таймер для вызова следующего шага. Используемый для этого метод setTimeout О имеет два параметра. Первый — это функция или выражение JavaScript, которое требуется выполнить, второй — время в миллисекундах до выполнения функции/выражения. В данном случае мы планируем вызвать функцию SetClass (), в качестве аргумента которой передается параметр состояния, значение которого увеличено на 1. Время ожидания задается согласно значению глобальной переменной flipLength, объявленной в разделе 13.3.1.
В ветке else сценария обрабатывается ситуация, когда мы уже прошли по всем пяти классам CSS, последовательно применив их к элементу div. Здесь мы вначале убираем из элемента div класс CSS ©. Параметр непрозрачности элемента устанавливается равным 100% (значение по умолчанию); таким образом, мы разрешаем элементу div полностью закрывать другой блок, расположенный на нижележащем слое.
Далее мы увеличиваем на 1 значение переменной currentMessage ©, позволив загружать следующее сообщение. Мы проверяем, чтобы номер этого сообщения © был больше числа сообщений, содержащихся в массиве arrayMessage. Если это так, мы запускаем с начала текущее сообщение. Таймер перезапускается, чтобы загрузить следующее сообщение по прошествии заданного промежутка времени ©. За вызов функции ChangeView() отвечает метод setTimeout, а время ожидания определяется глобальной переменной flipLength. Чтобы все сказанное было возможным, мы должны проверить, что значение глобальной переменной bPaused не равно true. Подробнее кодирование паузы будет рассмотрено в разделе 13.5.2.
Теперь переход между сообщениями готов, и мы можем проверить созданный сценарий. Если все работает нормально, мы должны увидеть счетчик

Глава 13 Создание приложений Ajax, не использующих сервер 541
имя и щелкаем на кнопке Add Feed ("Добавить ленту"). Большинство необходимых функциональных возможностей было реализовано в разделе 13.3; все, что от нас требуется сейчас (листинг 13.14), — запустить объект ContentLoader, который и добавит ленту, выбранную в элементе select.
Листинг 13.14. Функция JavaScript, загружающая дополнительные ленты RSS function LoadNews(){
var sel = document.Forml.ddlRSS; if(sel,options[sel.selectedlndex].className!="added"){
var url = sel.options[sel.selectedlndex].value;
|
sel.options[sel.selectedlndex].className="added"; |
|
var loaderl = new net.ContentLoader(url, |
|
BuildXMLResults); |
|
} |
_ ! |
- |
В приведенном листинге показана функция LoadNews (), которая запускается щелчком на кнопке btnAdd. Поскольку указатели URL дополнительных лент RSS мы получаем из элемента select, нам необходимо обращаться к значениям элемента ddlRSS.
Если мы собираемся добавлять RSS-ленты из элемента select, необходимо учесть возможность того, что эта лента уже добавлена. Одним из возможных решений здесь является добавление к опции класса CSS. Таким образом, в приложение необходимо включить проверку, гарантирующую, что лента RSS не была добавлена ранее. Если лента действительно является новой, мы берем значение выбранной опции и изменяем значение className на added.
При выполнении объекта ContentLoader мы передаем ему в качестве аргументов URL ленты и функцию BuildXMLResults (). Если в процессе произойдет какой-либо сбой, мы можем использовать принятое по умолчанию сообщение об ошибке объекта ContentLoader. Таким образом, сейчас мы можем загружать документ из списка, но, кроме того, нам требуется предусмотреть введение в этот список новых RSS-лент и связать с кнопкой обработчик
событий, приведенный в листинге 13.15. |
|
<select name="ddlRSS"> |
|
<option |
|
value="http://radio.javaranch.com/frank/rss.xml"> |
|
Frank</option> |
|
<option |
|
value="http://radio.javaranch.com/gthought/rss.xml"> |
|
Gregg</option> |
|
</select> |
|
<mput type="button" name="btnAdd" |
|
value="Add Feed" onclick="LoadNews()" /> |
« |