- •Введение
- •Несколько слов о книге
- •Глава 1. Каким должен бытъ Web-интерфейс
- •Действия пользователя при работе с приложением
- •Накладные расходы при работе в сети
- •Асинхронное взаимодействие
- •Независимый и переходный образы использования
- •Четыре основных принципа Ajax
- •Браузер имеет дело с приложением, а не с содержимым
- •Сервер доставляет данные, а не содержимое
- •Реальное кодирование требует порядка
- •Применение богатых клиентов Ajax
- •Системы, созданные с использованием Ajax
- •Google Maps
- •Альтернативные технологии
- •Macromedia Flash
- •Java Web Start
- •Резюме
- •Ресурсы
- •Основные элементы Ajax
- •JavaScript изучался не зря
- •Определение внешнего вида с помощью CSS
- •Селекторы CSS
- •Свойства стилей
- •Простой пример использования CSS
- •Обработка DOM с помощью JavaScript
- •Поиск узла DOM
- •Создание узла DOM
- •Добавление стилей к документу
- •Свойство innerHTML
- •Асинхронная загрузка с использованием XML
- •Элементы IFrame
- •Объекты XmlDocument и XMLHttpRequest
- •Использование фуниции обратного вызова для контроля запроса
- •Жизненный цикл процедуры поддержки запроса
- •Отличия Ajax от классических технологий
- •Резюме
- •Ресурсы
- •Порядок из хаоса
- •Образы разработки
- •Реструктуризация и Ajax
- •Во всем надо знать меру
- •Реструктуризация в действии
- •Варианты применения реструктуризации
- •Несоответствие браузеров: образы разработки Fagade и Adapter
- •Управление обработчиками событий: образ разработки Observer
- •Повторное использование обработчиков событий: образ разработки Command
- •Обеспечение единственной ссылки на ресурс: образ разработки Singleton
- •"Модель-представление-контроллер "
- •Серверная программа Ajax, созданная без применения образов разработки
- •Реструктуризация модели
- •Разделение содержимого и представления
- •Библиотеки независимых производителей
- •Библиотеки, обеспечивающие работу с различными браузерами
- •Компоненты и наборы компонентов
- •Элементы, располагаемые на стороне сервера
- •Резюме
- •Ресурсы
- •Применение архитектуры MVC к программам различных уровней
- •Применение архитектуры MVC к объектам, присутствующим в среде браузера
- •Представление в составе Ajax-приложения
- •Отделение логики от представления
- •Отделение представления от логики
- •Контроллер в составе Ajax-приложения
- •Классические JavaScript-обработчики
- •Модель обработки событий W3C
- •Реализация гибкой модели событий в JavaScript
- •Модель в составе Ajax-приложения
- •Использование JavaScript для моделирования предметной области
- •Взаимодействие с сервером
- •Генерация представления на основе модели
- •Отражение объектов JavaScript
- •Обработка массивов и объектов
- •Включение контроллера
- •Резюме
- •Ресурсы
- •Программы, выполняемые на сервере
- •Создание программ на стороне сервера
- •N-уровневые архитектуры
- •Управление моделью предметной области на стороне клиента и на стороне сервера
- •Принципы создания программ на сервере
- •Серверные программы, не соответствующие основным принципам разработки
- •Использование архитектуры Model!
- •Использование архитектуры на базе компонентов
- •Архитектуры, ориентированные на использование Web-служб
- •Частные решения: обмен данными
- •Взаимодействие, затрагивающее только клиентскую программу
- •Пример отображения информации о планетах
- •Взаимодействие, ориентированное на содержимое
- •Взаимодействие, ориентированное на сценарий
- •Передача данных серверу
- •Использование HTML-форм
- •Использование объекта XMLHttpRequest
- •Управление обновлением модели
- •Резюме
- •Ресурсы
- •Создание качественного приложения
- •Отклик программы
- •Надежность
- •Согласованность
- •Простота
- •Как получить результат
- •Предоставление сведений пользователю
- •Поддержка ответов на собственные запросы
- •Обработка обновлений, выполненных другими пользователями
- •Создание системы оповещения
- •Основные принципы оповещения
- •Реализация базовых средств оповещения
- •Отображение пиктограмм в строке состояния
- •Отображение подробных сообщений
- •Формирование готовой системы
- •Предоставление информации в запросах
- •Информация о новизне данных
- •Простой способ выделения данных
- •Выделение данных с использованием библиотеки Scriptaculous
- •Резюме
- •Ресурсы
- •JavaScript и защита браузера
- •Политика "сервера-источника"
- •Особенности выполнения сценариев в Ajax-приложении
- •Проблемы с поддоменами
- •Взаимодействие с удаленным сервером
- •Взаимодействие с Web-службами
- •Защита конфиденциальной информации
- •Вмешательство в процесс передачи данных
- •Организация защищенного НТТР-взаимодействия
- •Передача шифрованных данных в ходе обычного HTTP-взаимодействия
- •Управление доступом к потокам данных Ajax
- •Создание защищенных программ на уровне сервера
- •Ограничение доступа к данным из Web
- •Резюме
- •Ресурсы
- •Что такое производительность
- •Скорость выполнения JavaScript-программ
- •Определение времени выполнения приложения
- •Использование профилировщика Venkman
- •Оптимизация скорости выполнения Ajax-приложения
- •Использование памяти JavaScript-кодом
- •Борьба с утечкой памяти
- •Особенности управления памятью в приложениях Ajax
- •Разработка с учетом производительности
- •Простой пример управления памятью
- •Как уменьшить объем используемой памяти в 150 раз
- •Резюме
- •Ресурсы
- •Сценарий двойной комбинации
- •Недостатки клиентского решения
- •Недостатки клиентского решения
- •Архитектура клиента
- •Разработка взаимодействия клиент/сервер
- •Реализация сервера: VB.NET
- •Написание кода сервера
- •Представление результатов
- •Применение каскадных таблиц стилей
- •Дополнительные вопросы
- •Запросы при выборе нескольких элементов
- •Переход от двойного связного выбора к тройному
- •Реструктуризация
- •Новый и улучшенный объект netContentLoader
- •Создание компонента двойного списка
- •Резюме
- •Глава 10. Опережающий ввод
- •Изучаем опережающий ввод
- •Типичные элементы приложений опережающего ввода
- •Google Suggest
- •Ajax как средство опережающего ввода
- •Структура серверной части сценария: С#
- •Сервер и база данных
- •Тестирование серверного кода
- •Структура клиентской части сценария
- •HTML
- •JavaScript
- •Обращение к серверу
- •Дополнительные возможности
- •Реструктуризация
- •День 1: план разработки компонента TextSuggest
- •День 3: включаем Ajax
- •День 4: обработка событий
- •День 5: пользовательский интерфейс всплывающего окна с предлагаемыми вариантами
- •Итоги
- •Резюме
- •Эволюционирующий портал
- •Классический портал
- •Портал с богатым пользовательским интерфейсом
- •Создание портала с использованием Java
- •Таблица пользователя
- •Серверная часть кода регистрации: Java
- •Структура регистрации (клиентская часть)
- •Реализация окон DHTML
- •База данных окон портала
- •Серверный код окна портала
- •Добавление внешней библиотеки JavaScript
- •Возможность автоматического сохранения
- •Адаптация библиотеки
- •Автоматическая запись информации в базе данных
- •Реструктуризация
- •Определение конструктора
- •Адаптация библиотеки AjaxWindows.js
- •Задание команд портала
- •Выводы
- •Резюме
- •Понимание технологий поиска
- •Классический поиск
- •"Живой" поиск с использованием Ajax и XSLT
- •Возврат результатов клиенту
- •Код клиентской части сценария
- •Настройка клиента
- •Инициализация процесса
- •Код серверной части приложения: РНР
- •Создание XML-документа
- •Создание документа XSLT
- •Объединение документов XSL и XML
- •Совместимость с браузером Microsoft Internet Explorer
- •Совместимость с браузерами Mozilla
- •Последние штрихи
- •Применение каскадных таблиц стилей
- •Улучшение поиска
- •Поддержка браузерами Opera и Safari
- •Использовать ли XSLT
- •Решение проблемы закладок
- •Реструктуризация
- •Объект XSLTHelper
- •Компонент "живого" поиска
- •Выводы
- •Резюме
- •Считывание информации из внешнего мира
- •Поиск XML-лент
- •Изучение структуры RSS
- •Богатый пользовательский интерфейс
- •Чтение лент
- •HTML-структура без таблиц
- •Гибкое CSS-форматироеание
- •Глобальный уровень
- •Предварительная загрузка средствами Ajax
- •Богатый эффект перехода
- •Правила прозрачности, учитывающие индивидуальность браузеров
- •Реализация затухающего перехода
- •Интеграция таймеров JavaScript
- •Дополнительные возможности
- •Введение дополнительных лент
- •Интеграция функций пропуска и паузы
- •Как избежать ограничений проекта
- •Обход системы безопасности браузеров Mozilla
- •Изменение масштаба приложения
- •Реструктуризация
- •Модель приложения
- •Представление приложения
- •Контроллер приложения
- •Выводы
- •Резюме
- •Отладчики
- •Для чего нужен отладчик
- •Средство Safari DOM Inspector для Mac OS X
- •Ресурсы
- •JavaScript — это не Java
- •Формирование объектов
Глава 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.