Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax_v_deystvii.pdf
Скачиваний:
34
Добавлен:
05.03.2016
Размер:
5.83 Mб
Скачать

400 Часть IV. Ajax в примерах

10.3.3. Обращение к серверу

Объект xMLHttpRequest позволяет передавать текст из текстового окна на сервер и получать результаты с сервера. В данном случае мы отправляем данные на сервер, поскольку серверная страница, созданная в листинге 10.1 обращается к элементам, отправленным в форме. Как показано в листинге 10.11, в объекте ContentLoader требуется задать положение страницы на сервере, функцию, вызываемую после завершения выполнения этого объекта а также параметры формы, которые будут отправлены форме.

Листинг 10.11. Функция Ajax, используемая для отправки данных на сервер \ function TypeAhead(xStrText){

 

v a r

s t r P a r a m s

• "q= " + x S t r T e x t +

 

"&where=" + theTextBox.obj.matchAnywhere;

 

v a r

l o a d e r l =

new n e t . C o n t e n t L o a d e r (

 

t h e T e x t B o x . o b j . s e r v e r C o d e , B u i l d C h o i c e s , n u l l , " P O S T " , s t r P a r a m s ) ;

)

 

 

ш

При вызове функции TypeAhead() из функции GiveOptions () мы (для выполнения поиска) передаем ей текущее значение строки из текстового окна. Нам требуется создать строку параметров, strParams, содержащую значение строки в текстовом окне, а также булево значение matchAnywhere. Оба названных элемента использовались в листинге 10.1 для получения результатов поиска. Затем мы начинаем загружать документ, вызывая ContentLoader. Для этого в качестве двух первых параметров ContentLoader мы отправляем URL серверной страницы и функцию JavaScript, которая будет вызвана при возврате результатов. Третий параметр равен null, поскольку мы собираемся игнорировать все сообщения об ошибках. Благодаря этому поле с опережающим вводом будет выглядеть подобно обычному текстовому полю. Последние два параметра указывают ContentLoader поместить данные на сервер и отправить параметры формы, содержащиеся в строке strParams.

После возврата результатов с сервера вызывается функция BuildChoicest), позволяющая завершить обработку данных на стороне клиента. Разрабатывая код серверной части сценария, мы возвращали результаты в виде двухмерного массива JavaScript. Этот массив содержал пары "текстзначение" предлагаемых вариантов выбора. Тем не менее в отклике мы имеем просто строку символов. Следовательно, нужно взять эту возвращенную строку и отформатировать ее как массив JavaScript. Соответствующая функция, обрабатывающая информацию, полученную от ContentLoader с помощью метода eval(), приводится в листинге 10.12.

Листинг 10.12. Преобразование свойства responseTextB массив JavaScript function BuildChoices(){

var strText = this.req.responseText; eval(strText); BuildList(strLastValue); bMadeRequest = false;

Глава 10. Опережающий ввод 401

Свойство responseText возвращенного объекта запроса позволяет получать текст из запроса Ajax. Чтобы данную возвращенную строку можно было использовать в нашем коде JavaScript, необходим метод evalO, нужным образом обрабатывающий строку, переданную ему в качестве аргумента. В данном случае этот метод распознает, что строка представляет собой объявление переменной для создания нового массива. Если бы мы просто записали строку на страницу, она не была бы доступна для выражения JavaScript. Как правило, разработчики не приветствуют использование метода eval {) из-за его известной медлительности. Однако в данном случае он позволяет отказаться от циклического прохода XML-документа на стороне клиента для получения значений. Теперь мы можем вызвать функцию BuildListО, форматирующую и отображающую возвращенные результаты. Кроме того, мы устанавливаем значение булевой переменной bMadeRequest равным false, сообщая оставшейся части сценария, что запрос к серверу завершен.

Создание окна результатов

Использование JavaScript для обработки текущего документа обычно считается признаком DHTML. В данном примере мы принимаем двухмерный массив и превращаем его в строки текста на экране. Вернувшись к рис. 10.4, мы увидим список слов с подчеркнутыми фрагментами — частями текста, совпадающими с тем, что ввел в окне пользователь. В нашем приложении данные слова будут отображаться как элемент span.

Функция BuildList (), созданная в листинге 10.13. использует три функции: поиск слов по шаблону, установку положения окна и форматирование результатов с использованием подчеркивания.

Листинг 10.13. Форматирование результатов в формат отображения

function BuildList(theText){

 

SetElementPosition(theTextBox);

 

// Установить положение элемента

,

var theMatches - MakeMatches(theText);

 

// Отформатировать соответствия

 

theMatches = theMatches.join().replace{/\,/gi,"");

 

// Показать результаты

 

if(theMatches.length > 0){

 

document.getElementByld("spanOutput")

 

.innerHTML = theMatches;

 

document.getElementByldf

 

"OptionsList_0").className =

 

"spanHighElement";

 

currentValueSelected = 0;

 

bNoResults = false;

 

}

 

//He показывать варианты

 

else{

 

currentValueSelected = -1;

 

bNoResults = true;

 

if{theTextBox.obj.showNoMatchMessage)

 

document.getElementByld(

 

"spanOutput").innerHTML =

 

402 Часть IV. Aj'ax в примерах

 

 

 

 

"<span

class='noMatchData'>"

+

 

 

 

theTextBox . ob j

 

 

 

 

. noMa.tchinqDataMessa.ge +

 

 

 

 

" < / s p a n > " ;

 

 

 

else

HideTheBox();

 

 

}

 

 

 

 

_ J

 

 

 

 

,

 

 

 

 

Функция BuildListO, приведенная в листинге 10.13, принимает строку введенную пользователем, и форматирует результаты. Первое, что мы должны сделать, — это динамически разместить элемент span непосредственно под текстовым окном, в котором реализован опережающий ввод. Для этого мы вызываем функцию SetElementPositionO (подробнее об этом — ниже, в разделе "Динамическая установка положения элемента"). Расположив элемент span в нужном месте страницы, мы можем манипулировать массивом, отыскивая соответствия с использованием функций MakeMatches() (речь о ней пойдет в разделе "Использование регулярных выражений"). Эта функция возвращает массив, содержащий только информацию, согласующуюся с введенной пользователем. В отличие от большинства интерактивных приложений опережающего ввода мы не требуем обработки на сервере, а с помощью JavaScript ограничиваем результаты на стороне клиента.

Функция MakeMatches () форматирует результаты и возвращает их в виде массива. Затем мы превращаем этот массив в строку, используя метод join. Если длина строки больше 0, тогда мы можем отображать результаты в виде списка, задав его свойство innerHTML. Затем мы выбираем первый элемент списка и устанавливаем его свойство className, чтобы выделить этот элемент.

Если возвращенный массив не содержит данных, мы отображаем сообщение "до matches" ("нет соответствий"), если текстовое окно это позволяет. Мы знаем, что соответствий нет, поскольку проверяем, что значение currentselectedValue установлено равным - 1 . Если никаких сообщение отображать не требуется, мы скрываем окно.

Итак, мы завершили выполнение функции BuildListO и теперь можем создавать все функции, которая она вызывает. Первой из них рассмотрим SetElementPosition().

Динамическая установка положения элемента

За расположение текстового окна ввода на странице отвечает процессор браузера. Создавая раскрывающийся список с предложениями DHTML, мы желаем выровнять его точно по текстовому окну. Поскольку нам требуются координаты раскрывающегося списка, то приходится решать довольно сложную задачу нахождения положения элемента (в нашем случае — текстового окна), которое не задано жестко. Такие элементы размещаются на странице относительно — без указания абсолютных координат верхнего левого угла. Попытавшись сослаться на левую верхнюю точку текстового окна, мы получим в ответ неопределенную строку. Следовательно, для определения по-

Глава 10. Опережающий ввод 403

дожения элемента нужно использовать возможности JavaScript — тогда мы сможем выровнять окно с вариантами выбора непосредственно под текстовым окном. Динамическое размещение элемента списка с выравниванием его под текстовым окном представлено в листинге 10.14.

Листинг 10.14. Динамическое нахождение положения элемента

function SetElementPosition(theTextBoxInt){ var selectedPosX - 0;

var selectedPosY = 0;

var theElement « theTextBoxInt; if (!theElement) return;

var theElemHeight = theElement.offsetHeightr var theElemWidth = theElement.offsetWidth; while(theElement != null)I

selectedPosX += theElement.offsetLeft; selectedPosX += theElement.offsetTop; theElement = theElement.offsetParent;

}

xPosElement • document.getElementById("spanOutput"); xPosElement.style.left = selectedPosX; if(theTextBoxInt.obj.matchTextBoxWidth)

xPosElement.style.width • theElemWidth; xPosElement.style.top = selectedPosY + theElemHeight xPosElement.style.display = "block"; if(theTextBoxInt.obj.useTimeout){

xPosElement.onmouseout = StartTimeout;

xPosElement.onmouseover = EraseTimeout;

}

else{

xPosElement.onmouseout • null; xPosElement.onroouseover *= null;

}

_ }

 

_ и

В листинге 10.14 мы объявляем функцию SetElementPosition(), принимающую один параметр: ссылку на объект текстового окна. Значения двух локальных переменных, selectedPosX и selectedPosY, устанавливаются равными 0. Эти две целочисленные переменные используются для расчета положения элемента. Ссылка на текстовое окно задается в другой локальной переменной. Для получения ширины и высоты текстового окна применяются свойства offsetHeight и offsetWidth.

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

Получив положение текстового окна, мы можем извлекать объектную ссылку списка, использованную для задания верхней левой точки раскрывающегося списка с предложениями. Затем мы изучаем объект obj текстового окна, проверяя, должно ли его свойство width согласовываться с шириной текстового окна. Если соответствующее булево значение равно true, мы

404 Часть IV Ajax в примерах

устанавливаем ширину списка. Если значение равно false, ширина задается равной значению, указанному в таблице стилей. Последнее, что от нас требуется, — изменить параметр видимости списка, чтобы он больше не скрывался от пользователя. Для этого свойство display устанавливается равным block.

Подытожим: мы правильно разместили список и сделали его видимым пользователю. Теперь можно разрабатывать код, заполняющий список предположениями.

Использование регулярных выражений

Поскольку приложение предполагает поиск фрагментов строки, напомним, что одним из лучших инструментов поиска соответствий являются регулярные выражения, обладающие к тому же хорошей гибкостью. Функция МакеMatchesO, которую мы создадим ниже, позволяет находить в списке вариантов слова, согласующиеся с тем, что пользователь ввел в текстовом окне. Это означает, что мы можем обойтись без обращения к серверу после каждого нажатия клавиши, так как необходимый отбор вариантов производится функцией на стороне клиента. Код, приведенный в листинге 10.15, позволяет сэкономить полосу пропускания, ограничивая множество полученных результатов.

.•••;: Листинг 10.15. Ограничение результатов с помощью регулярных выражений var countForld = 0;

function MakeMatches(xCompareStr){ countForld = 0;

var matchArray = new Array();

var regExp = new RegExp(theTextBox.obj.regExAny +

xCompareStr,theTextBox.obj.regExFlags); for(i: =0;i<arrOptions.length;i++) {

var theMatch = arrOptions[i][0].match(regExp); if(theMatch){

matchArray[matchArray.length]= CreateUnderline(arrOptions[i][0], xCompareStr,i);

}

 

m

Мы создаем функцию MakeMatches (), которая принимает один параметр: строку, введенную пользователем. Затем мы присваиваем переменной countForld значение 0 и создаем локальную переменную массива matchArray. (Обратите внимание на то, что countForld — это глобальная переменная. Это позволяет немного упростить структуру примера. Позже мы избавимся от этой переменной.) Суть данной функции заключается в создании регулярного выражения, находящего варианты, согласующиеся с тем, что вводит пользователь. Поскольку мы уже определили параметры для регулярного выражения, создавая код, приведенный в листинге 10.6, сейчас нам требуется сослаться на объект текстового окна. Далее мы добавим свойство regExAny, позволяющее согласовывать текст, начиная с начала или любого места стро-

Глава 10 Опережающий ввод 405

$#• Свойство regExFlags позволяет определять, следует ли при поиске соответствий игнорировать регистр.

По завершении работы с регулярными выражениями мы последовательно проходим массив arrOptions, проверяя, действительно ли находящиеся в нем варианты согласуются с нашим регулярным выражением. Если да, то МЫ добавляем текст в массив matchArray после вызова функции CreateUnderline(), которая форматирует код, отображаемый на экране.

Завершив проход по всем элементам массива, мы возвращаем найденные соответствия основной функции BuildList{), после чего отобранные варианты отображаются на экране. Функция MakeMatches() предоставляет механизм кэширования, о котором мы уже говорили выше. Вместо повторного обращения к серверу за ограничением множества результатов после каждой набранной буквы мы используем регулярные выражения, сокращая уже имеющийся набор вариантов. Последним этапом обработки результатов является вызов функции CreateUnderline (), которая обеспечивает нужное форматирование.

Обработка строк

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

Листинг 10.16. Обработка строк средствами JavaScript

var undeStart = "<span class='spanMatchText'>"; var undeEnd = "</span>";

var selectSpanStart = "<span style=lwidth: 100%,-display:block; ' class='spanNormalElement' onmouseover=•SetHighColor(this)' ";

var selectSpanEnd ="</span>";

function CreateUnderline(xStr,xTextMatch,xVal){ selectSpanMid = "onclick='SetText(" + xVal + " ) ' " +

"id='OptionsList_"+countForId+ "'theArrayNumber='"+ xVal +'">"; var regExp = new RegExp{theTextBox.obj.regExAny +

xTextMatch,theTextBox.obj.regExFlags); var aStart = xStr.search(regExp);

var matchedText = xStr.substring(aStart,

aStart + xTextMatch.length);

countForId++;

return selectSpanStart + selectSpanMid + xStr.replace(regExp,undeStart + matchedText + undeEnd) + selectSpanEnd;

406Часть IV. Ajax в примерах

Влистинге 10.16 определяются две переменные, которые содержат строки, используемые для привязки класса CSS к фрагменту текста, согласующемуся с заданной строкой. Благодаря этому можно легко определить :тиль нужного текста. Первая переменная, undestart, содержит открываощий дескриптор span; вторая, undeEnd, — соответствующий закрываюций дескриптор.

Следующие две переменные формируют контейнер для всей строки. Этот сонтейнер позволяет манипулировать цветом фона и определять, щелкнул ли юльзователь на ячейке. Чтобы визуально выделить ячейку, на которую на- !еден указатель мыши, мы добавили в переменную selectSpanStart событие louseover. Переменная selectSpanEnd представляет собой закрывающий декриптор элемента span.

Функция CreateUnderline() вызывается функцией MakeMatches<), коюруго мы написали чуть выше. MakeMatches () принимает три параметра: троку, введенную пользователем, текст предлагаемого варианта, а также начение данной опции. Используя переданные данные, можно создать об- •аботчик onclick и добавить к списку идентификатор. Обработчик событий nclick позволяет выбрать один из предложенных вариантов, а идентифиатор — использовать DOM для выбора варианта из списка.

Для сопоставления текста, введенного пользователем, с фрагментами редлагаемых вариантов мы снова используем регулярные выражения. Таим образом мы можем вставить в строку созданные блоки с подчеркиванием. [тобы определить, в каком месте строки находится соответствие, применяет- ^ метод search. Найдя положение искомой строки, мы можем получить подгроку, в которой можно сохранить исходное форматирование. Далее значене счетчика countForld увеличивается на 1, и мы возвращаем отформатирошную строку, объединяя все созданные элементы span. Теперь текст отфоратирован, осталось дописать классы CSS, добавленные к элементам span.

Элементам span были присвоены имена классов CSS, поэтому от нас 3 требуется вручную редактировать код JavaScript, изменяя определенные юйства текста. Это позволяет подогнать текстовое окно с опережающим юдом под любую цветовую схему, изменив несколько правил CSS.

span.spanMatchText{ text-decoration: underline;

font-weight: bold; } span.spanNormalElement{ background: #C0COCO; } span.spanHighElement{ background: #000040;

color: white; cursor: pointer; } span.noMatchData{ font-weight: bold; color: #0000FF; }

Напомним (см. рис. 10.4), что текст списка, согласующийся с текстом на, подчеркнут и выделен полужирным. Оба этих свойства заданы в прале CSS span.spanMatchText. Стиль блока по умолчанию (серый цвет фо ) представлен в правиле span. spanNormalElement. К выбранному элементу именяется правило CSS span.spanHighElement. На рисунке также видно, о фон имеет темно-серый цвет, а текст представлен белым. Кроме того, рсор был заменен указателем, поэтому пользователь знает, что вариант жно выбрать с помощью мыши. К любому из этих элементов можно добагь и другие свойства: шрифт, размер, границу и т.д. Итак, мы сформулиро-

Глава 10 Опережающий ввод 407

вали правила таблицы стилей и закончили работу по выводу результатов на экран. Осталось только реализовать обработку клавиш со стрелками и клавиши <Enter> и создать таймер, который будет скрывать список вариантов в моменты бездействия.

Выделение опций

ранее в этой главе мы показали, как отслеживать нажатия клавиш со стрелками вверх и вниз, чтобы пользователь перемещал selectedlndex вверх-вниз по списку, не прибегая к помощи мыши. При нажатии клавиш со стрелками мы получали 1 ("сместить выбор вниз") или -1 ("сместить выбор вверх"). При перемещении выбора мы применяем правила классов CSS к элементам span. Кроме того, мы создали глобальную переменную currentValueSelected, в которую помещаем номер текущей позиции. Функция MoveHighlight(), приведенная в листинге 10.17, предлагает более богатый пользовательский интерфейс, поскольку взаимодействует и с мышью, и с клавиатурой.

:Листинг 10.17. Изменение имен классов CSS с помощью JavaScript

function MoveHighlight(xDir){

if (currentValueSelected >«= 0){

newValue = parselnt(currentValueSelected) + parselnt{xDir); if(newValue > -1 && newValue < countForld){

CurrentValueSelected = newValue;

SetHighColor (null);

1

}

}

function SetHighColor(theTextBox){ if(theTextBox){ currentValueSelected =

theTextBox.id.slice(theTextBox.id.indexOf("_")+1, theTextBox.id.length);

}

for{i = 0; i < countForld; i++){ document.getElementById('OptionsList_' + i).className =

1spanNormalElement';

}

document. getElementByld ( ' OptionsList__' + currentValueSelected).className = 'spanHighElement';

_ }

 

m

Функция MoveHighlight t) позволяет пользователю использовать для выбора клавиши со стрелками вверх и вниз. Эта функция принимает один параметр, xDir, обозначающий направление, в котором должно сместиться выделение. Прежде всего мы должны убедиться, что у нас есть варианты на выбор и можно получить новое значение. Далее мы проверяем, что это новое значение принадлежит разрешенному диапазону. При положительном ответе мы устанавливаем состояние currentValueSelected и переходим к следующей функции, SetHighColor (), выделяя новую выбранную позицию.

408 Часть IV. Ajax в примерах

Функция SetHighColor() вызывается двумя различными событиями: нажатием клавиш со стрелками и наведением указателя мыши на позицию (обработчик событий onmouseover). Эта функция предназначена для удаления подсветки с последней выбранной опции и добавления ее к новой выбранной позиции. Событие onmouseover (см. листинг 10.16) принимает в качестве аргумента объект блока предложенных вариантов; следовательно, мы должны отсечь идентификатор и получить порядковый номер блока. Требуемое значение передают клавиши со стрелками, поэтому нам не нужно ничего делать так как функция moveHighlight () уже установила currentValueSelected.

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

Установка выбранного значения

Мы разрабатываем сценарий, который позволил бы пользователям, выбирая из предложенных вариантов, уменьшать объем работы, требуемой для заполнения поля формы. Для этого нам нужно взять номер выбранного пользователем элемента и установить текст в текстовом окне и значение в скрытом текстовом поле. Чтобы поведение нашего списка вариантов было похоже на поведение элемента HTML select, мы разработали три функции, приведенные в листинге 10.18.

Листинг 10.18. Обработка нажатия клавиш со стрелками и щелчков мышью

function SetText(xVal){

theTextBox.value = arrOptions[xVal][0]; //set text value theTextBox.obj .hidden, value •» arrOptions [xVal] [1] ; document.getElementByldf"spanOutput").style.display = "none"; currentValueSelected = -1; //remove the selected index

}

function GrabHighlighted(){ if(currentValueSelected >= 0){

 

xVal = document.getElementByldf"OptionsList_" +

 

 

 

currentValueSelected).getAttribute("theArrayNumber");

 

 

 

SetTextfxVal);

 

 

 

HideTheBoxO ;

 

 

}

 

 

}

 

 

function HideTheBoxf){

 

 

 

document.getElementByldf"spanOutput").style.display = "none";

 

 

 

currentValueSelected = -1;

 

 

_J EraseTimeout();

-

 

 

 

 

Глава 10 Опережающий ввод 409

Функция GrabHighlighted() позволяет получить текст и значение выбранной позиции. Итак, вначале нам нужно проверить, выбрал ли пользователь значение. Если да, тогда мы получаем порядковый номер массива arrOptions, в котором располагается текст. Для этого мы берем установленное ранее значение атрибута theArrayNumber, затем вызываем функцию SetText (), устанавливающую текст значения выбранной опции в соответствующие элементы формы.

Функция SetText () использует порядковый номер, переданный ей как параметр для индексирования массива arrOptions. Видимый пользователю текст устанавливается путем индексирования первого индекса массива. Скрытый элемент формы получает номер второго элемента, записанного в массиве. После того как мы извлечем значения, мы удалим список вариантов с экрана, вызвав функцию HideTheBox ().

Функция HideTheBox () позволяет удалять блок spanOutput из поля зрения. Для этого мы обращаемся к блоку и устанавливаем его свойство style .display равным none. Для удаления выбранного индекса присваиваем переменной currentValueSelected значение - 1 . Все запущенные таймеры удаляются посредством функции EraseTimeout(), разработкой которой мы займемся в следующем разделе.

Использование таймеров JavaScript

Этот раздел является последним, в которым фигурирует JavaScript. Написав его, мы завершим создание приложения опережающего ввода, и вы сможете немного отдохнуть от всего этого кода. Итак, таймер, приведенный в листинге 10.19, является еще одним элементом, делающим интерфейс пользователя немного богаче. Он используется для того, чтобы укрыть блок выбора после определенного периода бездействия. В данном случае мы используем метод JavaScript setTimeout(), выполняющий выражение по прошествии определенного времени. Это время задается в миллисекундах и привязывается к объекту, созданному в листинге 10.6. Отметим, что функция setTimeout () будет вызвана только в том случае, если присвоить параметру объекта useTimeout значение true.

Листинг 10.19. Присоединение и удаление запланированных событий :

function EraseTimeout(){ clearTimeout(isTiming); isTiming • false;

}

function StartTimeout{){

isTiming = setTimeout("HideTheBox()", theTextBox.obj.theVisibleTime);

Функция StartTimeout () устанавливает таймер при выполнении функции. Для инициализации таймера мы присваиваем переменной isTiming значение setTimeout. Метод setTimeout должен вызвать функцию HideTheBox () по прошествии установленного времени, которое задается с помощью

theVisibleTime.

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