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

Глава 4. Web-страница в роли приложения 161

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

На этом мы заканчиваем работу над представлением. В следующем разделе будет рассмотрена роль контроллера в рамках архитектуры MVC и его реализация в Ajax-приложении посредством JavaScript-обработчиков событий.

4.3. Контроллер в составе Ajax-приложения

Контроллер в архитектуре MVC выполняет роль посредника между моделью и представлением. В программе с графическим пользовательским интерфейсом, в частности, в составе клиентских средств Ajax, контроллер состоит из обработчиков событий. Эволюция клиентских программ для Web привела к тому, что в современных браузерах поддерживаются две различные модели событий. Одна из них, классическая модель, относительно проста, однако в настоящее время происходит ее замена новой моделью обработки, спецификация которой разработана W3C. На момент написания данной книги новая модель была по-разному реализована в различных браузерах, и ее использование было сопряжено с проблемами. В данном разделе мы обсудим обе модели.

4.3.1. Классические JavaScript-обработчики

Реализация JavaScript в Web-браузерах позволяет определять код, выполняемый в ответ на событие. Обычно событиями являются действия с мышью или клавиатурой. В современных браузерах, поддерживающих Ajax, обработчики этих событий можно связать с большинством элементов, отображающихся на экране. Обработчики обеспечивают взаимодействие визуального пользовательского интерфейса, т.е. представления, и бизнес-объектов, составляющих модель.

Классическая модель поддержки событий достаточно проста и используется с момента появления JavaScript. В большинстве элементов DOM предусмотрены свойства, позволяющие связывать с ними функции обратного вызова. Например, для того, чтобы присоединить функцию, которая будет вызываться по щелчку на элементе myDomElement, мы должны написать следующее выражение:

myDomElement.onclick=showAnimatedMonkey

Здесь myDomElement — любой элемент DOM, к которому мы имеем доступ из программы. Функция showAnimatedMonkey () — это обычная функция JavaScript, которая определяется следующим образом:

162

Часть II. Основные подходы к разработке приложений

Таблица 4.1. Свойства, позволяющие связывать обработчики событий

с элементами DOM

 

Свойство

Описание

onmouseover

Активизируется, когда курсор мыши попадает в область,

 

 

занимаемую элементом

onmouseout

Активизируется, когда курсор мыши покидает область,

 

 

занимаемую элементом

onmousemove

Активизируется при каждом перемещении курсора мыши

 

 

в пределах области, занимаемой элементом

onclick

 

Активизируется щелчком в области, занимаемой элементом

onkeypress

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

 

 

которым находится курсор, обладает фокусом ввода

onfocus

 

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

onblur

 

Активизируется, когда видимый элемент теряет фокус ввода

f u n c t i o n showAnimatedMonkey(){

 

// Сложный

код для поддержки движущихся изображений }

Заметьте, что при связывании обработчика события мы не вызываем функцию, а передаем объект Function, поэтому круглые скобки после имени не указываются. Ниже приведен пример распространенной ошибки.

myDomElement.onclick=showAnimatedMonkey();

Для некоторых программистов такая запись более привычна, однако приводит к результату, отличному от желаемого. Функция будет выполняться не в результате щелчка на элементе DOM, а в процессе присвоения, свойство onclick получит значение, возвращаемое функцией. Возможно, это сложное решение, при котором в результате выполнения одной функции возвращается ссылка на другую, но подобный подход используется достаточно редко и требует от программиста высокой квалификации. Еще раз покажем, как правильно выполнять связывание обработчика.

myDomElement.onclick=showAnimatedMonkey;

В результате ссылка на функцию обратного вызова присваивается элементу DOM, сообщая ему о том, что функция должна быть вызвана щелчком на элементе. Элемент DOM может содержать несколько свойств, предназначенных для связывания обработчиков событий. Свойства, часто используемые при создании графического пользовательского интерфейса, описаны в табл. 4.1. Встречавшиеся ранее свойства XMLHttpRequest.onreadystate и window. onload также предназначены для связывания обработчиков.

Механизм обработки событий имеет одну особенность, которую часто не учитывают программисты, использующие JavaScript. Эту особенность необходимо знать всем, кто собирается приступать к работе над клиентскими программами Ajax.

Глава 4. Web-страница в роли приложения 163

Предположим, что мы связали с элементом DOM функцию обратного вызова, используя для этого свойство onclick. Эта функция получает управление по щелчку мышью на элементе DOM. Однако контекст функции (т.е. значение, получаемое переменной this) соответствует узлу DOM, который получаем событие (объекты Function в языке JavaScript обсуждаются в приложении Б). Контекст зависит от того, как была объявлена функция. Этот эффект может привести к возникновению ошибки.

Рассмотрим пример. Предположим, что мы определили класс, представляющий объект кнопки. В нем указан узел DOM, обработчик обратного вызова и значение, которое отображается после щелчка мышью на кнопке. Все экземпляры кнопки одинаковым образом реагируют на щелчок, потому мы определяем обработчик как метод класса. Теперь обратимся к JavaScriptкоду. Конструктор кнопки имеет следующий вид:

function Button(value,domEl){ this.domEl=domEl; this.value=value;

this.domEl.onclick=this.clickHandler;

}

Определим обработчик в составе класса Button.

Button.prototype.clickHandler=function(){ alert(this.value);

}

Такое решение выглядит достаточно просто, но полученный результат — совсем не тот, которого можно было бы ожидать. В окне с сообщением отображается не значение, которое было передано конструктору, а сообщение undefined. Это происходит по следующей причине. Браузер вызывает функцию clickHandler () по щелчку на элементе DOM, поэтому задает контекст элемента DOM, а не JavaScript-объекта Button. Таким образом, this.value ссылается на свойство value элемента DOM, а не объекта Button. Это трудно предположить, глядя на определение обработчика события, не правда ли?

Разрешить данную проблему можно, модифицировав конструктор следующим образом:

function Button(value,domEl){ this.domEl=domEl; this.value=value; this.domEl.buttonObj =this ;

this.domEl.onclick=this.clickHandler;

}

Элемент DOM по-прежнему не имеет свойства value, но содержит ссылку на объект Button, который используется для получения значения. Кроме того, нам надо изменить обработчик события.

Button.prototype.clickHandler=function(){ var buttonObj=this.buttonObj;

var value=(buttonObj && buttonObj.value) ? buttonObj.value : "unknown value";

alert(value);

}

164 Часть II. Основные подходы к разработке приложений

Узел DOM ссылается на объект Button, который ссылается на свойство value, и наш обработчик получает требуемое значение. Мы можем присвоить значение непосредственно узлу DOM, но, связывая ссылку с базовым объектом, мы получаем возможность работать с элементами произвольной сложности. Следует заметить, что в данном случае был реализован "минивариант" архитектуры MVC, согласно которой элемент DOM, выполняющий функции просмотра, отделен от объекта поддержки, являющегося моделью.

Мы рассмотрели классическую модель обработки событий. Основной недостаток этой модели состоит в том, что она допускает для элемента наличие лишь одной функции обработки. При обсуждении образа разработки Observer в главе 3 мы говорили о том, что может потребоваться связать с элементом Observable любое количество элементов Observer. Указанное здесь ограничение не является серьезным препятствием в работе над простым сценарием, но при переходе к более сложным клиентам Ajax оно может существенно помешать нам. Этот вопрос будет подробно рассмотрен в разделе 4.3.3, а пока перейдем к новой модели обработки событий.

4.3.2. Модель обработки событий W3C

Модель поддержки событий, предложенная W3C, обеспечивает большую степень гибкости, но она сложна в использовании. Согласно этой модели с элементом DOM может быть связано произвольное количество обработчиков. Более того, если действие происходит в области пересечения нескольких элементов, обработчики каждого из них имеют шанс получить управление и запретить остальные вызовы из стека событий. Этот эффект принято называть "проглатыванием" (swallowing) событий. Согласно спецификации осуществляются два прохода по стеку событий: первый — от внешнего элемента к внутреннему (т.е. от элемента, представляющего документ, к элементам, содержащимся в нем), в ходе которого происходит заполнение стека, а второй — от внутреннего элемента к внешнему. На практике различные браузеры реализуют подобное поведение по-разному.

В браузерах Mozilla и Safari функции обратного вызова, предназначенные для обработки событий, связываются с помощью функции addEventListener(), а для разрыва связи осуществляется обращение к removeEventListener (). Браузер Internet Explorer предоставляет для этих целей функции attachEvent () и detachEvent (). Объект xEvent, разработанный Майком Фостером (Mike Foster) и входящий в состав библиотеки х, предпринимает попытку реализовать средства обработки событий, маскирующие различия между браузерами. Для этой цели он создает единую точку входа в соответствии с образом разработки Fagade (см. главу 3).

Существуют также различия между браузерами, касающиеся обращений к обработчикам обратного вызова. В браузерах Mozilla, как и в классической модели обработки, контекст вызываемой функции определяется элементом DOM, получившим событие. В Internet Explorer контекст всегда определяется объектом Window, и в результате невозможно выяснить, какой элемент DOM вызвал функцию обработки. При написании обработчиков необходимо учитывать указанные различия, даже в случае применения объекта xEvent.

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