Benken e samkov g ajax programmirovanie dlya interneta

Page 1

Елена Бенкен Геннадий Самков

Санкт-Петербург «БХВ-Петербург» 2009


УДК ББК

681.3.06 32.973.26-018.2 Б46

Б46

Бенкен, Е. С. AJAX: программирование для Интернета / Е. С. Бенкен, Г. А. Самков. — СПб.: БХВ-Петербург, 2009. — 464 с.: ил. + (СD-ROM) ISBN 978-5-9775-0428-7

Описана технология AJAX и показаны возможности, которые открываются перед разработчиком с ее применением. Рассмотрена объектная модель документа: DOM в JavaSript и DOM-функции в PHP. Изложены основы языка XML и формат JSON. Показан принцип генерации асинхронных запросов к серверу средствами JavaScript. Сделан обзор основных JavaScript-библиотек: Prototype, Scriptaculous, ExtJS и jQuery. Подробно рассмотрены популярные и перспективные библиотеки ExtJS и jQuery: описана объектная модель языка JavaScript, на которой базируются эти библиотеки; применение AJAX-запросов; обработка событий и др. Приведено большое количество практических примеров. Компакт-диск содержит дистрибутивы Web-сервера, модуля PHP и сервера MySQL, исходные коды описываемых библиотек, распространяемых на основании лицензии GPL, а также примеры из книги. Для Web-программистов

УДК 681.3.06 ББК 32.973.26-018.2 Группа подготовки издания:

Главный редактор Зам. главного редактора Зав. редакцией Редактор Компьютерная верстка Корректор Дизайн обложки Оформление обложки Зав. производством

Екатерина Кондукова Игорь Шишигин Григорий Добин Анна Кузьмина Натальи Караваевой Виктория Пиотровская Инны Тачиной Елены Беляевой Николай Тверских

Лицензия ИД № 02429 от 24.07.00. Подписано в печать 04.03.09. Формат 70×1001/16. Печать офсетная. Усл. печ. л. 37,41. Тираж 2000 экз. Заказ № "БХВ-Петербург", 190005, Санкт-Петербург, Измайловский пр., 29. Санитарно-эпидемиологическое заключение на продукцию № 77.99.60.953.Д.003650.04.08 от 14.04.2008 г. выдано Федеральной службой по надзору в сфере защиты прав потребителей и благополучия человека. Отпечатано с готовых диапозитивов в ГУП "Типография "Наука" 199034, Санкт-Петербург, 9 линия, 12 ISBN 978-5-9775-0428-7

© Бенкен Е. С., Самков Г. А., 2009 © Оформление, издательство "БХВ-Петербург", 2009


Оглавление Введение .................................................................................................................. 3

Терминология .......................................................................................................... 3 Структура книги ...................................................................................................... 5 Как работать с книгой ............................................................................................. 6 Источники информации ......................................................................................... 7 Благодарности ......................................................................................................... 7

ЧАСТЬ I. ТЕХНОЛОГИИ, СОСТАВЛЯЮЩИЕ AJAX ................................ 9 Глава 1. Принцип работы AJAX ...................................................................... 11 Глава 2. Объектно-ориентированное программирование в серверных приложениях ................................................................. 14

Принципы объектно-ориентированного программирования............................ 14 Объектная модель в PHP 5. Классы и объекты .................................................. 15 Конструктор класса ............................................................................................... 16 Создание объекта .................................................................................................. 17 Деструктор объекта ............................................................................................... 17 Копирование и клонирование объектов.............................................................. 19 Наследование ......................................................................................................... 20 Финальные классы ................................................................................................ 22 Доступ к свойствам и методам класса ................................................................ 24 Статические свойства и методы класса .............................................................. 27 Абстрактные классы и интерфейсы .................................................................... 28 Константа класса ................................................................................................... 29 Ключевое слово instanceof.................................................................................... 30 Обработка ошибок ................................................................................................ 30 Автозагрузка класса .............................................................................................. 32 Итераторы: просмотр всех общедоступных свойств объекта .......................... 33 Синглетон............................................................................................................... 34


IV

Оглавление

Глава 3. Объектно-ориентированное программирование в JavaScript ........................................................................................... 35 Создание объекта с помощью оператора new .................................................... 36 Создание объектов с помощью объектных литералов ...................................... 36 Конструктор объекта ............................................................................................ 37 Функции как объекты ........................................................................................... 38 Добавление методов при помощи прототипа ..................................................... 38 Наследование при помощи прототипа ................................................................ 40 Создание класса-наследника ................................................................................ 41 Полиморфизм ........................................................................................................ 42 Частные элементы классов ................................................................................... 43 Пространства имен ................................................................................................ 44 Обработка ошибок ................................................................................................ 45 Синглетоны ............................................................................................................ 46 Замыкания .............................................................................................................. 47 Применение замыканий .................................................................................... 48 Глава 4. XML и JSON ......................................................................................... 51 Язык XML .............................................................................................................. 51 Синтаксис XML. Правильно оформленный XML.......................................... 51 XML-декларация................................................................................................ 53 Атрибуты ............................................................................................................ 53 Комментарии ...................................................................................................... 53 Процессуальная инструкция ............................................................................. 55 Пространства имен XML .................................................................................. 55 Особые символы ................................................................................................ 56 CDATA ............................................................................................................... 57 JSON ....................................................................................................................... 58

Глава 5. Объектная модель документа ........................................................... 61 Объект Node ........................................................................................................... 64 Свойства и методы объекта Document ................................................................ 65 Доступ к узлу DOM ........................................................................................... 66 Объект Element ...................................................................................................... 66 Объект NodeList ..................................................................................................... 67 Объект NamedNodeMap ........................................................................................ 67 Объект Attr ............................................................................................................. 68 Объект Text ............................................................................................................ 68 Объект DOMImplementation ................................................................................. 68


Оглавление

V

Глава 6. DOM в JavaScript ................................................................................ 69

Объект Element ...................................................................................................... 69 Создание HTML-элемента с помощью методов DOM и включение его в дерево документа................................................................................................ 71 Чтение данных из XML-документа ..................................................................... 73

Глава 7. DOM-функции в PHP ......................................................................... 75

Создание XML-документа с помощью DOM-функций..................................... 76 Глава 8. Проблема русификации Web-приложений .................................... 81

Кодировки .............................................................................................................. 81 Передача локализованных данных в протоколе HTTP ..................................... 83 Кодирование символов в сценарии JavaScript.................................................... 85 Русский язык в PHP............................................................................................... 86 Локализация MySQL ............................................................................................. 89 ЧАСТЬ II. СОЗДАНИЕ AJAX-ПРИЛОЖЕНИЙ .......................................... 91 Глава 9. Объект XMLHttpRequest ..................................................................... 93 Глава 10. Использование XML и создание периодических запросов ..... 101

Создание периодических запросов.................................................................... 108 Глава 11. Запрос данных с сервера MySQL ................................................. 112

Передача данных в формате JSON .................................................................... 118 ЧАСТЬ III. БИБЛИОТЕКИ ДЛЯ РАБОТЫ С AJAX ................................ 125 Глава 12. Обзор библиотек для создания AJAX-приложений.................. 127 Глава 13. Библиотека Prototype ..................................................................... 131

Полезные методы в Prototype ............................................................................. 132 Класс Element....................................................................................................... 135 Класс Array .......................................................................................................... 137 AJAX в Prototype ................................................................................................. 141 Класс Ajax.Request ........................................................................................... 142 Класс Ajax.Response ......................................................................................... 143 Класс Ajax.Updater .......................................................................................... 144 Класс Ajax.PeriodicalUpdater.......................................................................... 145 Использование AJAX-запросов в Prototype .................................................. 145


VI

Оглавление

Глава 14. Библиотека script.aculo.us.............................................................. 149 Эффекты ............................................................................................................... 150 Перетаскивание и сортировка (Draggable & Sortable) ..................................... 153 AJAX в script.aculo.us ......................................................................................... 157 Автодополнение............................................................................................... 157 Класс Ajax.InPlaceEditor ................................................................................. 161 ЧАСТЬ IV. БИБЛИОТЕКА EXTJS ............................................................... 165 Глава 15. Структура и идеология библиотеки ............................................ 167 Соглашения об именах ....................................................................................... 169 Конфигурирование ExtJS и первый пример применения................................ 169 Объект Ext.Element .......................................................................................... 171 Firebug — запаситесь выжигателем жучков ..................................................... 172 Контекст ........................................................................................................... 172 Задание контекста в ExtJS .................................................................................. 173 Адаптеры и пространство имен ......................................................................... 175 Механизм наследования в ExtJS ........................................................................ 175 Вызов метода базового класса ........................................................................... 176 Обработка событий в ExtJS ................................................................................ 177 События DOM .................................................................................................. 177 События JavaScript .......................................................................................... 177 Пользовательские события ............................................................................. 179 Xtypes ................................................................................................................... 180 Классы ExtJS ........................................................................................................ 181 Класс Component .............................................................................................. 181 Класс BoxComponent........................................................................................ 183 Класс Container ................................................................................................ 184 Класс Panel ....................................................................................................... 184 Компоновка (layout) ............................................................................................ 184

Глава 16. Поиск элементов: класс DomQuery .............................................. 187 Выбор узлов DOM ............................................................................................... 187 Селекторы элементов ...................................................................................... 188 Селекторы атрибутов ...................................................................................... 188 Отбор элементов CSS Value selectors ............................................................ 188 Глава 17. Панели и компоновка элементов ................................................. 196 Простая панель .................................................................................................... 196 Вложенные панели .............................................................................................. 198


Оглавление

VII

Компоновка панелей: создание аккордеона ..................................................... 199 Панель с несколькими вкладками ..................................................................... 203 Глава 18. Формы ............................................................................................... 209 Создание элемента формы ................................................................................. 209 Компоновка формы ............................................................................................. 210 Передача данных формы на сервер методом submit ........................................ 213 Проверка форм с помощью класса VTypes. Календарь-подсказка ................. 217 Глава 19. Визуальные эффекты. Drag & drop ............................................. 222 Свертывание и развертывание блока ................................................................ 222 Изменение размеров блока ................................................................................. 226

Drag & drop .......................................................................................................... 229

Глава 20. Простые виджеты............................................................................ 236 Всплывающие подсказки.................................................................................... 236 Глава 21. Создание редактируемых таблиц................................................. 240 Создание базы данных ........................................................................................ 240 Серверный сценарий для запроса к базе и генерации ответа клиенту........... 241 Клиентская часть: HTML и сценарий JavaScript.............................................. 243 Разработка динамически редактируемой таблицы .......................................... 249 ЧАСТЬ V. jQuery ............................................................................................... 259 Глава 22. Знакомство с jQuery ....................................................................... 261 Установка библиотеки ........................................................................................ 262 Что такое $()? ....................................................................................................... 263 Глава 23. Функции ядра jQuery ..................................................................... 265 Доступ к объекту jQuery ..................................................................................... 270 Глава 24. Селекторы jQuery ........................................................................... 275 Базовые селекторы .............................................................................................. 275 Иерархические селекторы .................................................................................. 280 Основные фильтры.............................................................................................. 286 Фильтры содержимого........................................................................................ 298 Фильтры видимых и невидимых элементов ..................................................... 302 Фильтры атрибутов ............................................................................................. 306 Фильтры потомков .............................................................................................. 316 Селекторы в формах ........................................................................................... 323 Фильтры состояния элементов форм ................................................................ 326


VIII

Оглавление

Глава 25. События в jQuery ............................................................................ 331

Помощники при работе с событиями................................................................ 332 Глава 26. Манипуляции элементами в jQuery ............................................ 352

Изменение содержимого элементов .................................................................. 352 Вставка содержимого внутрь элементов .......................................................... 358 Вставка содержимого снаружи элементов ....................................................... 364 Обертывание элементов ..................................................................................... 369 Замещение, удаление, копирование элементов ................................................ 371 Глава 27. AJAX-запросы в jQuery ................................................................. 378

Загрузка содержимого ........................................................................................ 378 Реализация GET-запросов................................................................................... 385 Реализация POST-запросов................................................................................. 392 Полный контроль над AJAX-запросами ........................................................... 395 Глава 28. События AJAX в jQuery ................................................................ 402 Глава 29. Расширения для jQuery ................................................................. 410

Плагин jQuery Form ............................................................................................ 410 Плагин Live Query ............................................................................................... 416 Резюме .................................................................................................................. 420 ПРИЛОЖЕНИЯ ................................................................................................ 421 Приложение 1. Установка Web-сервера Apache, модуля PHP 5 и сервера MySQL в Windows .............................................. 423

Установка сервера Apache .................................................................................. 423 Директивы конфигурации Apache ................................................................. 425 Установка модуля PHP ....................................................................................... 426 Установка сервера MySQL 5 .............................................................................. 428 Приложение 2. Отладка JavaScript. Использование Firebug ................... 431

Выполнение и отладка кода JavaScript ............................................................. 432 Просмотр HTTP-заголовков и AJAX-запросов ................................................ 434 Приложение 3. Описание компакт-диска..................................................... 436 Литература ......................................................................................................... 437 Предметный указатель .................................................................................... 439


Не секрет, что встречаются профессионалы-практики, которые на бумаге не могут структурированно изложить свои знания и опыт, и есть те, кто пишет много книг, но не обладает реальным практическим опытом. К счастью, авторы книги не относятся ни к первым, ни ко вторым. В Елене Сергеевне сочетается профессиональный опыт и умение донести этот опыт до аудитории. Как и предыдущие книги автора, эту отличает от многих других то, что изложенный материал апробирован на сотнях слушателей самых разных компаний, которые проходили обучение у Елены Сергеевны. Для лучшего закрепления материала, в книге приведены различные практические задания для самостоятельной работы. Мы считаем, что книга является качественным продуктом и заслуживает того, чтобы ее прочитали те, кого интересует данная область. Как и многие другие книги, написанные нашими преподавателями, данная книга выходит в серии "Avalon.ru — советуют профессионалы". Опыт предыдущих книг показал, как важен контакт между тем, кто написал книгу, и тем, кто ее изучает. Книга не позволяет наладить диалог между автором и читателем, однако, для успешного восприятия материала, важно иметь возможность задать преподавателю вопрос и получить квалифицированный ответ. Если после прочтения книги у вас появились вопросы, на которые вы не нашли ответа, если вы хотите получить еще больше практических заданий, чтобы повысить свою квалификацию, если вы просто хотите сказать спасибо автору данной книги, вы можете сделать это на форуме нашего учебного центра в соответствующем разделе: http://forums.avalon.ru. На ваши вопросы ответят лично авторы книги. Проректор Санкт-Петербургского государственного политехнического университета Александр Витальевич Речинский

Декан факультета переподготовки специалистов СПбГПУ

Щукин Александр Валентинович


Введение Предлагаемая книга является учебником в полном смысле этого слова, не больше, но и не меньше. Предполагается, что читатель знаком с основами JavaScript на среднем уровне, но ему не приходилось вдаваться в детали объектной модели этого языка. Потребуются и знания основ PHP, но изложение объектной модели этого языка приведено. Кроме того, кратко излагаются основы XML. Затем рассказывается, как именно создается AJAX-запрос к серверу и как на клиентской стороне обработать ответ сервера. Большое внимание в настоящей книге уделено библиотекам JavaScript, позволяющим упростить и ускорить процесс создания Web-приложений, главным образом рассматриваются библиотеки jQuery и ExtJS.

Терминология В 2005 г. появился термин, обозначивший основное направление развития Интернета — Web 2.0. Как часто случается в информационных технологиях, термин есть, а точного определения нет. Но явление, которое он обозначил, все-таки можно охарактеризовать некоторыми общими чертами. Главный принцип Web 2.0 — усиление технологий за счет коллективного разума. Ярким примером этого служит проект Wikipedia (http://wikipedia.org). С другого ресурса, Google, началась популярность еще одной важной составляющей понятия Web 2.0 — AJAX. AJAX (Asynchronous JavaScript and XML, асинхронный JavaScript и XML) — это подход к построению пользовательских интерфейсов Web-приложений, при котором данные, запрошенные пользователем, отображаются на уже загруженной странице. При этом не происходит полной перезагрузки страницы, обновляется только ее часть. Использование AJAX стало наиболее популярно после того, как Google начала активно использовать его при создании


4

Введение

сайтов, таких как Gmail и Google Maps, а также Google Suggest — технологии автозаполнения строки поискового запроса на основе общей статистики самых популярных запросов.

AJAX в настоящее время пользуется популярностью горячих пирожков в холодный день. Правда, при ближайшем рассмотрении многие выясняют, что разработчику Web-приложений с использованием AJAX требуется такой объем знаний, что эта модная технология приближается по стоимости к суши, а то и бутербродам с черной икрой. Многие авторы отмечают, что применение термина "технология" по отношению к AJAX не является корректным: ведь он представляет собой просто совокупность нескольких ранее известных технологий. Для освоения AJAX важно хорошо разбираться в объектной модели документа — DOM (Document Object Model). Тема это обширная, местами занудная, а потому непопулярная среди авторов учебников по Web-технологиям. Но именно описанию и примерам использования DOM уделено особое внимание в данной книге. Второй важный компонент AJAX — язык XML. Его в наше время надо знать любому Web-мастеру. Понимание XML коренным образом изменяет видение современного Интернета, да и в целом информационных технологий. Правда, следует сказать, что упоминание XML именно в аббревиатуре AJAX в настоящее время в значительной степени стало архаикой, там, где простой текст недостаточен, XML все больше вытесняется форматом JSON. Но главной составляющей AJAX все-таки является JavaScript, и данная книга рассчитана на читателя, знакомого с основами этого языка. Лучшей же доступной русскоязычному читателю книгой по JavaScript явля1 ется книга Д. Флэнагана "JavaScript. Подробное руководство" — вот где, в частности, много примеров применения DOM в JavaScript. Конечно, необходимо и знание CSS. Кроме того, читателю потребуется знание языка PHP 5. Лучшей книги для изучения PHP, чем книга Л. Веллинга, Л. Томсон "Разработка Web-приложений 2 с помощью PHP и MySQL" , предложить трудно. Хотя дочитывать ее до конца для изучения AJAX и необязательно. Правда, бросить ее из-за скуки вам не удастся!

Флэнаган Д. JavaScript. Подробное руководство. // Пер. с англ. — СПб.: Символ-плюс, 2008. Веллинг Л., Томсон Л. Разработка Web-приложений с помощью PHP и MySQL. — М.: Вильямс, 2007. 1 2


Введение

5

Наконец, надо знать хотя бы основы работы с MySQL. Укажем только любимую книгу Л. Аткинсона "MySQL. Библиотека профессионала"3. Опять же там рассказано гораздо больше, чем потребуется для изучения AJAX.

Структура книги

Книга содержит пять частей и три приложения. В части I представлен обзор технологий, составляющих AJAX: принцип работы AJAX; объектно-ориентированное программирование в интернет-приложениях. Объектная модель PHP 5, работа с объектами в JavaScript; объектная модель документа DOM, DOM в JavaScript и PHP; основы XML и работа с форматом JSON; обработка XML-документов с помощью DOM-функций в PHP; проблемы русификации Web-приложений, использующих JavaScript, XML, PHP и MySQL. В части II изложены основы AJAX: объект XMLHTTPRequest; примеры создания Web-приложений: создание, отправка и обработка результатов запроса; генерация данных в форматах XML и JSON на сервере, обработка таких данных на клиентской машине; создание повторяющихся запросов, особенности разработки AJAXприложений; запрос данных с сервера MySQL. В части III обсуждаются библиотеки для работы с Ajax, в том числе: приведен обзор библиотек для ускорения разработки Web-приложений; библиотека Prototype: основы и примеры использования; Scriptaculous: создание визуальных эффектов и развитие возможностей библиотеки Prototype; библиотека Jquery: все, что вы хотели узнать об этой библиотеке; библиотека ExtJS, принципы построения и примеры применения. 3

Аткинсон Л. MySQL. Библиотека профессионала. — М.: Вильямс, 2002.


Введение

6

В части IV излагаются основы работы с библиотекой ExtJS: архитектура и идеология библиотеки ExtJS; реализация объектной модели в ExtJS; генерация элементов Web-страницы и компоновка элементов с помощью ExtJS;

создание элементов интерфейса с помощью ExtJS; применение AJAX-запросов в ExtJS для получения данных с сервера; создание редактируемой таблицы. Часть V содержит описание библиотеки jQuery, а именно: установка jQuery; функции ядра jQuery, доступ к объекту jQuery; селекторы jQuery; обработка событий в jQuery; манипуляции элементами в jQuery; AJAX-запросы в jQuery; глобальные и локальные события AJAX в jQuery; расширения для jQuery, плагины jQuery Form и Live Query. В приложении 1 обсуждается порядок установки сервера Apache и модуля PHP в операционной системе Windows. В приложении 2 кратко рассматривается Firebug — отладчик для Webприложений, работающий с Mozilla Firefox. Приложение 3 — это описание компакт-диска, приложенного к книге.

Как работать с книгой Книга ориентирована на разработчика, располагающего компьютером с операционной системой Windows, но пользователь UNIX также сможет выполнить на своем компьютере все примеры. В ходе чтения следует выполнять на компьютере примеры, описываемые в книге. Каждый пример стоит изменять и переделывать самому с тем, чтобы лучше понимать, как он работает или как сделать так, чтобы все перестало работать. Авторы приложили все усилия, чтобы изложить материал с наибольшей точностью, но не исключают возможности ошибок и опечаток. Авторы также не несут ответственности за последствия использования сведений, изложенных в книге.


Введение

7

Источники информации

Как известно, в книге нельзя охватить все вопросы, и читателю нужно иметь возможность получить дополнительные сведения, например, из Интернета. Вот адреса, которыми вы можете воспользоваться: http://apache.org; http://php.net; http://dev.mysql.com; http://w3schools.com — школы W3C по XML; http://www.w3.org/ — дартам Интернета;

содержит всеобъемлющую информацию по стан-

http://json.org; http://developer.mozilla.org.

Можно рекомендовать следующие русскоязычные сайты: http://phpclub.ru; http://opennent.ru — этот сайт в основном посвящен операционным системам, в первую очередь семейству UNIX, но посмотрите внимательно — здесь прекрасные статьи, отслеживаются все новости и даются ссылки на оригиналы и переводы статей; http://phpworld.ru — сайт посвящен PHP 5;

http://xml.nsu.ru — переводы на русский язык школ консорциума W3C по XML; http://zvon.org — учебник по XML, XSLT.

Благодарности

Авторы приносят свои благодарности Василию Руже за советы, позволившие увидеть логику развития технологии.


Часть I Технологии, составляющие AJAX


Глава 1

Принцип работы AJAX Идея AJAX (Asynchronous Javascript and XML) была изложена Джесси Гарреттом в его статье "AJAX: новый подход к Web-приложениям" (см. http://www.adaptivepath.com/ideas/essays/archives/000385.php, русский перевод — http://ajax-development.narod.ru/ajax-article.html). Web-приложения — это приложения, функциональные возможности которых обеспечиваются сервером и доставляются пользователям по Интернету или интрасети. Классическая модель Web-приложения действует следующим образом. Клиентское приложение отправляет на сервер HTTP-запрос. Сервер производит необходимую обработку: считывает и обрабатывает данные, взаимодействует с различными системами, например, с базами данных или другими серверами, и затем выдает HTML-страницу клиенту (рис. 1.1). Страница может содержать таблицы CSS и сценарии JavaScript. Существенным недостатком такого алгоритма взаимодействия клиента с сервером является то, что клиенту приходится ждать загрузки каждой последующей страницы. Суть идеи Гарретта состоит в том, что ожидание клиента сокращается или становится совсем незаметным за счет нескольких усовершенствований (рис. 1.2). Запрос HTTP

Сервер Обработка запроса

Браузер Данные HTML + CSS

Хранимые данные

Рис. 1.1. Классическая схема работы Web-приложения


Часть I. Технологии, составляющие AJAX

12

Браузер

Событие JavaScript Интерфейс Сценарий пользователя JavaScript HTML + CSS

Запрос HTTP Данные XML

Сервер Обработка запроса Хранимые данные

Рис. 1.2. AJAX-модель Web-приложения AJAX перераспределяет нагрузку между клиентом и сервером, разрешив им общаться между собой, пока пользователь работает со страницей. Клиент загружает в браузер страницу, содержащую сценарий JavaScript. Этот сценарий включает в себя функции обработки событий, которые генерируют HTTP-запрос на сервер. Запрос отправляется незаметно для пользователя. В то время, когда запрос обрабатывается на сервере и происходит передача ответа клиенту, последний продолжает работу, не ожидая полной перезагрузки страницы. Клиентский сценарий отслеживает состояние этого запроса, и, как только все данные, загруженные в качестве ответа сервера, получены браузером, происходит обновление части Web-страницы, уже отображаемой в окне браузера. Такое обновление происходит в результате работы того же сценария JavaScript, который обрабатывает данные, полученные от сервера, и отражает их в определенном фрагменте Web-страницы.

Ответ сервера в AJAX может представлять собой простой текст, текст в XML-формате, в честь которого и добавлена последняя буква в название технологии AJAX, или в формате JSON, который следует признать наиболее удобным для многих Web-приложений. Основной выигрыш в скорости работы получается за счет того, что запрос к серверу отправляется незаметно для клиента, который продолжает работу, не дожидаясь ответа сервера. Данные же ответа встраиваются в имеющуюся на клиентской стороне страницу. Итак, AJAX объединяет: стандартизованное представление данных с использованием XHTML и CSS; динамическое отображение и обработку данных на стороне клиента в сценарии JavaScript при помощи Document Object Model; асинхронное получение данных с использованием объекта XMLHttpRequest, создаваемого сценарием JavaScript; обмен данными XML или данными в других текстовых форматах.


Глава 1. Принцип работы AJAX

13

AJAX не является чудесным средством, одним своим появлением украшающим Web-приложения, но разумное использование этой технологии может сделать сайты более дружественными к пользователю. AJAX часто применяют для решения следующих задач:

проверка правильности заполнения форм с привлечением возможностей сервера; подсказки для автодополнения; создание динамических таблиц данных (girds), которые на лету обновляют базы данных на сервере; разработка приложений, которые требуют обновления информации в режиме реального времени, получая ее из различных источников. Применение AJAX создает определенные трудности, а именно: динамически создаваемые страницы могут иметь один и тот же адрес, поэтому не работает кнопка Назад, предоставляющая пользователям возможность вернуться к просмотренным ранее страницам; изменение содержимого страницы при постоянном адресе приводит к тому, что сделать закладку на странице непросто; поисковые машины не могут проиндексировать все страницы сайта, созданного на основе AJAX; на клиентской стороне JavaScript может быть отключен, в результате AJAX-приложения перестанут работать.


Глава 2

Объектно-ориентированное программирование в серверных приложениях Принципы объектно-ориентированного программирования

В реальной жизни мы повседневно имеем дело с объектами. Любые предметы, окружающие нас, можно представлять как объекты, характеризующиеся некоторыми свойствами. Кроме того, объекты могут совершать действия или над ними можно производить какие-либо действия. Понятие объекта в программировании предоставляет программисту возможность оперировать данными как объектами реальной жизни. Объект представляет собой совокупность свойств и операций, выполняемых объектом или над объектом. Операции принято называть методами. Объектно-ориентированное программирование базируется на трех основных принципах: инкапсуляции, наследовании и полиморфизме. Инкапсуляция подразумевает обращение с объектом как с черным ящиком: при работе с объектом не видно его внутреннего устройства. Объект дает возможность составить о нем представление по ограниченному числу доступных свойств и методов. Инкапсуляция как раз и означает, что объект представляет собой совокупность данных и операций с ними. Инкапсуляция — это механизм защиты свойств и операций объекта, ограждающий их от неправильного использования. Данные внутри объекта могут быть доступны только для других частей этого объекта, но закрыты от программ, находящихся вне этого объекта. Полиморфизм позволяет использовать одно и то же имя для решения разных задач. Иначе говоря, одно и то же имя метода для разных объектов может означать разные действия.


Глава 2. Объектно-ориентированное программирование в серверных приложениях

15

Наследование —

это процесс, позволяющий объекту приобретать свойства другого объекта. В процессе наследования объект может получить свойства объекта-родителя, добавив к ним свои особенности. Объектная модель представляет собой удобный инструмент для программиста, ибо позволяет разделять работу над проектом между несколькими участниками. Каждый работает над своим объектом, над его интерфейсами, при этом минимально пересекаясь с другими программистами. Этот подход обеспечивает также наилучшие условия для повторного использования созданного кода. В данной главе будут рассмотрены основные черты объектных моделей языков PHP и JavaScript. Читатель сможет увидеть сходства и отличия объектов и операций с ними в этих языках. При построении объектной модели PHP за образец была взята широко распространенная классовая модель, реализованная в C или Java. В JavaScript объекты наследуются иным способом — на основе прототипов. В JavaScript нет классов как таковых, но все задачи объектного программирования, тем не менее, решаются. Понимание этих особенностей понадобится читателю в тех главах нашей книги, где обсуждаются библиотеки JavaScript, применяемые при разработке Web-приложений с использованием AJAX-запросов.

Объектная модель в PHP 5. Классы и объекты

Класс

Объектная модель PHP базируется на понятии класса. определяет совокупность свойств, которые имеет объект данного класса, а также и операций (методов), выполняемых над объектом. Представьте себе класс млекопитающих. Принадлежность животного к этому классу определяется по наличию у животного некоторых характеристик — свойств. Например, кровь у млекопитающих теплая, при этом все млекопитающие могут совершать действия, например, двигаться или кормить детенышей молоком. На языке программирования свойство или атрибут — это переменная, имеющая некоторое значение. Действие, совершаемое объектом, — это функция. Мы можем объявить несколько свойств: public $blood, $legs;

Ключевое слово public указывает, что после него идет объявление элемента класса (т. е. свойства или метода). Кроме того, это ключевое слово определяет


Часть I. Технологии, составляющие AJAX

16

механизм доступа к элементу, о чем пойдет речь далее в этой главе. Дадим свойству значение: $blood="теплая";

Определим метод move(), который принимает один аргумент $legs — количество лап у животного: public function move($legs) { if ($legs) echo "$this->name двигается на $legs ногах <br>"; else echo "Животное плавает"; }

Значение переменной $legs надо указать при вызове метода — ничего необычного в этом нет, так мы делали при вызове функции. Специальный указатель $this применяется для обозначения объекта. Аналогично в процедурном программировании в определении функции указывается формальный аргумент. В приведенном здесь фрагменте кода распечатывается значение свойства name объекта, для которого вызывается метод move(). Можно сказать, что класс млекопитающих характеризуется совокупностью свойств и методов. Какое-либо животное, относящееся к этому классу, будет обладать этими свойствами и методами, но возможно, что методы будут реализовываться по-разному (ног-то может быть разное количество).

Конструктор класса При создании объекта — экземпляра класса вызывается функция, которая инициализирует все требуемые переменные, выполнит все действия, нужные для полного определения объекта. Эта функция называется конструктором.

В

PHP 5

конструктор — это метод, имеющий зарезервированное имя и может быть определен так:

__construct,

function __construct($name) { $this->name = $name; $this ->blood='теплая'; echo "Запущен конструктор класса mammal <br>"; }

Свойства класса name и blood получают значения при вызове конструктора.


Глава 2. Объектно-ориентированное программирование в серверных приложениях

17

Создание объекта

Объект — экземпляр класса, который можно создать с помощью оператора new, после которого указывается имя класса и параметры, передаваемые конструктору: $cat = new mammal("кошка");

При создании объекта $cat для него устанавливаются значения свойств. Получить доступ к ним можно так: echo $cat->name;

Вызвать метод объекта $cat можно таким образом: $cat->move(4);

Напишем определение класса mammal (листинг 2.1). Листинг 2.1. Класс mammal <?php class mammal { public $blood, $legs; public function __construct($name) { $this->name = $name; $this->blood="теплая"; echo "Запущен конструктор класса mammal <br>"; } public function move($legs) { if ($legs) echo "$this->name двигается на $legs ногах <br>"; else echo "Животное плавает"; } } ?>

Деструктор объекта

Объект можно уничтожить в ходе выполнения сценария, вызвав функцию unset() и передав ей в качестве параметра имя объекта. Но в любом случае при завершении работы сценария память, занимаемая объектом, высвобож-


18

Часть I. Технологии, составляющие AJAX

дается, и объект из нее удаляется. В объектной модели PHP 5 определена функция __destruct(), которая вызывается автоматически при уничтожении объекта. В листинге 2.2 вы можете увидеть определение конструктора и деструктора класса, а также создание объекта как экземпляра созданного класса.

Листинг 2.2. Класс mammal и создание объекта <?php class mammal { public $blood, $legs; public function __construct($name) { $this->name = $name; $this->blood="теплая"; echo "Запущен конструктор класса mammal <br>"; } public function move($legs) { if ($legs) echo "$this->name двигается на $legs ногах <br>"; else echo "Животное плавает"; } function __destruct() { echo "Вызван деструктор объекта <br>"; } } $cat = new mammal("кошка"); echo $cat->name."<br>"; $cat->move(4); unset($cat); echo "А теперь завершается работа сценария"; ?>

При выполнении этого примера обратите внимание на то, что деструктор выполняется именно при вызове функции unset(). Если же вы удалите из сценария строку с этой функцией, то деструктор будет вызван в самом конце работы после выполнения всех остальных операторов. Деструктор — это подходящее место для действий, которые наводят порядок после выполнения различных работ: закрывают соединения с серверами управления базами данных, очищают память для предотвращения утечек памяти и т. п.


Глава 2. Объектно-ориентированное программирование в серверных приложениях

19

Копирование и клонирование объектов При копировании объектов не происходит копирование данных — создается только ссылка на область данных так же, как при создании ссылки на переменную. Попробуем создать два объекта класса млекопитающих — кошку и кита. Дадим кошке 4 лапы, а про кита скажем — нет у него лап ($legs=0). Посмотрим, что получилось (листинг 2.3). Листинг 2.3. Копирование объектов <?php class simple_mammal { public $legs; } $cat = new simple_mammal; $cat -> legs = 4; $whale = $cat; $whale -> legs = 0; echo $cat -> legs; echo $whale -> legs; ?>

А ничего хорошего не вышло! Поскольку при копировании объекта не копировалась область данных, то и у кошки лап не оказалось. Чтобы скопировать свойства и методы объекта, надо применить клонирование (листинг 2.4). Листинг 2.4. Клонирование объектов <?php class mammal { public $legs; } $cat = new mammal; $cat -> legs = 4; $whale = clone $cat;


Часть I. Технологии, составляющие AJAX

20 $whale -> legs = 0; echo $cat -> legs; echo $whale -> legs; ?>

Вот теперь все работает так, как задумывалось. Последние примеры призваны пояснить отличительные черты работы с объектами в PHP 5: при создании копии объекта с помощью оператора присваивания ($whale=$cat) создается ссылка на объект $cat, а не копия всех свойств и методов объекта $cat.

Наследование

Можно определить класс beast, являющийся наследником ранее определенного класса mammal: class beast extends mammal

Класс-наследник может наследовать свойства и методы класса-родителя, а может их переопределить (это называется перегрузкой): public $fur;

// Объявляем новое свойство

Метод move() перегрузим: function move($legs) { if ($legs) echo "$this->name бегает, лазает по деревьям на ". $legs." лапах <br>";

}

Создадим новый метод, присущий только этому классу-наследнику: function description() { $this->fur="мягкая и пушистая"; echo $this->name, " ", $this->fur, " . "; echo "Кровь - ", $this->blood, "<br>"; }

Конструктор родительского класса не вызывается автоматически при создании объекта — экземпляра класса-наследника. Его следует вызвать явно, используя символ двойного двоеточия. Конструктор класса beast определяется следующим образом: function __construct($name) {


Глава 2. Объектно-ориентированное программирование в серверных приложениях

21

parent::__construct($name); echo "Запущен конструктор класса beast <br>"; }

В итоге определение класса ге 2.5.

beast

выглядит так, как показано в листин-

Листинг 2.5. Класс beast <?php class mammal{} class beast extends mammal { public $fur; function __construct($name) { parent::__construct($name); echo "запущен конструктор класса beast <br>"; } function move($legs) { if ($legs) echo "$this->name бегает, лазает по деревьям на ". $legs." лапах <br>"; } function description() { $this->fur="мягкая и пушистая"; echo $this->name, " ", $this->fur, " . "; echo "Кровь - ", $this->blood, "<br>"; } } ?>

Можем теперь создать объекты класса-наследника и посмотреть, как это все работает: $Murka = new beast("кошка"); $Murka-> move(4); $Murka->description();


22

Финальные классы

Часть I. Технологии, составляющие AJAX

Теперь мы хотим создать класс cat — потомок класса beast. Класс cat является также потомком класса mammal. Мы хотим указать, что у этого класса cat никаких наследников быть не может. Для этого используется ключевое слово final: final class cat extends beast

Из-за применения этого ключевого слова попытки создать классы-наследники класса cat будут вызывать сообщение об ошибке. Объявим новое свойство $sound и определим его значение в конструкторе: public $sound; function __construct($name) { parent::__construct($name); echo "Запущен конструктор класса cat <br>"; $this->sound="мурр";

}

Объявим и новый метод: function speak() { echo $this->name, " говорит ", $this->sound."<br>"; }

Объявление класса и создание объекта, экземпляра этого класса, будет выглядеть так, как показано в листинге 2.6. Листинг 2.6. Класс cat <?php class mammal { public $blood, $legs; public function __construct($name) { $this->name = $name; $this->blood="теплая";

echo "Запущен конструктор класса mammal <br>"; }


Глава 2. Объектно-ориентированное программирование в серверных приложениях public function move($legs) { if ($legs) echo "$this->name двигается на $legs ногах <br>"; else echo "Животное плавает"; } function __destruct() { echo "Вызван деструктор объекта <br>"; } } class beast extends mammal { public $fur; function __construct($name) { parent::__construct($name); echo "запущен конструктор класса beast <br>"; } function move($legs) { if ($legs) echo "$this->name бегает, лазает по деревьям на ". $legs." лапах <br>";

} function description() { $this->fur="мягкая и пушистая"; echo $this->name, " ", $this->fur, " . "; echo "Кровь - ", $this->blood, "<br>"; } } final class cat extends beast { public $sound; function __construct($name) { parent::__construct($name); echo "Запущен конструктор класса cat <br>"; $this->sound="мурр"; }

23


Часть I. Технологии, составляющие AJAX

24 function speak() {

echo $this->name, " говорит ", $this->sound."<br>"; } } // Теперь создадим объект этого класса и вызовем его методы: $Murka = new cat("кошка"); $Murka-> move(4); $Murka->description(); $Murka->speak(); ?>

Объект $Murka создается, и все методы работают, но попробуйте добавить в конец этого сценария строки class ChildClass extends cat { public function moreTesting() { echo "Вызван метод ChildClass::moreTesting()\n"; } }

и вы увидите на экране сообщение об ошибке: Fatal error: Class ChildClass may not inherit from final class (cat) in C:\Program Files\Apache Group\Apache2\htdocs\13-6.php on line 64

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

Доступ к свойствам и методам класса

В объектно-ориентированном программировании доступ к свойствам класса осуществляется обычно с определенными ограничениями. Рекомендуется, чтобы работа со свойствами велась через вызов методов — это называется инкапсуляцией. Смысл инкапсуляции состоит в том, что внешний пользователь не знает детали реализации объекта, работая с ним путем предоставленного объектом интерфейса.


Глава 2. Объектно-ориентированное программирование в серверных приложениях

25

Для реализации инкапсуляции надо ввести ограничения на доступ к свойствам и методам класса (то и другое можно называть элементом класса) с помощью следующих спецификаторов. public — общедоступный или открытый, к свойствам и методам, помеченным этим спецификатором, можно получить доступ без каких-либо ограничений. В PHP 4 все свойства и методы объявлялись только как общедоступные, причем перед именем свойства писалось ключевое слово var. Объектный код, созданный на языке PHP 4, будет работать и с модулем 5 версии, причем слово var будет трактоваться как определение свойства с открытым доступом. protected — защищенный или с ограничением доступа. Элементы этого типа доступны внутри класса, в котором они объявлены, и в его классахнаследниках. Оборот "внутри класса" означает, что прочесть защищенное свойство можно только с помощью метода, определенного в этом же классе. private — закрытый или частный. Элементы этого типа доступны только внутри класса, в котором они объявлены. Если не указывать ни один из спецификаторов, то по умолчанию элемент будет общедоступным. Посмотрим, как все это работает, на примере. Создадим два класса (листинг 2.7). Листинг 2.7. Типы доступа к свойствам класса <?php class mammal { public $blood = "теплая"; protected $legs = "4"; private $eat = "молоко"; public function printPrivate() { echo $this->eat; } } class cat extends mammal { public function printProtected() { echo $this->legs; } } ?>


Часть I. Технологии, составляющие AJAX

26

Попробуем теперь создать экземпляр класса mammal и распечатать значения его свойств: $Murka = new mammal; echo $Murka->blood; echo $Murka->classname;

Работает. А если так? echo $Murka->legs;

Не выходит, получаем сообщение об ошибке: Fatal error: Cannot access protected property mammal::$legs

А так? echo $Murka->eat;

Нет, нельзя: Fatal error: Cannot access private property mammal::$eat

Получить значение частного свойства можно только через вызов метода: $Murka->printPrivate();

Определим этот метод так: public function printPrivate() { echo $this->eat; echo $this->legs; }

Вызов такого метода предоставит нам значения и частного, и защищенного свойств. А в чем же между ними разница? Посмотрим, как обстоит дело с классом-наследником cat. Создадим новый экземпляр класса и вызовем его метод: $Barsik = new cat(); $Barsik->printProtected();

Все отлично, значение защищенного свойства $legs читается. А теперь попробуем с помощью этого метода получить значение частного свойства $eat, добавив его вывод в определение метода printProtected(): public function printProtected() { echo $this->legs; echo $this->eat; }


Глава 2. Объектно-ориентированное программирование в серверных приложениях

27

Нет, видим в браузере: Notice: Undefined property: cat::$eat

Теперь перечитайте определения спецификаторов доступа, и они станут вам понятнее.

Статические свойства и методы класса

В дальнейшем, изучая классы для работы с XML-документами, вы обнаружите в них статические свойства. Мы с вами видели, как определяются статические переменные в процедурном программировании на PHP. Можно объявить статические свойства класса так, как представлено в листинге 2.8. Листинг 2.8. Статическое свойство класса <?php class mammal { static $feeding = "молоко"; } ?>

Статические свойства едины для всего класса и не могут принадлежать ни одному из объектов класса. Кроме этого, можно обратиться к такому свойству, не создавая объекта: echo mammal::$feeding;

Аналогично можно определить статический метод и использовать его без создания объекта такого класса (листинг 2.9). Листинг 2.9. Статический метод класса <?php class mammal { static function moving() { echo "Все звери двигаются "; } } echo mammal::moving(); ?>


Часть I. Технологии, составляющие AJAX

28

Однако в статическом методе становится невозможным использовать указатель $this, т. к. при вызове статического метода неизвестно, в контексте какого объекта он вызывается.

Абстрактные классы и интерфейсы Представьте себе, что вы создаете сетевую игру, участники которой ведут борьбу с противниками. Каждый игрок представлен фигуркой того или иного существа, обладающего разными видами оружия, защиты и пр. Для описания этих разнообразных и многочисленных героев вам придется создавать много классов — родителей и наследников, описывать в них методы, которыми могут воспользоваться участники игры. Возможно, в одном из базовых классов вы захотите только указать наличие некоторого метода, а конкретную его реализацию отложить до перегрузки его в классе-наследнике. Это можно сделать с помощью абстрактного метода. Абстрактным называется метод, который имеет только объявление, но не имеет реализации. Реализация же выполняется в классе-наследнике. Класс, который содержит абстрактный метод, объявляется как абстрактный. При этом в нем могут содержаться и обычные, неабстрактные, методы. Создать объект, являющийся экземпляром абстрактного класса, невозможно. Но вот после того, как в классе-наследнике бывший абстрактный метод будет переопределен, можно уже создавать экземпляры этого класса-наследника (листинг 2.10). Листинг 2.10. Абстрактный класс <?php abstract class voin { abstract public function deistvie(); } class super_voin extends voin { public function deistvie() { echo "Появилась реализация метода "; } } $obj = new super_voin; $obj->deistvie(); ?>


Глава 2. Объектно-ориентированное программирование в серверных приложениях

29

В PHP невозможно описать класс, являющийся наследником сразу двух классов, даже абстрактных. Для решения этой проблемы существуют интерфейсы, представляющие собой абстрактные классы, не содержащие ни одного неабстрактного метода. Класс может наследовать двум интерфейсам одновременно, переопределяя их методы. Можно создавать объекты — экземпляры такого класса-наследника. В листинге 2.11 представлено объявление двух интерфейсов и класса — их общего наследника. Листинг 2.11. Интерфейсы <?php interface voin{ function shoot(); } interface artist{ function paint(); } class hero implements voin, artist { public function shoot() { echo "Герой умеет стрелять. "; } public function paint() { echo "Герой умеет рисовать. "; } } $obj = new hero; $obj->shoot(); $obj->paint(); ?>

Константа класса В PHP 5 введен такой элемент класса, как константа (листинг 2.12). Листинг 2.12. Константа класса <?php class baza {


Часть I. Технологии, составляющие AJAX

30 const DBNAME = "taxi"; } echo baza::DBNAME; ?>

Из приведенного листинга ясно, что использовать константу можно без создания объекта. Константы класса пригодятся нам для хранения параметров подключения к серверу баз данных или других данных, не подлежащих изменениям в ходе выполнения сценария.

Ключевое слово instanceof Ключевое слово instanceof позволяет определить, является ли объект экземпляром определенного класса (назовем класс country) или экземпляром класса-наследника (класс-наследник назовем city) определенного класса (листинг 2.13). Листинг 2.13. Ключевое слово instanceof <?php class country { } $obj1 = new country(); if ($obj1 instanceof country) { echo "\$obj1 - объект класса country"; } class city extends country{ } $obj2 = new city(); if ($obj2 instanceof country) { echo "\$obj2 - объект класса, производного от country"; } ?>

Обработка ошибок Посмотрим теперь, как в объектном коде реализована обработка ошибок. Мы видели, что при написании процедурного кода не рекомендуется выводить сообщения о возможных ошибках, генерируемые самим модулем PHP, на экран. Лучше предусмотреть возможность ошибки и создать свой код ее обработки. Например, может возникнуть проблема с открытием файла, так


Глава 2. Объектно-ориентированное программирование в серверных приложениях

31

не будем заставлять пользователя созерцать неудобочитаемые сообщения, а создадим свое, подходящее к случаю. Для обработки ошибок в ядро PHP 5 встроен класс Exception (в листинге 2.14 представлена структура класса, но не описана реализация, это не рабочий пример, а демонстрация идеи). Конечно, нельзя разрешать пользователю переопределять методы этого класса, поэтому они объявлены как финальные.

Листинг 2.14. Класс Exception <?php class Exception { protected $message = 'Unknown exception'; protected $code = 0; protected $file; protected $line;

// сообщение об исключении

// код исключения, определяемый пользователем

// имя файла-источника исключения

// строка, в которой произошло исключение

function __construct($message = null, $code = 0); final function getMessage();

// сообщение об исключении

// код исключения

final function getCode();

// файл источника

final function getFile();

// строка источника

final function getLine();

// массив

final function getTrace();

backtrace()

final function getTraceAsString();// форматированная строка трассировки /* Overrideable — можно перегружать */ function __toString();

// Создание строки для отображения на экране

?>

Во многих объектно-ориентированных языках реализована схема обработки исключений с помощью конструкции try/catch/throw. Она позволяет весь код обработки ошибок локализовать в одном месте сценария. Попытаемся открыть несуществующий файл: $fp = fopen("file.txt", "r");

Нельзя, получаем сообщение об ошибке. Скроем ее от пользователя: @$fp = fopen("file.txt", "r");

Теперь напишем объектный код так, чтобы при возникновении ошибки, т. е. в случае, когда $fp получила значение false, создавался новый экземпляр $exception класса Exception. Создавать будем, конечно, оператором new.


Часть I. Технологии, составляющие AJAX

32

Передадим для его создания в качестве значения свойства $message этого класса сообщение об исключении "Невозможно открыть файл!". Не будет ошибки, ну и хорошо, запишем все, что нужно, в файл. Но если возникнет ошибка, то она будет перехвачена конструкцией catch, которая напечатает "Ошибка в строке ", затем вызовет метод getLine() для нашего объекта и метод getMessage(). Эти методы вернут номер строки скрипта, в которой произошла ошибка, и значение свойства $message. Синтаксис обработки ошибок таков, как представлено в листинге 2.15. Листинг 2.15. Обработка ошибок с помощью конструкции try/catch/throw <?php try { @$fp = fopen("file.txt", "w"); if (!$fp) throw new Exception("Невозможно открыть файл!");

// Запись данных в файл при отсутствии ошибок fclose($fp); } catch (Exception $exception) {

echo "Ошибка в строке ", $exception->getLine(); echo $exception->getMessage(); } ?>

Автозагрузка класса

Итак, есть у нас большой проект, при реализации которого мы решили использовать объектный подход. Определили классы и записали каждый класс в отдельный файл — так мы сможем соблюсти принцип модульности программного обеспечения и подключать объявления классов к любому скрипту, используя, например, инструкцию include. Теперь каждый из описанных ранее классов хранится в файлах mammal.php, beast.php и cat.php. Для их подключения в начале скрипта пишем: include("mammal.php");

и т. д. для всех классов. Забудем подключить — получим ошибку при создании объекта. В PHP 5 есть возможность загрузки классов, не подключенных к скрипту, как указано ранее, с помощью глобальной функции __autoload.


Глава 2. Объектно-ориентированное программирование в серверных приложениях

33

Эта функция принимает один параметр — имя класса. Действия, которые будут выполняться при вызове функции автозагрузки, придется определить (листинг 2.16). Листинг 2.16. Автозагрузка класса <?php function __autoload($class) { include($class.".php"); } $Barsik = new mammal("кошка"); echo $Barsik->move(4); ?>

При создании классов и файлов надо помнить о чувствительности к регистру и при необходимости выполнять соответствующие преобразования имен. Объекты также могут сохраняться в сессии, но при чтении объекта из сессии для корректной работы необходимо подключить описание класса к тому скрипту, в котором происходит такое чтение. Функция __autoload поможет и в этом случае, позволяя загружать коды тех классов, экземплярами которых являются объекты, хранимые в сессии.

Итераторы: просмотр всех общедоступных свойств объекта

В объектной модели определена возможность перебора всех открытых свойств объекта с помощью оператора цикла foreach(). Это и выполнено в листинге 2.17. Листинг 2.17. Использование итератора <?php class mammal { public $blood, $legs; public function __construct($name) { $this->name = $name;


Часть I. Технологии, составляющие AJAX

34 $this->blood = "теплая"; $this->legs = 4; } } $Murka = new mammal("Кошка");

foreach ($Murka as $svoistvo => $znachenie) { echo "$svoistvo => $znachenie"; } ?>

Подытоживая сказанное, следует отметить, что в PHP реализована объектная модель на основе классов со всеми основными принципами объектноориентированного программирования.

Синглетон

Синглетон — это шаблон проектирования, который следует применить в том случае, когда в приложении необходим только один экземпляр какого-либо класса. Посмотрите пример в листинге 2.18. Применение статического метода doAction позволяет решить поставленную задачу. Листинг 2.18. Реализация синглетона <?php class Singleton { private static $instance; public static function getInstance() { if (self::$instance === null) { self::$instance = new self; } return self::$instance; } public function doAction() { echo "Вызов общедоступного метода doAction <br>"; } } Singleton::getInstance()->doAction(); ?>


Глава 3

Объектно-ориентированное программирование в JavaScript Объект в JavaScript — это коллекция поименованных свойств. JavaScript относится к языкам прототипного программирования, при котором отсутствует понятие класса, а повторное использование (наследование) производится путем клонирования существующего экземпляра объекта — прототипа. В JavaScript существуют два метода создания нового объекта: клонирование имеющегося объекта либо создание объекта "с нуля". Для создания объекта с нуля программисту предоставляются средства добавления свойств и методов в объект. В дальнейшем, с получившегося объекта может быть получена полная копия — клон. В процессе клонирования копия наследует все характеристики своего прототипа, но с этого момента она становится самостоятельной и может быть изменена. Вообще же в JavaScript согласно спецификации ECMA Script определены три типа объектов: базовые (native), объекты браузера (host object) и объекты, создаваемые пользователем (user-defined). Базовые объекты поддерживаются механизмом JavaScript, например, объекты Object, Math или Number. Имена базовых объектов являются чувствительными к регистру и начинаются с заглавной буквы. Второй тип объектов поддерживается браузером, обеспечивая, таким образом, взаимодействие пользователя с загруженным документом. Имена таких объектов начинаются со строчной буквы, например, document, window, frames. Наконец, программист может сам создать объект и дать ему имя. Имя может начинаться с любой буквы и также является чувствительным к регистру. Объектом в JavaScript является даже функция. Следует отметить, что JavaScript позволяет решать задачи объектноориентированного программирования, т. е. реализовывать инкапсуляцию,


Часть I. Технологии, составляющие AJAX

36

полиморфизм и наследование. Для демонстрации справедливости последнего утверждения, нам потребуется показать, как в JavaScript можно: определить класс; определить и вызывать методы класса; определить класс-наследник; вызывать конструктор родительского класса из класса-наследника; переопределить методы родительского класса в классе-наследнике; вызывать методы родительского класса из класса-наследника.

Создание объекта с помощью оператора new В JavaScript определен тип данных Object, который и применяется при создании объектов. Простейший способ создания объекта состоит в вызове оператора new: obj = new Object; obj.x = 1; obj.y = 2;

При создании объекта к указанным нами явно свойствам добавляется еще одно — constructor. Вначале создается объект obj, а затем ему добавляются свойство и метод.

Создание объектов с помощью объектных литералов Другой путь состоит в использовании объектных литералов, как в листинге 3.1.

Листинг 3.1. Создание объекта в JavaScript <html> <head> <script> var

nash_object = {

svoistvo: "значение свойства",

drugoe_svoistvo: "придумаем еще какое-нибудь значение",


Глава 3. Объектно-ориентированное программирование в JavaScript

37

message: function(){ alert("Привет!"); },

nash_metod: function(){ alert("Наш метод!"); } }; alert(nash_object["svoistvo"]); nash_object.nash_metod(); </script> </head> <body></body> </html>

Литерал может содержать массивы и выражения JavaScript. Мы создали объект nash_object и теперь можем получить доступ к его свойствам с помощью точки или квадратных скобок: alert(nash_object. svoistvo); alert(nash_object['svoistvo']);

Второй вариант иногда дает более гибкий способ обращения к свойствам, например, зная о том, что к свойствам можно обращаться с помощью квадратных скобок, мы можем определить, например, функцию, меняющую свойства объектов, вот так: function changeValue(property, value) { nash_object[property] = value; }

Каков бы ни был способ создания объекта, с помощью оператора new или литеральной нотации, в них есть существенный недостаток: результат не годится для повторного использования. Каждый новый объект нужно создавать заново, нельзя сделать просто новую версию старого объекта с новым значением какого-нибудь одного свойства.

Конструктор объекта Для создания однотипных объектов требуется сначала заполучить объект, который бы обладал нужными чертами, но который можно было бы использовать для того, чтобы по его подобию конструировать другие объекты. В JavaScript для этой цели используется функция конструктора объекта. Конструктор — это функция языка, отличающаяся тем, что вызывается она с помощью оператора new. Ей передается в виде значения ключевого слова this ссылка на создаваемый пустой объект. Действие конструктора состоит в наполнении созданного пустого объекта свойствами и методами.


Часть I. Технологии, составляющие AJAX

38

Вот фрагмент кода, который демонстрирует сказанное: function cr() { this.x = 1; this.y = 2; } Obj = new cr;

Можно создать сколько угодно объектов, применяя конструктор. Каждый из таких объектов будет иметь свойства x и y, имеющие значения 1 и 2 соответственно.

Функции как объекты

Функция в JavaScript является объектом, как и все остальное. Функцию можно создать следующим образом: function a(x) { return 1 + x; }

или: var a = function(x) { return 1 + x; }

Над функцией можно выполнять те же действия, что и над другими объектами. Например, присвоить ее переменной. Во втором варианте оператор function создает новый объект функции, который присваивается переменной а. Как и у любого объекта, у функции могут быть свойства. Допустима, например, следующая запись: b.someVar = 15;

Функцию можно присвоить не только переменной, но и свойству объекта. Причем допустимо использовать как нотацию с точкой, так и квадратные скобки: var q = {}; q["someFunc"] = b; q.someOtherFunc = function(){}

Добавление методов при помощи прототипа

Мы видели, что метод можно добавить в функцию-конструктор, просто вписав его внутрь при определении. Но в JavaScript обычно используют другой путь: создание прототипа.


Глава 3. Объектно-ориентированное программирование в JavaScript

39

Прототип дает возможность добавлять в объект новые свойства или менять значения имеющихся свойств уже после создания объекта. Понятию прототипа трудно подобрать аналогию в нашем житейском мире. Скажем, автомобиль, выпускаемый с конвейера, имеет все те свойства, что заложил в него человек-конструктор. Если человек решит изменить, например, параметры двигателя и внесет эти изменения в чертежи, то лишь новые автомобили, созданные по этим новым чертежам, будут обладать двигателем с новыми параметрами. С объектами JavaScript иное дело: если мы изменим прототип уже созданного объекта, то и объект мгновенно изменится, получит новые свойства, которые не существовали в тот момент, когда объект создавался. Это, скорее, напоминает рассказ Бредбери, где человек, попав в прошлое, нечаянно давит ногой бабочку. Он возвращается в настоящее время и обнаруживает, что существующая вокруг него реальность пересоздана заново в соответствии с последствиями его неосторожного движения. У каждого объекта в JavaScript есть свойство prototype, которое, в свою очередь, является объектом со своими свойствами. К этим свойствам в любой момент можно добавить новое свойство, например, так, как показано в листинге 3.2. Листинг 3.2. Добавление метода в прототип объекта <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Использование прототипа/title> <script> function person(name) { this.name = name; this.age = 22; } var girl = new person("Анна"); person.prototype.addFName = function(fname) { this.f_name = fname } girl.addFName("Николаева"); document.write(girl.f_name); </script> </head> <body></body> </html>


Часть I. Технологии, составляющие AJAX

40

Как видите, вызов метода addFName для объекта girl не вызвал ошибки и дал необходимый результат: свойство f_name был добавлено в объект. В результате добавления метода addFName в прототип все экземпляры объектов, созданные на основе этого прототипа, получат новый метод.

Наследование при помощи прототипа В JavaScript каждый объект может унаследовать свойства от другого объекта, называемого прототипом. Пытаясь определить значение запрошенного свойства, JavaScript сначала смотрит, определено ли это свойство для данного объекта. Если нет, то проверяется, определено ли это свойство для прототипа данного объекта. Цепочка поиска по прототипам может продолжаться до корневого прототипа языка. Каждый объект связан со своим прототипом, который создается функцией-конструктором данного объекта. Посмотрим, как создается цепочка прототипов на примере, приведенном в листинге 3.3. Листинг 3.3. Цепочка прототипов <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Цепочка прототипов</title> <script> Object.prototype.inObj = 1; function A() { this.inA = 2; } A.prototype.inAProto = 3; // Конструктор А подключается в цепочку прототипов: B.prototype = new A; B.prototype.constructor = B; function B() { this.inB = 4; } B.prototype.inBProto = 5; x = new B; document.write(x.inObj + ',' + x.inA

+ ', ' + x.inAProto + ',' +

x.inB + ',' + x.inBProto);


Глава 3. Объектно-ориентированное программирование в JavaScript

41

</script> </head> <body></body> </html>

Объект x создается функцией-конструктором B, получая при этом свойство inB. Кроме того, объект x получает еще и свойство inBProto, поскольку оно было добавлено в прототип уже после создания конструктора. Сам же конструктор B имеет свойство prototype, значение которого — функцияконструктор A. В браузере Firefox можно обратиться к прототипу посредством нестандартного свойства _proto_.

Создание класса-наследника

Идея состоит в том, чтобы использовать цепочку прототипов для организации наследования методов от базового класса. Посмотрите, как это делается в листинге 3.4. Листинг 3.4. Наследование <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Наследование</title> <script> // Определение базового класса

function A() {

this.x = 1; }

A.prototype.DoIt = function() {

// Определение метода

this.x += 1; } // Определение класса-наследника

B.prototype = new A; B.prototype.constructor = B; function B() { A.call(this);

// Вызов конструктора базового класса

this.y = 2; } B.prototype.DoIt = function() { A.prototype.DoIt.call(this); this.y += 1;

// Определение метода

// Вызов метода базового класса


Часть I. Технологии, составляющие AJAX

42 } b = new B;

document.write((b instanceof A) + ', ' + (b instanceof B) + '<BR/>'); b.DoIt(); document.write(b.x + ', ' + b.y); </script> </head> <body></body> </html>

В классе-наследнике мы явным образом вызываем конструктор базового класса для того, чтобы включить его в цепочку прототипов.

Полиморфизм Полиморфизм обеспечивается тем, что различные классы объектов содержат коллекцию методов с одинаковыми именами. Таким образом, при вызове надо только корректно задать имя вызываемого метода, например, так, как показано в листинге 3.5. Листинг 3.5. Полиморфизм <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Полиморфизм</title> <script> function A(){ this.x = 1; } A.prototype.DoIt = function(){this.x += 1; } function B() { this.x = 1; } B.prototype.DoIt = function(){this.x += 2; } a = new A; b = new B; a.DoIt(); b.DoIt();


Глава 3. Объектно-ориентированное программирование в JavaScript

43

document.write(a.x + ', ' + b.x); </script> </head> <body></body> </html>

Частные элементы классов JavaScript поддерживает даже частные элементы классов. Переменные, определенные в конструкторе, имеют локальную область видимости, если они определены с помощью ключевого слова var. Для доступа к таким перемен-

ным необходимо только создать локальные функции с областью видимости внутри конструктора (листинг 3.6).

Листинг 3.6. Частные элементы класса <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Частные методы</title> <script> function A() { var x = 7; this.GetX = function() { return x;} this.SetX = function(xT) { x = xT; } } obj = new A; obj2 = new A; document.write(obj.GetX() + ' ' + obj2.GetX()); obj.SetX(14); document.write(' ' + obj.GetX() + ' ' + obj2.GetX()); </script> </head> <body></body> </html>

Конструктор A() создает частное свойство x. Оно — часть объекта, но его значение невозможно получить вне объекта. Но с помощью метода GetX можно прочитать это свойство, а методом SetX — изменить.


Часть I. Технологии, составляющие AJAX

44

Пространства имен

В JavaScript нет никакой встроенной поддержки пространств имен, но их легко воспроизвести, используя объекты. Допустим, нужно создать библиотеку на JavaScript. Вместо создания глобальных функций и классов их можно обернуть в пространство имен следующим образом (листинг 3.7). Листинг 3.7. Пространство имен <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Пространства имен</title> <script> var NS = {}; NS.Pet = function(name) { this.name = name; }; NS.Pet.prototype.toString = function() { alert(this.name); }; var pet = new NS.Pet("Кролик"); pet.toString(); </script> </head> <body></body> </html>

Одного уровня пространства имен может быть недостаточно, так что можно создавать вложенные пространства имен. Как легко можно себе представить, написание этих длинных вложенных имен довольно быстро становится утомительным. К счастью, пользователи могут создать более короткий псевдоним для пространства имен (листинг 3.8). Листинг 3.8. Вложение пространств имен <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Пространства имен</title> <script> var NS = {}; NS.Examples = {}; var Eg = NS.Examples;


Глава 3. Объектно-ориентированное программирование в JavaScript

45

Eg.Pet = function(name) { this.name = name; }; Eg.Pet.prototype.toString = function() { alert(this.name); }; var pet = new

Eg.Pet("Кролик");

pet.toString(); </script> </head> <body></body> </html>

Обработка ошибок

Оператор try...catch используется в тех фрагментах сценария, где может возникнуть исключение, для его обработки. Он имеет вид: try {

оператор1 } catch (исключение) {

оператор2 }

Здесь исключение — любое имя переменной, а оператор1 и оператор2 — любые группы операторов JavaScript, заключенные в фигурные скобки. Оператор1 содержит программный код, в котором возможно возникновение исключения. Если исключение не возникло, то после исполнения оператора1 управление передается обычным образом оператору, следующему за try...catch. Если же оно возникло, то информация об исключении заносится в локальную переменную исключения, и управление передается оператору2, который должен содержать код обработки этого исключения (листинг 3.9). Листинг 3.9. Обработка исключений <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>try_catch</title> <script language="JavaScript"> function f(i) { if (i<0) throw "i меньше 0"; else

throw "i больше 0";


Часть I. Технологии, составляющие AJAX

46 } try { f(2); } catch (e) { alert(e); } </script> </head> <body></body> </html>

Синглетоны В некоторых случаях необходимо иметь единственный экземпляр объекта. Вы уже встречались с такой ситуацией в главе 2. Аналогично в JavaScript можно создать объект, который не сможет послужить прототипом. Посмотрите в листинге 3.10, как это делается с помощью объектного литерала. Листинг 3.10. Создание синглетона <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Синглетон</title> <script> var single_obj = new function() { this.chislo = 30; this.start = function() { alert("some function"); } } document.write(single_obj.chislo); single_obj.start(); </script> </head> <body></body> </html>

Мы вызываем анонимную функцию и используем this для установки свойств и методов. Создание нового объекта этим путем позволяет избежать потенциального использования объекта как шаблона. В нашем синглетоне даже можно создать частные свойства (листинг 3.11).


Глава 3. Объектно-ориентированное программирование в JavaScript

47

Листинг 3.11. Синглетон с частными свойствами <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Синглетон</title> <script> var single_obj = new function() { var svoistvo = 30; function construct() { this.metod = function() {} this.getsvoistvo = function() { return svoistvo; } this.setsvoistvo = function(sv) { svoistvo = sv; } } return new construct(); } single_obj.setsvoistvo(50); alert(single_obj.getsvoistvo()); </script> </head> <body></body> </html>

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

Замыкания Фундаментальная статья на эту тему принадлежит Ричарду Корнфорду (Richard Cornford) — см. http://www.jibbering.com/faq/faq_notes/closures.html. По-русски на настоящий момент лучшая статья В. Агафонкина на http://habrahabr.ru/blogs/webdev/38642/. Как известно, в JavaScript областью видимости локальных переменных (объявляемых словом var) является тело функции, внутри которой они определены. Посмотрите, что происходит, если внутри одной функции определена другая (листинг 3.12).


Часть I. Технологии, составляющие AJAX

48

Листинг 3.12. Замыкание <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Замыкание</title> <script> function createCounter() { var numberOfCalls = 0; return function() { return ++numberOfCalls; } } var fn = createCounter(); document.write(fn()); document.write(fn()); document.write(fn()); </script> </head> <body></body> </html>

Откройте эту страницу в браузере, и вы увидите на экране текст "1 2 3". Это означает, что переменная numberOfCalls доступна для чтения и записи во внутренней анонимной функции. Но это еще не все: после того как функция createCounter была выполнена, переменная numberOfCalls сохранила свое значение, и это значение было использовано при втором вызове функции numberOfCalls. Именно за эти свойства такие вложенные функции в JavaScript называют замыканиями (от англ. closure) — они замыкают на себя переменные той функции, внутри которой определены.

Применение замыканий

Упростим немножко пример из листинга 3.12 — уберем необходимость отдельно вызывать функцию createCounter, сделав ее анонимной и вызвав сразу же после ее объявления: var fn = (function() { var numberOfCalls = 0;


Глава 3. Объектно-ориентированное программирование в JavaScript

49

return function() { return ++ numberOfCalls; } })();

Такая конструкция позволила нам привязать к функции данные, сохраняющиеся между ее вызовами — это одно из применений замыканий. Иными словами, с их помощью мы можем создавать функции, имеющие свое изменяемое состояние. Обычно замыкания используются для того, чтобы передать параметры в функцию до ее запуска. Например, для передачи параметров в функцию setTimout.

запускает на выполнение функцию (назовем ее funct), указанную в ее первом параметре через интервал времени, заданный в миллисекундах во втором параметре. Но неудобство при работе с этой функцией состоит в том, что в функцию funct невозможно передать параметр. Посмотрите, что можно сделать, как это представлено в листинге 3.13. setTimeout

Листинг 3.13. Применение замыкания для запуска функции setTimout <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Замыкание с SetTimeout</title> <script> function ok(){ function callLater(paramA, paramB, paramC){ return (function(){ paramA[paramB] = paramC; }); } var el = document.getElementById("ok"); var functRef = callLater(el.style, "display", "none"); hideMenu=setTimeout(functRef, 500); } </script> </head>


Часть I. Технологии, составляющие AJAX

50 <body onload = "ok();">

<div id="ok" style="display:block;">Текст</div> </body> </html>

Но мы можем вызвать другую функцию callLater, которая вернет ссылку на внутреннюю анонимную функцию. Ссылка functRef на функцию и будет передана функции setTimeout в качестве параметра. Параметры, которые потребуются для выполнения внутренней функции, передаются в вызове функции callLater. setTimout выполняет внутреннюю функцию, не требующую параметров, но эта внутренняя функция имеет доступ к параметрам внешней функции.


Глава 4

XML и JSON Web-сервер отвечает на запрос клиента, отправляя запрошенные данные. Это может быть простой текст, но обычно используется другой формат: либо XML, либо JSON. Язык XML разрабатывался для того, чтобы обеспечить передачу данных между различными приложениями и даже между разными платформами.

JSON предоставляет возможность передавать данные в более компактном виде, чем XML. К тому же формат JSON предназначен для передачи данных в виде, наиболее легко обрабатываемом сценариями JavaScript. В этой главе мы рассмотрим синтаксис обоих форматов.

Язык XML XML (EXtensible Markup Language) — расширяемый язык разметки был создан для описания данных, т. е. каждый тег языка XML предназначен для того, чтобы объяснить, какой смысл имеет текст, стоящий между открывающим и закрывающим тегами. XML-документ — это просто текст с тегами. В отличие от языка HTML, в XML теги не определены, при создании XMLдокументов автор создает собственные теги.

Синтаксис XML. Правильно оформленный XML Программы, читающие и анализирующие XML-документы, называют парсерами (от англ. parse — обрабатывать). Современные браузеры, такие как Internet Explorer (начиная с версии 6) или Mozilla Firefox, включают в себя парсеры XML-документов, отвечающие стандартам W3C.


Часть I. Технологии, составляющие AJAX

52

XML-документ, созданный в соответствии с синтаксическими правилами языка, называется правильно оформленным (well formed) XML-документом. Если браузер обнаруживает несоответствие XML-документа этим правилам, появляется сообщение о найденной ошибке, и содержимое документа не отображается. Спецификация языка XML консорциума W3C указывает, что программа не должна продолжать обработку XML-документа, если она обнаружит ошибку. Критерии правильности XML-документа таковы.

Каждый открывающий тег должен иметь соответствующий закрывающий тег. В отличие от HTML, в XML все теги должны быть парными: <p> Текст параграфа. </p> Пустой тег может записываться следующим образом: <br

/>.

Теги не могут перекрывать друг друга. В XML все элементы должны быть правильно вложены друг в друга: <parent> <child>Текст </child> </parent>

XML-документы должны иметь единственный корневой элемент (root element). XML-элементом является все, что заключено между открывающим тегом элемента и закрывающим тегом элемента, включая сами теги.

Все XML-документы должны иметь единственную пару тегов, задающую корневой элемент. Все остальные элементы должны быть вложены в корневой элемент. Элемент может иметь элементное содержимое, смешанное содержимое, простое текстовое содержимое, или он может быть пустым. Все элементы могут иметь дочерние элементы. Имена элементов должны подчиняться соглашениям XML о названиях: • названия могут содержать буквы, цифры и другие символы; • названия не могут начинаться с цифры или знака препинания; • названия не могут начинаться с букв xml (или XML, или Xml и т. д.); • в названии не должно быть пробелов.

Регистр символов (верхний/нижний) для XML существенен. Тег <Letter> отличается от тега <letter>. Таким образом, начальные и конечные теги должны писаться в одном регистре. XML сохраняет пробелы внутри текста.


Глава 4. XML и JSON

53

XML-декларация В начале документа бывает полезно указать, каков конкретный тип этого документа. XML предоставляет в наше распоряжение специальную декларацию для того, чтобы пометить документы как документы XML. XML-декларация всегда начинается с символов <?xml и заканчивается символами ?>. Декларация должна располагаться в самом начале файла, т. е. первым символом файла должна быть угловая скобка и никаких концов строки и пробелов. Декларация сама по себе не является частью XMLдокумента. Она не является XML-элементом и может не иметь закрывающего тега. Типичная декларация выглядит так: <?xml version='1.0' ?>

XML-документ может содержать символы различных языков мира. Чтобы парсер корректно обрабатывал эти символы, нужно сохранять документ с указанием используемой кодировки. Без указания кодировки в XML-документ нельзя вставлять буквы русского языка, в этом случае ошибка неминуема.

В настоящее время при использовании XML в Web-приложениях целесообразно применять кодировку UTF-8. Для этого в XML-декларацию необходимо добавить атрибут encjding: <?xml version="1.0" encoding="UTF-8"?>

Атрибуты

Элементы XML могут содержать атрибуты в начальном теге. Атрибуты — это пары "имя = значение", поставленные в соответствие одному из элементов. Атрибуты применяются для предоставления дополнительной информации об элементах. Они должны находиться при открывающем теге. Атрибуты всегда должны иметь значение, даже если оно — всего лишь пустая строка, и эти значения должны заключаться в кавычки. Допускаются как двойные, так и одинарные кавычки. Пример: <file type="gif">computer.gif</file>

Комментарии

Комментарии позволяют вставлять в XML-документ текст, который на самом деле не является частью документа, а предназначен для тех, кто будет читать сам исходный XML. Комментарии не могут располагаться внутри тега.


Часть I. Технологии, составляющие AJAX

54

Стандарт XML устанавливает, что XML-анализатор не должен передавать эти комментарии приложению. Комментарии всегда начинаются строкой <!-- и заканчиваются строкой -->. В листинге 4.1 представлен XML-документ, а на рис. 4.1 — вид его в браузере. Листинг 4.1. XML-документ <?xml version="1.0" encoding="UTF-8"?> <car id="f327" xmlns="http://myford.ru"> <make>Ford</make> <model>Focus</model> <date> <year>2008</year> <month>май</month> </date> <!-- текст комментария --> <color>красный</color> </car>

Рис. 4.1. Вид XML-документа в браузере


Глава 4. XML и JSON

55

Процессуальная инструкция

Процессуальная инструкция (т. е. инструкция по обработке вкладываемого в нее текста) имеет следующую общую форму записи: <?Имя_приложения

инструкция ?>

Инструкция передает информацию указанному в ее начале приложению.

Пространства имен XML

Поскольку имена элементов в XML не определены, возможны конфликты, когда два различных документа используют одно и то же имя для описания двух различных типов элементов. В этом XML-документе информация заключена в таблицу (table): <table> <tr> <td>sun</td> <td>rain</td> </tr> </table>

А в этом XML-документе содержится информация о столе как части мебели (table):

<table> <name>Kitchen table</name> <width>80</width> <length>120</length> </table>

Если эти два XML-документа сложить вместе, возникнет конфликт имен элементов, потому что оба документа содержат элементы <table> с разным содержанием и определением. Пространства имен (namespaces) XML позволяют избегать конфликтов имен элементов. Пространство имен XML — это коллекция имен, используемых в XML-документах для обозначения элементов и атрибутов. Эта коллекция идентифицируется именем ресурса в Интернете (URI). Пользуясь терминами интернет-технологий, можно сказать, что русский язык — это коллекция слов. О принадлежности слова к тому или иному языку говорят так: это слово японского языка. Название языка идентифицирует коллекцию слов, так же как URI идентифицирует пространство имен. Часто смысл слова можно понять только тогда, когда известно, из какого оно языка. "А-а, это по-японски значит «здравствуйте»!" — говорим мы.


Часть I. Технологии, составляющие AJAX

56

Для указания пространства имен в начальный тег элемента помещается атрибут с использованием следующего синтаксиса: xmlns: prefix="namespace"

Здесь xmlns — ключевое слово, означающее, что начинается определение пространства имен; prefix — префикс, который будет использоваться во всем документе для обозначения принадлежности тега, перед которым он ставится, к этому пространству имен; "namespace" — идентификатор пространства имен. Когда пространство имен задается в корневом теге элемента, все дочерние его элементы, обладающие тем же префиксом, относятся к тому же пространству имен. Адрес ресурса, участвующий в идентификации пространства имен, не используется парсером для поиска информации. Единственная задача этого адреса — дать пространству имен уникальное имя. Тем не менее, очень часто пространство имен используют как указатель на реальную Web-страницу, содержащую информацию об этом пространстве имен. В следующем XML-документе (листинг 4.2) пространство имен было определено с помощью URI http://www.w3.org/TR/html4/. Листинг 4.2. Пространство имен <h:table xmlns:h="http: //www.w3.org/TR/html4/"> <h:tr> <h:td>Apples</h:td> <h:td>Bananas</h:td> </h:tr> </h:table>

Кроме применения самих префиксов h в этих примерах к тегу table был добавлен атрибут xmlns. Это сделано для того, чтобы дать префиксу имя, связанное с пространством имен. Префикс h в приведенном примере играет такую же роль, как фамилия для ребенка: "Он из семьи Петровых" — могут сказать про ребенка, определив тем самым его принадлежность к некоторому множеству (семье).

Особые символы

Некоторые особые XML-символы не могут быть корректно обработаны и должны заменяться ссылками на сущности.


Глава 4. XML и JSON

57

Если вы поместите внутрь XML-элемента символ левой угловой скобки (<), это вызовет ошибку, потому что парсер интерпретирует его как начало нового элемента. Для того чтобы избежать ошибки, нужно заменить этот символ ссылкой на сущность: <message>if salary < 1000 then</message>

В XML есть пять изначально заданных сущностей: < — меньше, чем (<); > — больше, чем (>); & — амперсанд (&); ' — апостроф ('); " — двойная кавычка ("). Ссылки на сущности всегда начинаются со знака амперсанда (&) и заканчиваются точкой с запятой (;).

CDATA Если XML-элемент содержит много символов левой угловой скобки или знак амперсанда (например, надо вывести программный код), то можно задать секцию CDATA, очень похожую по смыслу ее применения на тег <pre> в HTML. CDATA (Character DATA) переводится как "символьные данные". Все, что находится внутри раздела CDATA, игнорируется парсером. Секция CDATA начинается символами <![CDATA[ и заканчивается символами ]]> (листинг 4.3). Листинг 4.3. Раздел CDATA <script> <! [ CDATA [ function matchwo(a,b) { if (a < b && a < 0) then { return 1 } else { return 0 } ]]> </script>


Часть I. Технологии, составляющие AJAX

58

В этом примере все, что расположено внутри секции CDATA, не будет анализироваться парсером.

JSON JSON (JavaScript Object Notation) дословно переводится как запись в формате JavaScript. JSON оперирует следующими понятиями.

объектов

Объект — неупорядоченный набор пар "ключ/значение". Объект начи-

нается с открывающей фигурной скобки и заканчивается закрывающей фигурной скобкой. Каждое имя сопровождается двоеточием, пары "ключ/ значение" разделяются запятой. Массив — упорядоченная коллекция значений. Массив начинается с открывающей квадратной скобки и заканчивается закрывающей квадратной скобкой. Значения разделены запятой. Значение может быть строкой в двойных кавычках, числом, true, false, null, объектом или массивом. Эти структуры могут быть вложенными. Строка — коллекция нуля или больше символов Unicode, заключенная в двойные кавычки. Используется обратная косая черта в качестве символа экранирования. Число представляется так же, как в C или Java, кроме того, что используется только десятичная система счисления. Пробелы могут присутствовать между любыми лексемами. Вспомните синтаксис создания объекта в JavaScript, и вам станет ясно, что в приведенном ранее описании рассказывается именно про этот синтаксис описания данных. В PHP, начиная с версии 5.2, существуют функции, преобразующие объекты PHP в объекты JSON и обратно. Рассмотрим пример их использования (листинг 4.4). Листинг 4.4. Передача данных в формате JSON <?php class car { public $make = "Ford"; public $model = "Focus"; public $color = "red"; public $date = array("year"=>2008, "month"=>"may");


Глава 4. XML и JSON

59

} $test = new car; echo json_encode($test); ?>

Запустив этот пример, вы увидите в браузере следующую строку: {"make":"Ford","model":"Focus","color":"red","date":{"year":2008,"month": "may"}}

Вот и все стало ясно, не правда ли? Функция json_encode преобразовала данные объекта $test в формат, которым пользуется JavaScript, а мы эти данные передали в клиентский браузер. Функция json_decode является парой только что описанной. Для более ранних версий PHP существуют различные варианты преобразования в формат JSON, вы найдете их множество на просторах Интернета, но мы не будем на них останавливаться в этой книге. В JavaScript определена функция eval, которая преобразует строку в формате JSON в объекты. Синтаксис ее использования немного замысловат, посмотрите листинг 4.5.

Листинг 4.5. Обработка ответа функцией eval <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>eval JSON</title> <script> var response = '{"make":"Ford", "model":"Focus", "color":"red", "date":{"year":2008, "month":"may"}}'; var data

= eval("(" + response

+ ")");

document.write(data.make); </script> </head> <body></body> </html>

Обратите внимание на скобки, которыми окружены данные JSON. Их присутствие заставляет функцию eval рассматривать аргумент именно как выражение, подлежащее обработке. Функция eval возвращает объект, содержащий все свойства того объекта, что был создан на сервере.


60

Часть I. Технологии, составляющие AJAX

Если вам пока трудно оценить выгоды применения JSON, то представьте себе, что вы пишете сетевую игру, герои которой действуют на клиентских машинах. На клиентской машине герои фигурируют в виде объектов JavaScript, их действиями управляет пользователь. Клиентская программа отправляет запросы на сервер для того, чтобы сделать эти действия известными другим участникам. Действия героев приводят к тому, что их свойства меняются, и, возможно, они приобретают новые возможности, описываемые функциями. На сервере герой представлен в виде объекта PHP. Обработка данных в случае использования JSON существенно упростится по сравнению с XML-вариантом.


Глава 5

Объектная модель документа Document Object Model (DOM) определяет стандартные способы доступа к данным XML-документа и оперирования этими данными. В дальнейшем данные из документа могут быть включены в Web-страницу. XML-документ должен быть правильно оформленным, а значит, с XHTML-документами также можно обращаться в соответствии с DOM.

Парсер браузера или любой другой парсер XML, отвечающий стандартам DOM, рассматривает документ как дерево, состоящее из узлов (Node). Узлами являются элементы документа, атрибуты, текстовое содержимое элемента, даже сам документ в целом является узлом. В табл. 5.1 представлены все допустимые типы узлов.

Таблица 5.1. Типы узлов nodeType

Тип узла

1

element

2

attr

3

text

4

cdatasection

5

entityreference

6

entity

7

processinginstruction

Описание

Элемент Атрибут

nodeValue null

Значение атрибута Содержимое узла Содержимое узла

Текстовое содержимое элементного узла Раздел CDATA (его содержимое не будет обрабатываться парсером) Имя ссылки на сущность null null Сущность Процессуальная инстСодержимое рукция узла


Часть I. Технологии, составляющие AJAX

62

Таблица 5.1 (окончание) nodeType

Тип узла

Описание

nodeValue

8

comment

Комментарий

Текст комментария

9

document

Весь документ

null

10

documenttype

Имя типа документа

null

11

documentfragment

Объект, который содержит часть документа

null

12

notation

Имя нотации

null

Каждый узел является объектом некоторого класса, для которого стандартом DOM определен набор свойств и методов. Скажем, все элементные узлы отображаются в дереве в виде объектов класса Element, а атрибуты — в виде объектов Attr. Иначе говорят, что Element, Attr и др. — это интерфейсы элементов документа. Термин "интерфейс" используется для того, чтобы указать, что некоторый объект играет определенную роль. Эта роль определяет, какие свойства имеет объект, какие методы для манипулирования с ним можно использовать. Рассмотрим XML-документ, представленный в листинге 5.1, и дерево, которое строится в браузере из данных этого документа (рис. 5.1). Листинг 5.1. XML-документ <?xml version="1.0" encoding="UTF-8" ?> <book edition="bhv" pubdate="2008"> <author> <name>Василий </name> <fname>Пупкин</fname> </author> <title>Как я стал героем русскоязычного Интернета.</title> <genre>мемуары</genre> </book>

Графическое представление дерева документа демонстрирует узлы и взаимосвязи между ними. Дерево начинается с корня, к которому крепится весь документ. Это первый узел, но узел, не представляющий некоторый HTMLэлемент, а специальный, указывающий на начало документа. Положение каждого узла в дереве документа определяется относительно корня. Корень документа следует отличать от корневого элемента. Корневой элемент заключает в себя все остальные элементы документа.


Глава 5. Объектная модель документа

63 Root

Nodelist Root Element <book>

Nodelist Element <author>

NamedNodemap Element <title>

Nodelist Element <name>

Nodelist TextNode

Василий

Element <genre>

Attr edition

Nodelist Element <fname>

Nodelist

Attr pubdate

Nodelist TextNode

TextNode

мемуары

Как я стал героем Интернета

TextNode

Пупкин

Рис. 5.1. Дерево XML-документа

Каждый узел, за исключением корня, имеет один родительский узел. У любого узла может быть несколько узлов-наследников. Элементы-наследники — это те элементы, которые вложены в родительские элементы. Дети одного и того же родителя называются siblings (братья и сестры). В приведенном выше примере узел author является первым ребенком (first child) узла book, а узел genre — последним (last child). Старшинство узлов одного поколения определяется положением в документе: чем выше строчка, где описывается узел, тем этот ребенок старше. Элементы-наследники одного родителя составляют упорядоченный список узлов (NodeList). Вообще списки узлов могут составляться из узлов, отобранных на основании различных критериев: положения в дереве документа, имени тега и т. д. Корневой элемент <book>, несмотря на то, что является единственным наследником корня, входит в список узлов (NodeList). Это общее правило: элементы-наследники любого родителя объединяются в список. Объект NodeList предоставляет программисту набор свойств и методов для работы с таким списком.


64

Часть I. Технологии, составляющие AJAX

Список атрибутов какого-либо элемента носит специальное название — NamedNodeMap.

Браузеры конвертируют полученные с сервера XML-документы в дерево DOM. Объекты DOM уже доступны сценариям JavaScript. Серверные сценарии также могут оперировать объектными деревьями, создавая, читая или изменяя документы.

Объект Node

Базовым объектом DOM является объект Node, методы которого наследуют прочие объекты — элементные узлы, атрибуты и др. Объекты-наследники имеют также характерные только им свойства и методы. В DOM для объекта Node, представляющего узлы документа, определены свойства и методы, которые могут быть использованы для обработки документов. Вот некоторые из них: nodeName — имя узла; nodeValue — значение узла; nodeType — тип узла в виде числа; textContent — текстовое содержимое узла и его наследников; parentNode — родитель данного узла; childNodes — список детей данного узла; attributes — список атрибутов данного узла; firstChild — первый дочерний узел данного узла; lastChild — последний дочерний узел данного узла; nextSibling — следующий узел, дочерний элемент того же родительского элемента; previousSibling — предыдущий дочерний элемент, обладающий тем же родительским элементом, что и данный узел. Свойство childNodes возвращает упорядоченный список узлов (т. е. объект класса NodeList). Нумерация узлов в списке начинается с 0. Свойство массива length показывает количество узлов в списке. Список узлов перестраивается в случае удаления или добавления узла автоматически. Свойство attributes элементного узла возвращает список атрибутных узлов (NamedNodeMap), подобный списку узлов за исключением некоторых отличий методов и свойств. Аналогично массиву элементных узлов массив атрибутов перестраивается автоматически при удалении или добавлении атрибутов.


Глава 5. Объектная модель документа

65

Отметим некоторые методы объекта Node: appendChild(node) — добавляет узел-наследник. Привязывает новый узел к дереву, ставя его последним в списке дочерних узлов данного узла; removeChild(node) — удаляет указанный узел-наследник; hasChildNodes() — возвращает значение true, если узел имеет хотя бы один дочерний узел; insertBefore(newNode, refNode) — вставляет новый узел, newNode, перед существующим узлом refNode; replaceChild(newNode, oldNode) — заменяет старый узел oldNode новым узлом newNode.

Свойства и методы объекта Document

Объект Document представляет корень документа и предоставляет доступ к данным документа. Поскольку элементы, текстовые узлы, комментарии и другие узлы являются частью документа, то объект Document также содержит методы создания узлов различных типов. Опишем теперь свойства и методы (табл. 5.2 и 5.3) объекта Document, представляющего весь XML-документ в целом. Напомним, что объект Document наследует объекту Node и не будет перечислять наследуемые свойства и методы. Среди свойств данного объекта в табл. 5.2 указано и свойство async, не являющееся частью стандарта W3C, но играющее важную роль в технологии AJAX. Таблица 5.2. Свойства объекта Document

Свойство async documentElement inputEncoding xmlEncoding

Описание

Определяет, будет ли загрузка XML-документа с сервера осуществляться асинхронно или нет Возвращает корневой элемент документа Возвращает входную кодировку документа Возвращает XML-кодировку документа Таблица 5.3. Методы объекта Document

Метод createAttribute(name) createComment()

Описание

Создает атрибутный узел с указанным именем и возвращает новый объект Attr Создает узел комментария


Часть I. Технологии, составляющие AJAX

66

Таблица 5.3 (окончание)

Метод

Описание

createDocumentFragment() createElement() createProcessingInstruction (target, data) createTextNode() getElementById(id)

getElementsByTagName()

Создает пустой объект DocumentFragment и возвращает его Создает элементный узел Создает узел ProcessingInstruction и возвращает его Создает текстовый узел Возвращает элемент с указанным значением идентификатора ID, если подходящего элемента нет, то возвращает null Возвращает список NodeList элементов с указанным тегом

Доступ к узлу DOM

Получить доступ к узлу можно одним из следующих способов: используя метод getElementsByTagName(); перебирая поочередно в цикле все узлы дерева; двигаясь по дереву, используя отношения между узлами.

Объект Element

Объект Element наследует свойства объекта Node и имеет ряд своих специфических свойств, которые можно читать и изменять (табл. 5.4 и 5.5). Таблица 5.4. Свойства объекта Element

Свойство

Описание

Attributes

Возвращает список атрибутов элемента в виде объекта NamedNodeMap

tagName textContent

Возвращает имя элемента Устанавливает или возвращает текстовое содержимое узла и вложенных в него элементов


Глава 5. Объектная модель документа

67

Таблица 5.5. Методы объекта Element

Метод getAttribute() getElementsByTagName() hasAttribute(name) hasAttributes() hasChildNodes() removeAttribute() removeAttributeNode() setAttribute()

Описание Возвращает значение атрибута Возвращает список элементов, указанных по имени, и их детей Возвращает true или false в зависимости от того, есть ли у элемента атрибут name Возвращает true или false в зависимости от того, есть ли у элемента атрибуты Возвращает true или false в зависимости от того, есть ли у элемента вложенные элементы Удаляет указанный атрибут Удаляет указанный атрибутный узел Добавляет новый атрибут

Объект NodeList

Объект NodeList представляет упорядоченный список узлов. Узлы в списке можно перебрать по их номерам, начиная с нуля. Объект NodeList всегда содержит актуальную информацию. Если элемент удаляется из документа или, наоборот, добавляется к документу, список узлов автоматически обновляется. Элементы указываются в списке в том порядке, в котором указаны в самом документе. Свойство length объекта NodeList возвращает число узлов в списке. Метод item возвращает тот узел, номер которого указан в качестве аргумента этого метода.

Объект NamedNodeMap

Атрибуты элемента перечислены в NamedNodeMap — списке атрибутов. Этот объект представляет собой неупорядоченный список узлов. Доступ к узлам в объекте NamedNodeMap осуществляется по имени атрибута. Объект NamedNodeMap всегда содержит актуальную информацию: если атрибут добавляется или удаляется, то список атрибутов обновляется автоматически.


68

Часть I. Технологии, составляющие AJAX

Объект NamedNodeMap располагает свойством length. Это свойство возвращает количество атрибутных узлов в списке. Перечислим некоторые методы объекта NamedNodeMap: getNamedItem — возвращает указанный по имени узел; item — возвращает указанный по номеру узел, номер отсчитывается с нуля; removeNamedItem — удаляет указанный по имени узел.

Объект Attr

Объект Attr возвращает атрибут элементного объекта как атрибутный узел. Объект Attr имеет те же самые свойства и методы, что и остальные узлы в целом. Свойства, специфичные для объекта Attr, таковы: name — возвращает или устанавливает имя атрибута; value — возвращает или устанавливает значение атрибута.

Объект Text

Этот объект представляет собой текстовое содержимое элемента или атрибута. Для дальнейшего разговора нам понадобятся его: свойство data, возвращающее или устанавливающее текстовое содержимое; метод appendData, добавляющий текст к содержимому узла.

Объект DOMImplementation

Объект DOMImplementation доступен через свойство implementation объекта Document. Нам понадобится его метод createDocument, который создает документ в виде дерева DOM.


Глава 6

DOM в JavaScript

Объект Element

Объект Element в JavaScript представляет собой реализацию одноименного объекта DOM с добавкой свойств и методов, применяемых для манипуляции с элементом и для его отображения на странице. Укажем в табл. 6.1 те свойства, которые поддерживает объект Element в добавление к свойствам, приведенным в главе 5. Таблица 6.1. Свойства объекта Element

Свойство style tagName textContent innerHTML className name id clientHeight clientLeft clientTop clientWidth offsetHeight

Описание

Объект, представляющий стиль отображения элемента Имя тега элемента Возвращает или устанавливает текстовое содержимое элемента и его потомков Возвращает или устанавливает содержимое и разметку внутри элемента Возвращает или устанавливает стилевой класс Возвращает или устанавливает значение атрибута name элемента Возвращает или устанавливает идентификатор элемента Внутренняя высота элемента Ширина левой границы элемента Ширина верхней границы элемента Внутренняя ширина элемента Высота элемента в скомпонованной странице


Часть I. Технологии, составляющие AJAX

70

Таблица 6.1 (окончание)

Свойство offsetLeft offsetParent offsetTop offsetWidth scrollHeight scrollLeft scrollTop scrollWidth

Описание Расстояние от левой границы элемента до левой границы элемента offsetParent Элемент, от которого ведутся расчеты сдвига текущего элемента Расстояние от верхней границы элемента до верхней границы элемента offsetParent Ширина элемента в скомпонованной странице Видимая высота прокручиваемого элемента Возвращает или устанавливает размер прокрутки в окне влево для элемента Возвращает или устанавливает размер прокрутки в окне вниз для элемента Видимая ширина прокручиваемого элемента

Полезность полного перечня свойств и методов любого из объектов, описываемого стандартом DOM, ограничена тем, что не все свойства и методы поддерживаются браузерами. Здесь и в дальнейшем в сводных таблицах будут приводиться только методы, поддерживаемые хотя бы несколькими из браузеров. Наилучшую поддержку стандартов обеспечивает браузер Opera, но при создании Web-страниц необходимо предусматривать корректные решения для всех популярных браузеров. Рассмотрим теперь методы объекта Element (табл. 6.2). Таблица 6.2. Методы объекта Element, определенные для HTML-документов

Имя Blur() click() focus() addEventListener() dispatchEvent(event) getElementsByClassName() removeEventListener()

Описание Переносит фокус с текущего элемента Симулирует щелчок по текущему элементу Переносит фокус на текущий элемент Задает обработчик некоторого события для элемента Передает событие, связанное с этим элементом, в DOM Отбирает элементы по имени стилевого класса Удаляет обработчик события для данного элемента


Глава 6. DOM в JavaScript

71

Пример из листинга 6.1 демонстрирует применение метода getElementById() объекта Document и установку значения свойства innerHTML объекта Element. Функция poisk() ищет в текущем документе HTML-элемент по указанному значению атрибута id и изменяет содержимое найденного элемента. Листинг 6.1. Использование метода и свойства <html> <head> <title>Example 1</title> <script type="text/javascript"> function poisk() { myDiv = document.getElementById("myElement"); myDiv.innerHTML = "Привет!"; } </script> </head> <body onload="poisk()"> <div>Какой-то контент страницы.</div> <div id="myElement"> </body> </html>

Приведенный сценарий является отправной точкой для использования технологии AJAX. Суть его в том, что в результате действий клиента оказывается возможным изменить фрагмент Web-страницы, не обновляя страницу в целом.

Создание HTML-элемента с помощью методов DOM и включение его в дерево документа

С помощью методов DOM можно динамически создавать ветки элементов и подключать их к дереву документа на странице, как это делается в листинге 6.2. Листинг 6.2. Создание элемента и включение его в документ <html> <head> <title>AJAX</title>


Часть I. Технологии, составляющие AJAX

72

<script type="text/javascript" > function obrabotka() { paragraf = document.createElement("b"); soderzanie = document.createTextNode("Какой угодно текст."); paragraf.appendChild(soderzanie); myDiv = document.getElementById("my"); myDiv.appendChild(paragraf); } </script> </head> <body onclick="obrabotka()"> <div id="my" /> </body> </html>

В этом примере использован метод createElement() объекта Document. С его помощью создается элемент документа, имя которого указывается в качестве параметра метода. Метод appendChild() применяется к тому элементу, после которого он пишется, этот метод присоединяет указанный в качестве аргумента элемент к списку дочерних элементов. При щелчке по любой области документа в браузере вызывается функция obrabotka(). В ней сначала методом createElement() создается новый параграф, затем методом createTextNode() формируется текстовый узел. Этот узел включается в список дочерних элементов элемента paragraf методом appendChild(). Далее уже известным нам методом getElementById() ищем тот элемент, в список дочерних элементов которого мы хотим включить элемент paragraf. Делаем это опять же методом appendChild(). Метод createElement создает элемент документа, имя которого указывается в качестве параметра метода. Затем методом createTextNode создается текстовый узел. Метод appendChild() присоединяет указанный в качестве аргумента элемент к списку дочерних элементов. Методом getElementById() ищем тот элемент, в список дочерних элементов которого мы хотим включить элемент paragraf, и присоединяем созданную ветку к исходному документу. Обратите внимание, что создать элемент недостаточно, он не будет отражаться в окне браузера, необходимо подсоединить его к имеющемуся дереву.


Глава 6. DOM в JavaScript

73

Чтение данных из XML-документа

Посмотрим, как сценарий JavaScript может анализировать XML-документ и добавлять на страницу данные из этого документа. Возьмем знакомый уже файл car.xml (см. листинг 4.1), загрузим его в DOM и прочитаем значение какого-либо узла из этого документа (листинг 6.3). Используем здесь функцию loadXMLDoc, которая принимает в качестве параметра имя файла с XML-документом. Она загружает указанный документ в DOM. Функция корректно работает в различных браузерах за счет того, что в ней предусмотрены разные способы создания документа: в Internet Explorer — за счет библиотеки ActiveXObject, в остальных браузерах обеспечена поддержка метода createDocument объекта DOMImplementation, что и используется. Функция loadXMLDoc возвращает объект Document. Листинг 6.3. Загрузка XML-документа и его анализ <html> <head> <title>firstChild.data</title> <script type="text/javascript"> function getText(){ function loadXMLDoc(dname) { try {

// Internet Explorer

xmlDoc=new ActiveXObject("Microsoft.XMLDOM");

}

catch(e) { try

//Firefox, Mozilla, Opera, etc.

{ xmlDoc=document.implementation.createDocument("","",null); } catch(e) {alert(e.message)} } try { xmlDoc.async=false; xmlDoc.load(dname); return(xmlDoc); }


Часть I. Технологии, составляющие AJAX

74

catch(e) { alert(e.message) } return(null); } var el = document.getElementById("ok"); xmlDoc = loadXMLDoc("car.xml"); x=xmlDoc.getElementsByTagName('color'); el.innerHTML = x[0].firstChild.data; } </script> </head> <body onload="getText();"> <div id="ok"/> </body> </html>

Из полученного документа методом getElementsByTagName извлекаем элемент color. Нам интересно его текстовое содержимое, которое является первым наследником элемента, чем мы пользуемся, выбирая значение свойства firstChild. Полученный узел будет текстовым, поэтому его содержание можно прочитать из свойства data. В заключение хочется привести список методов, используемых наиболее широко и составляющих, как выразились на просторах Рунета, малый джентльменский набор Web-программиста: getElementById; getElementsByTagName; createElement; createTextNode; appendChild; removeChild.


Глава 7

DOM-

функции в PHP

Объектная модель реализована в PHP 5 в соответствии со стандартом DOM Level 3, как выражаются в документации, "настолько, насколько это оказалось возможным". Для уверенного владения методами создания HTML- и XML-документов придется обстоятельно разобраться с классами, описанными в разделе "DOM functions" документации по PHP. Это семейство функций позволяет извлекать данные из XML-документов, создавать, изменять и сохранять XMLдокументы. Создание XML- или XHTML-документа следует представлять себе как построение дерева узлов, при этом для присоединения нового узла или целой ветви узлов нужно их сначала создать, указав все необходимые параметры. Затем созданные элементы следует включить в уже имеющееся дерево, указав точку прикрепления нового узла или ветки к существующему дереву. Основой работы с XML в PHP является библиотека libxml, информацию о которой можно найти на сайте http://www.xmlsoft.org. Она поддерживает основные стандарты XML: пространства имен, XML-схемы, XPath и многие другие. На том же сайте можно найти информацию о библиотеке libxslt. Обе эти библиотеки поддерживают ограниченное количество кодировок, из которых интерес с точки зрения русскоязычного программиста представляют: UTF-8, UTF-16 и ISO-8859-1. Следовательно, текстовые данные в создаваемых с помощью этих библиотек XML-документах обычно записываются в кодировке UTF-8. Если же вы указываете входные данные в иной кодировке, то функции библиотеки обычно правильно распознают кодировку и преобразуют текст в UTF-8. При работе с этой библиотекой используются предопределенные константы, позволяющие управлять обработкой XML-документа, в табл. 7.1 указаны некоторые из них, существенные для описываемых в настоящей книге задач.


Часть I. Технологии, составляющие AJAX

76

Таблица 7.1. Константы библиотеки libxml Константа

Описание

LIBXML_NOENT

Замена всех сущностей в документе на значение этих сущностей

LIBXML_NOERROR

Подавлять сообщения библиотеки libxml об ошибках, которые могут возникнуть при обработке

LIBXML_NOWARNING

Подавлять предупреждения библиотеки libxml, которые могут возникнуть при парсировании

LIBXML_NOBLANKS

Удалять пробельные символы из документа

LIBXML_NOCDATA

Преобразовывать узлы CDATA в текстовые

Классы, определенные в расширении DOM-функций, являются реализацией DOM, а поэтому их имена повторяют названия объектов DOM с приставкой DOM. Исключение составляет класс DOMDocumentFragment, не являющийся частью модели DOM. Нужен этот класс для того, чтобы в документ можно было импортировать целый фрагмент документа, а не только один узел. Метод у него один: appendXML() — вставить данные в формате XML (raw data, как говорится в документации, т. е. эти данные не оформлены в виде правильного XML-документа).

Создание XML-документа с помощью DOM-функций

Создадим простой XML-документ, содержащий только пустой корневой элемент (листинг 7.1). В начале следует вызвать конструктор класса DOMDocument и передать ему два параметра — версию языка XML и кодировку создаваемого документа. Если в документе хочется писать по-русски, необходимо указать кодировку UTF-8, иначе будет применяться кодировка по умолчанию — iso-8859-1. Это приведет к тому, что русские буквы в XMLдокументе будут заменены их сущностями (например, буква "а" будет заменена кодом а), что нисколько не поможет нам в дальнейшем. Объект $doc, который будет создан, представляет документ в целом, но пока документ пуст. Единственное, что в нем имеется, — корень документа, на который и указывает $doc. Метод createElement позволяет создать элемент с указанным именем car. Созданный в результате объект $node является объектом класса DOMElement. Следующее, что нужно сделать с объектом $node, — это включить в дерево документа, присоединив к корню методом appendChild.


Глава 7. DOM-функции в PHP

77

Листинг 7.1. Создание XML-документа <?php $doc = new DOMDocument('1.0', 'utf-8'); $node = $doc->createElement("car"); $doc->appendChild($node); header('Content-Type: text/xml'); echo $doc->saveXML(); ?>

Для того чтобы передать клиенту созданный документ, нам подойдет метод saveXML класса DOMDocument, который возвращает документ в виде строки. Для корректного отображения XML-документа в браузере необходимо передать заголовок 'Content-Type: text/xml'. Создадим теперь XML-документ, похожий на тот, что описан в главе 4 в листинге 4.1. Сначала создадим корневой элемент методом createElementNS сразу с указанием пространства имен (листинг 7.2). Затем создадим атрибут методом setAttribute класса DOMElement. Воспользуемся далее оператором new, чтобы вызывать конструкторы нужных нам классов DOMElement и DOMText. Последний отвечает за создание текстового узла. Листинг 7.2. XML-документ <?php $doc = new DOMDocument("1.0","utf-8"); $root = $doc->createElementNS('http://myford.ru', 'car'); $doc->appendChild($root); $root->setAttribute('id', 'f327'); $make = $root->appendChild(new DOMElement("make")); $make->appendChild (new DOMText("Ford")); $model = $root->appendChild(new DOMElement("model")); $model->appendChild(new DOMText("Focus")); $date = $root->appendChild(new DOMElement("date")); $year = new DOMElement("year");


Часть I. Технологии, составляющие AJAX

78 $date->appendChild($year);

$year->appendChild(new DOMText("2008")); $month = new DOMElement("month"); $date->appendChild($month); $month->appendChild(new DOMText("Май")); $color = $root->appendChild(new DOMElement("color")); $color->appendChild(new DOMText("красный")); header('Content-Type: text/xml'); echo $doc->saveXML(); ?>

Вот и все, скрипт получился однообразным, но вполне читабельным, в браузер отправляем правильный XML-документ, корректно отображающий русский текст. Наша главная задача по генерации XML-документа на серверной стороне успешно выполнена. Конечно, данные для текстовых узлов могут браться из базы данных, запрашиваться с других серверов и т. д. Осталось для лучшего понимания работы с DOM-функциями посмотреть, как с их помощью можно читать и редактировать готовый XML-документ. В следующем сценарии (листинг 7.3) мы загрузим документ из файла car.xml и прочитаем значения всех его текстовых узлов. Листинг 7.3. Чтение текстовых узлов XML-файла <?php $doc = new DOMDocument('1.0', 'utf-8'); $doc->load("car.xml", LIBXML_NOBLANKS); $root = $doc->documentElement; if ($root->hasChildNodes()) { $children = $root->childNodes; foreach($children as $node) { print $node->nodeValue."<br />"; } } ?>


Глава 7. DOM-функции в PHP

79

Сначала создаем новый пустой документ в кодировке UTF-8, указав его корень в переменной $doc. Применим здесь метод load объекта DOMDocument для загрузки документа из файла. Для того чтобы удалить лишние пробельные символы, укажем константу LIBXML_NOBLANKS. Нам это удобно потому, что в противном случае символы перехода на новую строку, которых предостаточно в нашем файле, будут преобразованы в текстовые узлы. Метод load прочитает файл, построит из него дерево и вернет готовое дерево, подключив его к корню нашего пустого документа. Затем мы хотим перейти к корневому элементу и перебрать все вложенные в него элементы. Для этого читаем свойство documentElement объекта DOMDocument и записываем полученный объект в переменную $root. Метод hasChildNodes поможет нам выяснить, есть ли элементы, вложенные в корневой элемент $root. Такая проверка не является необходимой для доступа к узлам, но служит защитой от бессмысленных действий в случае какихлибо ошибок при загрузке документа. Метод childNodes объекта DOMNode, а корневой элемент, конечно, является таким объектом, позволяет нам получить список элементов-наследников. Список этот будет объектом класса DOMNodeList, и мы сохраним его в переменной $children. Список же можно перебрать в цикле foreach, что мы и делаем, извлекая при этом значения свойства nodeValue всех отбираемых узлов. Пример получился простым, но обратите внимание на одну деталь, на экране вы увидите вот что: Ford Focus 2008май

текст комментария красный

Почему в этом выводе на экран текстовые узлы "2008" и "май" оказались на одной строке? Дело в том, что мы отбирали только узлы-наследники первого поколения, не разбирая, что вложено в них. Поэтому и получилось, что значением свойства nodeValue узла date оказалось все текстовое содержимое этого узла. Посмотрим, как можно отобрать значения элементов, задаваясь конкретными именами тегов (листинг 7.4). Листинг 7.4. Перебор элементов списка NodeList <?php $dom = new DOMDocument('1.0', 'utf-8'); $dom->load("car.xml", LIBXML_NOBLANKS);


Часть I. Технологии, составляющие AJAX

80 $date =

$dom->getElementsByTagName("date")->item(0);

$list=$date->childNodes; $length = $list->length; for ($x=0; $x < $length; $x++) { print "Element Value: ".$list->item($x)-> textContent"<br />"; } ?>

Загрузим наш XML-документ и вызовем метод getElementsByTagName, который позволит нам получить все элементы, созданные тегом <date>. Такой элемент у нас один, но метод все равно возвращает список узлов в виде объекта DOMNodeList. Поэтому для дальнейшей работы придется вызвать метод item и указать ему, что следует выбрать нулевой элемент из списка. Затем уже знакомым свойством childNodes отберем элементы-наследники. У полученного списка $list есть длина, значение которой содержится в свойстве length. Теперь все готово для того, чтобы в цикле перебрать текстовое содержимое всех элементов из полученного списка. На этот раз воспользуемся свойством textContent объекта DOMNode, чтобы прочитать нужную нам информацию.


Глава 8

Проблема русификации Web-приложений Чем отличается русскоязычный пользователь Интернета? Он думает "www", набирает "ццц", а произносит "ввв".

Народная мудрость

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

Кодировки

Набор символов (character set) — определенная таблица кодировки конечного

множества знаков. Такая таблица сопоставляет каждому символу последовательность длиной в один или несколько байтов. Кодовая страница (code page) — таблица, сопоставляющая каждому значению байта некоторый символ. Кодовая страница может содержать максимум 256 символов, из чего вытекает недостаточность всякой 8-битной кодовой страницы для представления многоязычных текстов. Есть несколько кодировок, знакомства с которыми при разработке Webприложений не избежать. Приведем пусть и широко известные, но необходимые сведения о них. ASCII (American Standard Code for Information Interchange, американский стандартный код для обмена информацией) — 7-битная компьютерная кодировка для представления латинского алфавита, десятичных цифр, знаков препинания, арифметических операций и управляющих символов. ISO-8859-1 — кодовая страница, предназначенная для западноевропейских языков. Кодовые позиции 0—31 и 127—159 здесь заполнены управляющими символами. В HTML ISO-8859-1 является кодировкой по умолчанию.


Часть I. Технологии, составляющие AJAX

82

Альтернативная кодировка, или кодировка IBM CP866, поддержка которой была добавлена в MS-DOS версии 6.22. В этой кодировке записываются имена файлов в системе FAT. CP866 до сих пор используется в консоли русифицированных систем семейства Windows NT. Windows-1251 — набор символов и кодировка, являющаяся стандартной 8-битной кодировкой для всех русских версий Microsoft Windows. Часто встречается также обозначение ANSI Cyrillic — одно из названий кодовой страницы Windows-1251. Происходит оно от названия стандартов, разрабатываемых американским национальным институтом стандартов (American National Standards Institute, ANSI). КОИ-8 — восьмибитовая ASCII-совместимая кодовая страница, разработанная для кодирования букв кириллических алфавитов. Существует несколько вариантов кодировки КОИ-8 для различных кириллических алфавитов. Русский алфавит описывается в кодировке KOI8-R. Юнико д, или Унико д (англ. Unicode) — стандарт кодирования символов, позволяющий представить знаки практически всех письменных языков. Коды в стандарте Unicode разделены на несколько областей. Область с кодами от +0000 до +007F содержит символы набора ASCII с соответствующими кодами. Далее расположены области знаков различных письменностей, знаки пунктуации и технические символы. Часть кодов зарезервирована для использования в будущем. Под символы кириллицы выделены коды от +0400 до +052F. Поскольку в ряде компьютерных систем уже были реализованы 16-битные символы, было решено все наиболее важное кодировать только в пределах первых 65 536 позиций. UTF-8 — это представление Юникода, обеспечивающее наилучшую совместимость со старыми системами, использовавшими 8-битные символы. Текст, состоящий только из символов с номером меньше 128, при записи в UTF-8 превращается в обычный текст ASCII. Символы UTF-8 получаются из Unicode следующим образом (табл. 8.1). Таблица 8.1. Кодирование символов в UTF-8 Unicode

UTF-8

0x00000000 — 0x0000007F

0xxxxxxx

0x00000080 — 0x000007FF

110xxxxx 10xxxxxx

0x00000800 — 0x0000FFFF

1110xxxx 10xxxxxx 10xxxxxx

0x00010000 — 0x001FFFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx


Глава 8. Проблема русификации Web-приложений

83

Передача локализованных данных в протоколе HTTP

Клиент может передать в HTTP-запросе серверу информацию о поддерживаемых типах данных и кодировках в заголовках (табл. 8.2). Таблица 8.2. Заголовки HTTP, устанавливающие локализационные данные Заголовок

Описание

Accept

Задает типы файлов MIME, приемлемые в отклике. Пример: Accept: text/*, text/html

Accept-Charset

Задает кодировки символов, приемлемые в отклике. Пример: Accept-Charset: iso-8859-5. Сервер должен по возможности передавать данные в указанной клиентом кодировке

Accept-Language

Задает естественные языки, приемлемые в отклике. Пример: Accept-Language: ru, en-us, en

При запросе методом GET данные передаются в виде значений переменных. Переменные добавляются к URL запрашиваемого ресурса в следующем виде: http://mysite.ru?name=Vasya

В стандарте URL можно использовать только ограниченный набор символов: латинские буквы, цифры и лишь некоторые знаки препинания. Для передачи в URL символов кириллицы нужно перекодировать эти символы особым образом. Преобразование происходит в два этапа: сначала каждый символ кириллицы кодируется в Unicode (UTF-8) в последовательность из двух байтов, а затем каждый байт этой последовательности записывается в шестнадцатеричном представлении: М и к р

→ → → →

D0 и 9C → %D0%9C D0 и B8 → %D0%B8 D0 и BA → %D0%BA D0 и 80 → %D0%80

и т. д. Перед каждым шестнадцатеричным кодом байта ставится знак процента. При таком кодировании на один символ уходит 3 байта. Альтернативой URL-кодированию является схема base64. Суть этой схемы состоит в кодировании передаваемой информации 6-битными символами.


Часть I. Технологии, составляющие AJAX

84

Алгоритм преобразований в base64 таков: символы представляются в виде их двоичных кодов. Алгоритм разбивает эту последовательность бит на группы по 6 бит, а если бит не хватает, то дописывает в конец нули. В этом алгоритме три 8-битных символа преобразуются в четыре 6-битных. Если браузер не сообщает кодировку в заголовке Accept-Charset, то сервер использует свою кодировку по умолчанию или ту, что сообщит браузеру серверное приложение. Кодировка сервера Apache по умолчанию устанавливается директивой AddDefaultCharset в конфигурационном файле httpd.conf. По причинам, подробно изложенным в этой главе далее, в настоящее время следует рекомендовать использовать только кодировку UTF-8, т. е. директива конфигурации должна выглядеть так: AddDefaultCharset UTF-8

Сервер может передать клиенту информацию о языке и кодировке в следующих заголовках (табл. 8.3). Таблица 8.3. Заголовки ответа сервера

Поле

Описание

Content-Language

Задает естественный язык, на котором написано тело сообщения. Пример: Content-Language: ru

Content-Type

Содержит тип MIME тела сообщения. Пример: Content-Type: text/html; charset=windows-1251

При передаче данных в формате plain text заголовок Content-type является единственным способом передачи сведений о кодировке. В XML-документе кодировка может передаваться в XML-декларации. Например: <?xml version="1.0" encoding="windows-1251"?>

В HTML-документе кодировку следует указывать в теге meta: <meta http-equiv="content-type" content="text/html; charset=windows1251"/>

П РИМЕЧАНИЕ ДЛЯ НАЧИНАЮЩИХ

Само по себе указание кодировки в теге meta не приводит к тому, что весь текст документа буден записан в этой кодировке, т. е. указать-то можно UTF-8, а писать при этом, например, в Windows-1251. Надо еще следить за настройками текстового редактора, который используется для написания программы. Используйте только те редакторы, которые явно указывают, в какой кодировке


Глава 8. Проблема русификации Web-приложений

85

записывается текст, например, Notepad++. Как советуют опытные люди (см. http://webmastak.com/article.aspx?id=300), при сохранении файла многие текстовые редакторы предлагают флажок Include Unicode Signature (BOM), Add Byte Order Mark или нечто подобное. Прежде всего, убедитесь, что в вашем редакторе это есть. Если похожей настройки не обнаружено — пользоваться таким редактором для серьезных задач не стоит. Найдя этот флажок, отключите его. Byte Order Mark (BOM) — это три служебных байта, которые автоматически записываются в начало документа и обозначают, что он сохранен в кодировке UTF.

Если кодировка указана в двух местах: в HTTP-заголовке и в самом документе, то браузер должен использовать кодировку, указанную в документе. Если данных о кодировке нет, то браузер либо выбирает кодировку, установленную в нем по умолчанию, либо пытается определить ее автоматически, по содержимому.

Кодирование символов в сценарии JavaScript Если в сценарии есть не ASCII-символы, то надо побеспокоиться о том, чтобы текст сценария был в той же кодировке, что и HTML-страница, в которую он подключается. Если сценарий динамически создает элементы, имеющие внешние ссылки, то компоненты строки нужно закодировать методом escape глобального объекта JavaScript. Метод escape преобразует входную строку в шестнадцатеричную кодировку. При этом все символы, не являющиеся символами латиницы, заменяются на их шестнадцатеричные escape-коды %xx. Глобальный объект создается исполняющей системой JavaScript перед началом исполнения сценария и располагает методами, указанными в табл. 8.4. Таблица 8.4. Методы глобального объекта

Метод

Описание

decodeURI

Декодирует URI

decodeURIComponent

Декодирует компонент URI

encodeURI

Кодирует URI

encodeURIComponent

Кодирует компонент URI

escape unescape

Преобразует строку в шестнадцатеричную кодировку

Unicode Преобразует шестнадцатеричную кодировку Unicode в строку


Часть I. Технологии, составляющие AJAX

86

Для кодировки строк, содержащих URI, следует использовать методы encodeURI и encodeURIComponent. Разберитесь в этом хорошенько, поэкспериментировав с этими методами, например, в таком сценарии, как тот, что представлен в листинге 8.1. Листинг 8.1. Методы глобального объекта <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>encodeURI</title> <script> document.write(encodeURI("http://www.test.com?val=привет")) </script> </head> <body> </body> </html>

Русский язык в PHP Для работы со строками в кодировке UTF-8 к модулю PHP необходимо подключать расширение для работы с многобайтными кодировками php_mbstring.dll, для этого на этапе установки PHP надо отметить расширение multi-byte string. Необходимо помнить, что стандартные функции для работы с регулярными выражениями, для преобразования и сравнения строк в UTF-8 выполняются функциями, имена которых начинаются с префикса mb. Например, преобразование строки к верхнему регистру выполняется функцией mb_strtoupper. Локализация в PHP производится по тем же правилам, что и вообще в UNIX, т. е. существует понятие локали (от англ. locale), определяющей формат вывода данных. Локализовать можно вывод текста, стандартных диагностических сообщений, формируемых приложениями, формат вывода даты и времени, денежных показателей и т. п. В PHP определен ряд именованных констант для задания категорий локализации: LC_ALL — все из нижеперечисленного; LC_COLLATE — для сравнения строк; LC_CTYPE — для преобразований как, например, в функции strtoupper; LC_MONETARY — для форматирования денежных данных функцией localeconv;


Глава 8. Проблема русификации Web-приложений LC_NUMERIC LC_TIME

87

— для разделителя целой и дробной части числа;

— для форматирования даты и времени в функции strftime;

LC_MESSAGES

— для системных диагностических сообщений.

Установить параметры локализации можно функцией пример, так:

setlocale,

указав, на-

setlocale(LC_TIME, 'RUS'); echo strftime('%A', time());

И все бы хорошо, но работать это будет корректно, только пока ваш сервер настроен на дефолтовую кодировку Windows-1251. А если вы настроили на UTF-8, то с удивлением обнаруживаете, что функция strftime в приведенном примере выдает русское название дня недели по-прежнему в кодировке Windows-1251. Приходится применять функцию iconv: setlocale(LC_ALL, 'RUS'); echo iconv('windows-1251', 'UTF-8', strftime('%A'));

Функция iconv служит для преобразования кодировки строки: в первом ее параметре — входная кодировка, во втором — выходная, а в третьем — преобразуемая строка. Вообще, внутреннее представление данных в PHP осуществляется в UTF-8. На практике это означает, что функции PHP анализируют входные данные, причем некоторые функции могут правильно определить входную кодировку, а некоторые нет. Скажем, функции графической библиотеки gd2 рассматривают входной текст как строку в кодировке UTF-8. Например, так ведет себя функция imagettftext, которая пишет текст на динамически создаваемом рисунке, но результат будет удобочитаемым, только если передаваемый ей текст действительно написан в кодировке UTF-8. То есть автоматического преобразования входных данных в строку с кодировкой UTF-8 не производится, строку в иной кодировке требуется перекодировать с помощью функции iconv. Функции для работы с XML-документами корректно распознают входную кодировку парсируемого текста, автоматически преобразуют в UTF-8, выполняют необходимые преобразования и генерируют результат в кодировке UTF-8. Это относится к функциям SAX Parser, классу SimpleXML и DOMфункциям. Если вы рассчитываете, что пользователь читает, скажем, новостную ленту в Windows-1251, то опять придется обращаться к iconv. При этом для добавления текстового узла в XML-документ с помощью функций SimpleXML опять же надо побеспокоиться о том, чтобы добавляемая строка была в UTF-8. Разумеется, настройка всех составляющих клиент-


Часть I. Технологии, составляющие AJAX

88

серверного взаимодействия в этом случае на UTF-8 снимает нужду в функции iconv. Но есть и еще одна проблема с кодировками, возникающая при работе с форматом JSON в AJAX. Функция json_encode, предназначенная для преобразования данных в JSON, выдает вместо русских букв коды, как это видно на следующем примере (листинг 8.2).

Листинг 8.2. Работа функции json_encode с русскими строками <?php class Table { public $material = "дерево"; } $test = new Table; echo json_encode($test); ?>

Этот простой пример выдаст вам следующее: {"material":"\u0434\u0435\u0440\u0435\u0432\u043e"}

Результат едва ли можно признать удовлетворительным, т. е. мы получили русские буквы, но представлены они в виде кодов. Вообще говоря, для подобных случаев приходится применять функции преобразования символов вроде представленной в листинге 8.3.

Листинг 8.3. Функция преобразования кодов <?php function String_RusCharsDeCode($string) { $russian_codes = array('\u0410','\u0411','\u0412','\u0413','\u0414','\u0415','\u0401', '\u0416','\u0417','\u0418','\u0419','\u041a','\u041b','\u041c','\u041d', '\u041e','\u041f','\u0420','\u0421','\u0422','\u0423','\u0424','\u0425', '\u0426','\u0427','\u0428','\u0429','\u042a','\u042b','\u042c','\u042d', '\u042e','\u042f','\u0430','\u0431','\u0432','\u0433','\u0434','\u0435', '\u0451','\u0436','\u0437','\u0438','\u0439','\u043a','\u043b','\u043c', '\u043d','\u043e','\u043f','\u0440','\u0441','\u0442','\u0443','\u0444', '\u0445','\u0446','\u0447','\u0448','\u0449','\u044a','\u044b','\u044c', '\u044d','\u044e','\u044f');


Глава 8. Проблема русификации Web-приложений

89

$russian_chars = array("А", "Б", "В", "Г", "Д", "Е", "Е", "Ж", "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я", "а", "б", "в", "г", "д", "е", "е", "ж", "з", "и", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", "ь", "э", "ю", "я"); return str_replace($russian_codes,$russian_chars,$string); } ?>

Сохраним эти функцию в файле ruschars.php. Но в нашем случае данные в формате JSON будет обрабатывать метод eval() глобального объекта JavaScript, который благополучно преобразует коды в русские буквы.

Локализация MySQL Начиная с версии MySQL 4.1, кодировку строк можно задать не только для всего сервера в целом, но и отдельно для каждой базы, таблицы и даже каждого столбца. Кроме того, при сравнении строк в MySQL необходимо решить, собираетесь ли вы различать регистр букв. Способ сравнения строк в MySQL называется COLLATION, что переводится обычно словом "сравнение" или "сопоставление". Сопоставление влияет именно на то, как приложение работает с совокупностью символов, оно влияет на порядок сортировки и на сравнение символов. Название каждого сопоставления начинается с имени соответствующей кодировки (например, utf8_). Далее идет спецификация сопоставления (чаще всего, идет простое сопоставление, general, не идентифицирующее буквы) и указание на чувствительность к регистру (cs (case sensitive) — чувствительно к регистру, ci (case insensitive) — не чувствительно). Отдельно стоит отметить бинарные сопоставления (binary). Если строки сопоставляются бинарным образом, то между ними не делается никаких сопоставлений и преобразований. После установки сервера MySQL в Windows необходимо настроить работу с кодировками и сопоставлениями, иначе русские буквы в базе корректно отображаться не будут. Для этого в каталоге, где вы установили MySQL, надо найти конфигурационный файл my.ini. В нем нам понадобится серверная секция, которая помечена так: # SERVER SECTION [mysqld]


90

Часть I. Технологии, составляющие AJAX

Найдите в этой секции директиву default-character-set, задающую кодировку сервера по умолчанию, и укажите для нее следующее значение: default-character-set=utf8

Кроме того, можно настроить взаимодействие с клиентом на кодировку UTF-8, добавив директивы init-connect="SET NAMES utf8" skip-character-set-client-handshake

При создании баз можно явно указывать кодировку и способ сравнения: CREATE DATABASE dbname DEFAULT CHARACTER SET cp1251 COLLATE cp1251_bin;

Наконец, утилита mysqldump по умолчанию сохраняет данные в файл в utf8 вне зависимости от того, в какой кодировке хранились ваши данные в базе.


Часть II

Создание AJAX-приложений


Глава 9

Объект XMLHttpRequest В технологии AJAX сценарий JavaScript выполняет следующие задачи: создание HTTP-запроса; отправка запроса и получение ответа от сервера. Отслеживание стадий выполнения такого запроса; обработка ответа, которая, в свою очередь, состоит из: • разбора полученного документа и вычленения из него данных; • создания фрагмента документа, включаемого в уже загруженную в браузер и отражаемую в его окне Web-страницу. Пришла пора заняться собственно запросом к серверу. Для создания запроса сценарию потребуется соответствующий объект, содержащий все данные запроса. По мере поступления ответа от сервера в этом объекте потребуется хранить информацию об успешности или неудаче запроса, при успехе надо будет хранить данные ответа, в каком бы формате они ни поступали. Такой объект существует и поддерживается большинством браузеров, называется он XMLHttpRequest. Черновой вариант спецификации объекта XMLHttpRequest создан рабочей группой под руководством World Wide Web Consortium (W3C) и опубликован 15 апреля 2008. Этот объект давно поддерживается многими браузерами и позволяет формировать запрос клиента серверу по протоколу HTTP, отслеживать и обрабатывать ответ сервера. Объект поддерживает передачу данных в текстовом формате, в первую очередь, в формате XML. Он может быть использован для создания как HTTP-, так и HTTPS-запросов. Методы объекта XMLHttpRequest представлены в табл. 9.1 и позволяют понять последовательность действий при отправке такого запроса.


Часть II. Создание AJAX-приложений

94

Таблица 9.1. Методы класса XMLHttpRequest

Метод open(method, URL, async, userName, password) setRequestHeader(label, value) send(content) abort() getAllResponseHeaders() getResponseHeader(headerName)

Описание Определяет метод, URL и другие параметры запроса Добавляет HTTP-заголовок к запросу Отправляет запрос на сервер Отменяет текущий запрос Возвращает полный список HTTPзаголовков в виде строки Возвращает значение указанного заголовка

Сначала сценарий JavaScript должен создать сам объект запроса. При генерации объекта не происходит никаких видимых изменений на странице, не отправляется еще и сам запрос. После создания объекта запроса его надо наполнить данными методом open. Метод open получает в качестве параметра название HTTP-метода (GET или POST), который будет задействован, адрес запрашиваемого ресурса, значение параметра async и данные для аутентификации клиента (логин и пароль), если они необходимы. Параметр async определяет, в каком режиме будет работать запрос. Если значение этого параметра true (по умолчанию так и есть), то будет использоваться асинхронный запрос, т. е. выполнение сценария продолжится после отправки запроса. Пользователь не заметит отправки запроса и сможет продолжать работу со страницей, не дожидаясь ответа сервера. Если же значение параметра async равно false, то сценарий будет ожидать ответа сервера, чтобы продолжить работу. Ясно, что второй вариант может привести к неприятным последствиям при возникновении проблем с сервером или каналами связи с ним. Для отправки подготовленного запроса используется метод send. Его единственным параметром является параметр content, значением которого могут быть данные для POST-запроса или пустая строка в случае использования метода GET. Итак, мы видим, что методы объекта XMLHttpRequest позволяют создать и отправить запрос, а затем прочитать заголовки ответа сервера. Посмотрим теперь, где находятся данные из ответа сервера (табл. 9.2).


Глава 9. Объект XMLHttpRequest

95

Таблица 9.2. Свойства класса XMLHttpRequest

Свойство readyState status statusText onreadystatechange responseText responseXML

Описание Возвращает текущее состояние объекта Возвращает HTTP-статус ответа сервера в виде числа (404 — "Not Found", 200 — "OK" и т. д.) Возвращает статус в виде строки ("Not Found", "OK" и т. д.) Содержит обработчик события, которое происходит при каждой смене состояния объекта Текст ответа на запрос Ответ на запрос в виде дерева XML, который затем может быть обработан сценарием JavaScript с использованием DOM

Свойство readyState позволяет отслеживать прохождение запроса. Это свойство может принимать одно из следующих значений: 0 — состояние после создания объекта XMLHttpRequest, но до вызова метода open, который инициализирует параметры запроса; 1 — запрос открыт, после вызова метода open, но до вызова метода send; 2 — отправка данных, после вызова метода send; 3 — получение данных после того, как браузер соединится с сервером, но до завершения сервером ответа; 4 — данные загружены, после завершения запроса и полного получения всех данных ответа с сервера. Каждая смена состояния вызывает событие, которое может быть обработано. Для этого необходимо назначить функцию-обработчик события, указываемую в качестве значения свойства onreadystatechange. Иначе говоря, это свойство определяет, какая функция будет вызываться каждый раз, когда изменяется свойство readyState. На английском языке функции, вызываемые при наступлении события, называются "callback functions", поэтому в русскоязычной литературе появился термин "функции обратного вызова", что зачастую сбивает с толку читателя, не знакомого с исходной терминологией. Браузеры по-разному отслеживают изменения свойства readyState, некоторые изменения проходят для них незаметно, но установка readyState в значение 4 сообщает, что данные получены, и пора их обрабатывать.


96

Часть II. Создание AJAX-приложений

Ответ в текстовом формате можно считать из свойства responseText. Для того чтобы ответ сервера был воспринят клиентским сценарием как XML-документ, он должен содержать заголовок Content-Type с корректным MIME-типом text/xml или application/xml. Тогда этот ответ будет содержаться в свойстве responseXML, и его следует обрабатывать в соответствии со спецификацией XML и моделью DOM. В случае наличия ошибок в пришедшем ответе значением свойств responseText и esponseXML становится null. Рассмотрим простой пример. Пользователь открывает страницу, представленную в листинге 9.1. Листинг 9.1. Страница с обработкой события click <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>AJAX</title> <meta http-equiv="Content-Language" content="ru"> <meta http-equiv="Content-Type" content="text/html; UTF-8"> <script type="text/javascript" src="request.js"></script> </head> <body onclick="http_zapros()"> Щелкните где-нибудь мышью, пожалуйста. <br/> <div id="otvet" /> </body> </html>

Открыв в браузере эту страницу и щелкнув в произвольном месте экрана, вы увидите, что вид страницы изменился. Он стал таким:

Щелкните где-нибудь мышью, пожалуйста. Ответ сервера. Но происходит это, только если вы обращаетесь к этой странице по протоколу HTTP. Рассмотрим сценарий JavaScript, работа которого и привела к изменениям на нашей странице (листинг 9.2).

Листинг 9.2. Сценарий request.js // Создаем объект XMLHttpRequest var xmlHttp = createXmlHttpRequestObject(); // Функция создания объекта XMLHttpRequest


Глава 9. Объект XMLHttpRequest function createXmlHttpRequestObject() { var xmlHttp; try { // Firefox, Opera 8.0+, Safari xmlHttp=new XMLHttpRequest(); } catch (e) { // Internet Explorer try { xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("Ваш браузер не поддерживает AJAX!"); return false; } } } return xmlHttp; } // Отправка асинхронного HTTP-запроса function http_zapros() { if (xmlHttp) { // Попытка отправки запроса серверу try {

97


Часть II. Создание AJAX-приложений

98

// Запрос файла request.txt с сервера xmlHttp.open("GET", "request.txt", true); xmlHttp.onreadystatechange = obrabotka; xmlHttp.send(null); } // Сообщение об ошибке в случае неудачи catch (e) { alert("Не удается соединиться с сервером"); } } } // Функция обработки ответа сервера function obrabotka() { // Только в этом состоянии ответа обрабатываем пришедшие данные if (xmlHttp.readyState == 4) { // Данные читаем, только если статус - "OK" if (xmlHttp.status == 200) { try { // Чтение сообщения сервера response = xmlHttp.responseText; // Ищем место на странице, где будем писать ответ сервера myDiv = document.getElementById("otvet"); // Отображение сообщения myDiv.innerHTML += response; } catch(e) { // Сообщение об ошибке alert("Ошибка при чтении ответа"); } } else {


Глава 9. Объект XMLHttpRequest

99

// Вывод сообщения о статусе ответа

alert("Возникла проблема при получении данных с сервера:\n" + xmlHttp.statusText);

} } }

Обратите внимание на то, что первая часть сценария request.js не связана с реакцией на событие. Этот сценарий подключается к странице на этапе загрузки ее в браузер и сразу начинает выполняться. Таким образом, первое, что делает сценарий, — это объявление переменной xmlHttp, которая получает значение, возвращаемое функцией createXmlHttpRequestObject. Задача этой функции — инициализировать в браузере объект XMLHttpRequest, просто создать его, но не наполнять конкретным адресом запроса и другими параметрами. Делать это мы будем очень осторожно: дело в том, что не все браузеры поддерживают этот объект. В браузерах Firefox, Opera 8.0+ и Safari объект асинхронного запроса можно создать, просто обратившись к соответствующему прототипу XMLHttpRequest. А вот браузер Internet Explorer версии 6.0 и более ранних версий не поддерживает объект XMLHttpRequest. Это вынуждает нас обращаться к библиотеке ActiveXObject, что мы и делаем. В разных версиях Internet Explorer работают различные версии этой библиотеки, что тоже приходится учитывать, но это осложнение мы рассмотрим в следующих примерах. Зная, что любое действие может вызвать ошибку в браузере пользователя, мы помещаем наши попытки создать объект внутрь блока try...catch. При благоприятном исходе функция возвращает объект xmlHttp, а при неудаче выводит на экран диагностическое сообщение в окне alert. Но вот объект создан, он находится в состоянии с readyState=0, всю исходную страницу загрузили в браузер, и пользователь начинает щелкать мышью. При щелчке вызывается функция http_zapros, которая для начала проверяет, удалось ли создать объект запроса. Если удалось, то начинается формирование и отправка запроса на сервер. Пользуясь методом open, мы задаем необходимые параметры запроса, т. е. указываем, что запрос отправляется методом GET, к ресурсу на сервере request.txt запрос будет выполняться асинхронно. В отношении типа файла, к которому мы отправляем запрос, нет никаких ограничений: он может быть простым текстом, HTML-страницей, PHP-скриптом — чем угодно. Важно другое: обычно невозможно отправить запрос к документу, расположенному не на том сервере, с которого была получена текущая страница. Таковы


100

Часть II. Создание AJAX-приложений

настройки безопасности в браузерах, они запрещают сценарию JavaScript обращаться к другому серверу. Настройки несколько отличаются в разных браузерах, но общее правило вам лучше запомнить с самого начала. Повторим еще раз, чтобы читатель не забывал в дальнейшем: сценарий JavaScript и страница, которую он запрашивает, должны быть на одном и том же сервере! Следующее, что надо сделать, — это задать значение свойства onreadystatechange. Оно должно содержать указатель на функцию, которая будет вызываться всякий раз, когда меняется состояние запроса. Вот это и есть callback-функция, назовем ее obrabotka, а определим несколько позже. Теперь надо отправить запрос, для этого вызываем метод send. Раз запрос посылается методом GET, то send вызывается с пустым параметром. Поскольку ошибки могут поджидать в любом месте, то и все только что описанные действия мы помещаем в блок try и подготавливаем сообщение на случай неудачи с отправкой запроса. На этом работа функции http_zapros заканчивается. Функция obrabotka, как мы уже сказали, вызывается при любом изменении состояния запроса, но что-то делать ей надо только после того, как клиентский браузер закончил прием ответа сервера, что соответствует состоянию readyState=4. Добавлять же данные в страницу нам потребуется только в случае, если статус ответа сервера равен 200, т. е. xmlHttp.status=200. Ответ сервера в данном случае представляет собой простой текст (таково содержимое файла request.txt), поэтому его можно прочитать из свойства responseText объекта xmlHttp. Затем надо найти место на странице, где будет выведено содержимое файла request.txt. Методом getElementById выбираем элемент с заданным идентификатором и меняем значение его свойства innerHTML, добиваясь нужного результата. Ну и, конечно, не забываем о подстерегающих неприятностях, обрабатывая потенциальные ошибки в блоке try/catch.

В этой главе мы познакомились с тем, как создать, отправить и обработать простой асинхронный запрос. Впереди нас ждет генерация ответа сервера в формате XML и JSON, обработка таких ответов на клиентской стороне и получение данных для ответа сервера из базы данных MySQL.


Глава 10

Использование XML и создание периодических запросов Начиная с этого момента, мы будем использовать усовершенствованную функцию создания объекта XMLHttpRequest, предусматривающую наличие у клиента старых версий браузера Internet Explorer. Будем держать эту функцию в файле requestobject.js, который представлен в листинге 10.1. Листинг 10.1. Функция создания объекта XMLHttpRequest var xmlHttp = createXmlHttpRequestObject(); function createXmlHttpRequestObject() { var xmlHttp; try { xmlHttp = new XMLHttpRequest(); } catch(e) { // Создаем объект в браузере IE6 или более старых версиях var XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"); // Пробуем создать до тех пор, пока не получится for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++) {


Часть II. Создание AJAX-приложений

102 try {

xmlHttp = new ActiveXObject(XmlHttpVersions[i]); } catch (e) {} } } if (!xmlHttp) alert("Ошибка при попытке создания объекта XMLHttpRequest"); else return xmlHttp; }

Массив XmlHttpVersions состоит из названий различных версий библиотеки MSXML, которая используется для создания объекта XMLHttpRequest в браузере Internet Explorer. Перебираем версии, начиная с самой свежей, до тех пор, пока не удастся создать объект, если его не удалось создать в самом начале оператором new. Теперь, когда мы запаслись удобной функцией, начнем обращаться, используя асинхронный запрос, к сценарию PHP. Этот сценарий будет генерировать ответ в виде XML-документа, а клиентский JavaScript-сценарий обработает ответ и добавит на страницу часть этого ответа. Посмотрите листинг 10.2, где представлена страница, предлагающая пользователю список автомобилей (например, он хочет взять один из них напрокат и выбирает цвет). Пользователь щелкает по выбранному автомобилю мышью, и внизу появляется квадратик, закрашенный цветом, в который выкрашен этот автомобиль. Сам список автомобилей нетрудно сгенерировать из серверных данных, но нас сейчас интересует другой аспект работы со страницей, поэтому список сделан коротким и простым. Листинг 10.2. Страница со списком автомобилей <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>AJAX</title> <meta http-equiv="Content-Language" content="ru"> <meta http-equiv="Content-Type" content="text/html; UTF-8">


Глава 10. Использование XML и создание периодических запросов

103

<script type="text/javascript" src="requestobject.js"></script> <script type="text/javascript" src="generateXML.js"></script> </head> <body >

Выберите автомобиль, чтобы узнать его цвет: <ul> <?php $avto = array("Opel", "Ford"); for ($i=0; $i<2; $i++) { ?> <li> <a href="javascript:void(0);" onclick="http_zapros('<?php echo $avto[$i]; ?>');"> <?php echo $avto[$i]; ?> </a> </li> <?php

} ?>

</ul> <div id="otvet" /> </body> </html>

Сценарий 10.2.php создает список автомобилей. Их названия хранятся в массиве $avto, а затем поодиночке выводятся в виде элементов списка. Для того чтобы пользователь понимал, куда следует щелкать, названия автомобилей представлены в виде ссылок. Операция void(0) приводит к тому, что никаких стандартных действий по обработке щелчка не происходит, но событие click приводит к вызову функции http_zapros, получающей в виде параметра идентификатор элемента, на котором произошло событие. Для того чтобы лучше понять, как все это работает, замените событие click на mouseover. Если вы пользуетесь браузером Mozilla Firefox, то к этому моменту в вашем браузере уже должен быть Firebug (посмотрите приложение 2, описывающее применение этого удобного отладчика), так что не забывайте нажимать клавишу <F12>, чтобы подробнее посмотреть, что происходит при работе вашей программы. Прекрасен Firebug и в том случае, когда надо понять, где возникла ошибка. Функция http_zapros определена в сценарии requestXML.js, который дан в листинге 10.3. Для отправки запроса серверу этой функции понадобится информация о том, по какому элементу щелкнули в браузере. В нашем случае сгодилось бы и значение идентификатора, но мы сделаем иначе: прочитаем текстовое содержимое элемента и отправим его на сервер, чтобы серверный сценарий по названию автомобиля переправил клиенту все данные о нем, завернув эти данные в структуру XML. Запрос к сценарию generateXML.php отправим методом GET, а название автомобиля передадим как значение параметра name.


Часть II. Создание AJAX-приложений

104

Листинг 10.3. Сценарий generateXML.js function http_zapros(el) { if (xmlHttp) { try { // Читаем название выбранного автомобиля var params = "name=" + el; // Отправляем запрос к сценарию

generateXML.php

xmlHttp.open("GET", "generateXML.php?" + params, true); xmlHttp.onreadystatechange = requestControl; xmlHttp.send(null); } catch (e) { alert("Невозможно соединиться с сервером:\n" + e.toString()); } } } // Функция, вызываемая при изменении состояния запроса function requestControl() { // Если readyState равно 4, то мы готовы обрабатывать ответ сервера if (xmlHttp.readyState == 4) { // Продолжаем, только если статус HTTP равен "OK" if (xmlHttp.status == 200) { try { // Обрабатываем ответ сервера obrabotka(); } catch(e)


Глава 10. Использование XML и создание периодических запросов

105

{ alert("Ошибка при обработке ответа сервера: " + e.toString()); } } else { // Показываем статус ответа сервера

alert("Проблема с получением данных от сервера:\n" + xmlHttp.statusText);

} } } // Обработка ответа сервера function obrabotka() { // Ожидаем, что ответ пришел в виде XML var xmlResponse = xmlHttp.responseXML; // Ловим возможные ошибки в IE и Opera if (!xmlResponse || !xmlResponse.documentElement) throw("Ответ не содержит XML-данных:\n" + xmlHttp.responseText);

// Получаем корневой элемент ответа

xmlRoot = xmlResponse.documentElement; ar = xmlRoot.getElementsByTagName("color"); // Выбираем текстовое содержимое первого элемента, вложенного в // третий элемент из числа наследников корневого

responselcolor = xmlRoot.childNodes[2].firstChild.data; myDiv = document.getElementById("otvet"); myDiv.innerHTML = "<div style='width:50px;height:50px;background-color:" + responselcolor + " ' />"; }

Обработка изменения состояния запроса осуществляется в функции requestControl. Поскольку XML-структуру придется разбирать, а процесс этот в общем случае довольно длинный, то вынесем его в отдельную функцию obrabotka, вызываемую, когда readyState стал равен 4, причем статус ответа — 200. При этом, как всегда, позаботимся о том, чтобы возможные ошибки были корректно обработаны.


106

Часть II. Создание AJAX-приложений

Для того чтобы понять, что, собственно, надо обрабатывать из ответа сервера, посмотрим на то, как этот ответ формируется в сценарии generateXML.php (листинг 10.4). Листинг 10.4. Сценарий generateXML.php, создающий ответ сервера <?php $param = mb_strtoupper($_GET['name']); if($param != "") { if ($param == "FORD") $kraska ="#FFFF00"; if ($param == "OPEL") $kraska ="#00FF00"; $doc = new DOMDocument("1.0","utf-8"); $root = $doc->createElementNS('http://myford.ru', 'car'); $doc->appendChild($root); $model = $root->appendChild(new DOMElement("model")); $model->appendChild(new DOMText("Focus")); $date = $root->appendChild(new DOMElement("date")); $date->appendChild(new DOMText("2008")); $color = $root->appendChild(new DOMElement("color")); $color->appendChild(new DOMText($kraska)); header('Content-Type: text/xml'); echo $doc->saveXML(); } ?>

Начнем мы с того, что в серверном сценарии прочитаем переданный параметр name. Для удобства сравнения строк их обычно преобразуют к верхнему регистру, и вот тут-то нельзя забывать, что все строковые операции приходится выполнять над строками в UTF-8, а потому используем функции, имена которых начинаются с приставки mb. Во избежание выполнения лишних действий сначала проверяем, не пустая ли строка была передана серверному сценарию. В данном примере, не мудрствуя лукаво, просто генерируем XML-ответ из готовых текстов. Но, конечно, следующим шагом должен стать запрос этих данных из базы или с какого-то иного сервера, ведь никаких ограничений


Глава 10. Использование XML и создание периодических запросов

107

по части безопасности для серверных сценариев не существует. Создаем небольшой XML-документ, предваряем его заголовком Content-Type: text/xml и отправляем клиенту. Обратите внимание, что корневой элемент созданного документа — car, а в клиентском сценарии мы захотим прочитать текстовое содержимое элемента color, чтобы использовать его для рисования на нашей странице цветного квадратика. Посмотрите на рис. 10.1, какую информацию о работе этого сценария выводит Firebug.

Рис. 10.1. Web-страница с заголовками ответа сервера в окне Firebug

Вернемся теперь к сценарию requestXML.js. Раз ответ пришел в виде XML, то читать его следует из свойства responseXML. Обращаясь к этому свойству, мы получим ответ в виде дерева DOM, что диктует способ дальнейшего обращения с ответом сервера. Сначала предстоит найти корневой элемент документа, прочитав свойство documentElement объекта xmlResponse, содержащего ответ сервера.


Часть II. Создание AJAX-приложений

108

Затем, если нам нужны все данные из ответа, надо перебирать узлы полученного дерева, но мы сделаем проще: найдем узел, содержащий цвет автомобиля. Сделаем это, запросив список элементов, вложенных в корневой элемент. Свойство childNodes даст желаемый результат, вернув нам массив таких узлов. Из этого массива выберем третий элемент (считаем с 0), найдем первый вложенный в него элемент с помощью свойства firstChild, это будет текстовый узел, и запросим его свойство data для получения требуемого значения. Вот теперь переменная responselcolor будет содержать цвет выбранного автомобиля, и этим цветом мы закрасим маленький квадрат, добавив его в страницу. Мы получили нужный результат, но, как только требуется более основательный анализ полученного XML-документа, сценарий обработки разрастается, да и сам XML-формат при большом количестве передаваемой информации становится неудобным: слишком много в нем избыточности, поэтому неудивительно, что, несмотря на то, что сама аббревиатура AJAX включает в себя XML, в настоящее время большинство разработчиков предпочитает передавать данные в формате JSON, что мы и продемонстрируем в следующих главах.

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

Листинг 10.5. Страница с полем ввода номера телефона <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>AJAX </title><meta http-equiv="Content-Language" content="ru"> <meta http-equiv="Content-Type" content="text/html; UTF-8"> <script type="text/javascript" src="requestobject.js"></script> <script type="text/javascript" src="settime.js"></script> </head> <body onload='http_zapros()'>


Глава 10. Использование XML и создание периодических запросов

109

Наберите номер своего телефона: <input type="text" id="myNumber" /> <div id="myDiv" /> </body> </html>

Генерация объекта XMLHttpRequest вынесена в подключаемый файл requestobject.js, который мы обсуждали ранее. Поэтому мы прямо перейдем к рассмотрению сценария JavaScript в файле settime.js (листинг 10.6), в котором определена функция http_zapros. Эта функция будет вызвана сразу после загрузки страницы в браузер. Листинг 10.6. Сценарий settime.js function http_zapros() { if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0) { // Запрашиваем значение поля формы name = encodeURIComponent(document.getElementById("myNumber").value); // Выполняем запрос к серверу xmlHttp.open("GET", "settime.php?name=" + name, true); // Определяем функции обработки события xmlHttp.onreadystatechange = obrabotka; // Отправляем запрос к серверу xmlHttp.send(null); } else // В случае занятости повторяем запрос через 1 секунду setTimeout('http_zapros()', 1000); } // function obrabotka() { // Начинаем обработку при завершении запроса if (xmlHttp.readyState == 4) { // Обрабатываем ответ, если его статус равен 200 if (xmlHttp.status == 200)


Часть II. Создание AJAX-приложений

110 { // Читаем текстовый ответ сервера xmlResponse = xmlHttp.responseText;

document.getElementById("myDiv").innerHTML = '<i>' + xmlResponse + '</i>'; // Повторяем вызов функции через 1 секунду setTimeout('http_zapros()', 1000); } // Если статус ответа отличен от 200, то выводим сообщение об ошибке else { alert("Проблема доступа к серверу: " + xmlHttp.statusText); } } }

Функция http_zapros начинает свою работу с проверки состояния запроса, а затем считывает данные из поля формы с идентификатором Mynumber. Этот идентификатор отправится на сервер в качестве параметра запроса к сценарию settime.php. В случае, если запрос к моменту обращения оказался не готов, попытка вызвать функцию http_zapros будет предпринята снова через 1 сек. Ответ сервера формируется в сценарии settime.php, представленном в листинге 10.7. Листинг 10.7. Сценарий settime.php <?php $name = $_GET['name']; if (preg_match("/(\d){7}/", $name)) echo 'Вы набрали ' . $name; else echo 'Наберите 7 цифр!'; ?>

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


Глава 10. Использование XML и создание периодических запросов

111

Рис. 10.2. Страница, периодически проверяющая состояние поля ввода формы

Надо отметить, что разработка и отладка AJAX-приложений для браузера Internet Explorer имеет несколько осложняющих ситуацию особенностей. Вопервых, Internet Explorer кэширует GET-запросы, следует использовать либо запросы методом POST, либо отправлять GET-запросы каждый раз с новым значением параметра. Альтернативным решением является запрет кэширования, который осуществляется отправкой специальных заголовков HTTP, как показано в следующей главе. Тем не менее, полностью решить проблему такого кэширования оказывается непростой задачей. Вторым аспектом работы с Internet Explorer оказываются ошибки, возникающие при попытке создать объекты JavaScript, одноименные с HTMLэлементами страницы. Эта недокументированная особенность заставляет рекомендовать отказаться от создания объектов JavaScript, имена которых совпадают с идентификаторами или именами тегов.


Глава 11

Запрос данных с сервера MySQL В реальной жизни большие объемы информации хранят в базах данных, и настало время формировать ответ сервера, обращаясь к серверу MySQL. В этой главе мы создадим базу данных и будем хранить в ней данные о нескольких автомобилях. Предоставим возможность пользователю снова запросить цвет автомобиля, теперь выбор данных производится по имени владельца. Имя отправляется на сервер MySQL. Web-страница, предлагающая пользователю узнать цвет автомобиля, дана в листинге 11.1. Листинг 11.1. Страница запроса данных с сервера MySQL <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html> <head> <title>Используем MySQL</title> <meta http-equiv="Content-Type" content="text/html; UTF-8"> <link href="12.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="requestobject.js"></script> <script type="text/javascript" src="requestMySQL.js"></script> </head> <body > <div class="dialog"> <span>Цвет автомобиля у моих друзей:</span> <select id="сar" onchange="http_zapros()"> <option value=""/><span>Выбери имя</span> <option value="Михаил"/><span>Михаил</span> <option value="Анна"/><span>Анна</span>


Глава 11. Запрос данных с сервера MySQL

113

<option value="Петр"/><span>Петр</span>

<option value="Наталия"/><span>Наталия</span> </select><div />

Ответ сервера: <div id="myDiv" class="myDiv" /> </div> </body> </html>

Итак, нам потребуется база данных, назовем ее ajax и создадим в ней таблицу cars. Сценарий создания таблицы представлен в листинге 11.2. При создании базы укажите, что вы хотите использовать кодировку UTF-8. В таблице cars мы будем хранить сведения о нескольких автомобилях, имени владельцев, марке автомобиля, модели, годе выпуска и цвете. Внесем в таблицу несколько строк данных. Листинг 11.2. Сценарий ajax.sql для создания таблицы в базе данных ajax CREATE TABLE cars ( car_id INT UNSIGNED NOT NULL AUTO_INCREMENT primary key, owner VARCHAR(30) NOT NULL, make VARCHAR(30) NOT NULL, model VARCHAR(30) NOT NULL, kogda year(4) not null, color VARCHAR(30) NOT NULL ); INSERT INTO cars (owner, make, model, kogda, color) VALUES ('Михаил','opel','astra', 2008, '#C0C0C0'); INSERT INTO cars (owner, make, model, kogda, color) VALUES ('Анна','opel','meriva', 2007, '#FAEBD7'); INSERT INTO cars (owner, make, model, kogda, color) VALUES ('Петр','ford','focus', 2007, '#800000'); INSERT INTO cars (owner, make, model, kogda, color) VALUES ('Наталия','subaru','impreza', 2007, '#008000'); grant all on ajax.* to 'myname@localhost' identified by 'secret';

Наш сценарий будет обращаться к серверу MySQL от имени пользователя myname с паролем secret. Поэтому не забудем создать такого пользователя


Часть II. Создание AJAX-приложений

114

сервера MySQL командой grant. Параметры для установления соединения с сервером запишем в конфигурационный файл config.php (листинг 11.3).

Листинг 11.3. Конфигурационный файл config.php <?php // Константы для соединения с сервером define('DB_HOST', 'localhost'); define('DB_USER', 'myname'); define('DB_PASSWORD', 'secret'); define('DB_DATABASE', 'ajax'); ?>

Для работы с базой данных мы хотим использовать класс, в нем определим все действия, которые потребуется выполнять. Посмотрите листинг 11.4 и файл car_class.php, чтобы разобраться во всем. Сначала подключаем конфигурационный файл для соединения с сервером MySQL, а затем определяем конструктор класса car. Работа конструктора состоит в установлении соединения с сервером MySQL, создавая экземпляр класса mysqli, определенного в одноименном расширении PHP. Свойство mysqli класса car будет содержать указатель на установленное соединение. Для отправки запросов к MySQL нам потребуется метод getcolor, который в качестве параметра принимает имя владельца автомобиля. Мы хотим определить цвет автомобиля, которым владеет этот человек. Запрос в базу отправляется методом query. Результат запроса будет представлять собой объект класса mysqliResult и храниться во временной таблице, на которую указывает переменная $result.

Листинг 11.4. Класс car в файле car_class.php <?php require_once ('config.php'); // Класс для работы с базой данных class car {

// Хранит идентификатор соединения с базой protected $mysqli; function __construct(){ $this->mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE); }


Глава 11. Запрос данных с сервера MySQL

115

function getcolor($name){ // Выбор данных об автомобиле $query = "SELECT color FROM cars where owner = '$name'"; // Выполнение запроса if ($result = $this->mysqli->query($query)){ // Выбор данных из ответа сервера MySQL $arr = $result->fetch_array(MYSQLI_ASSOC); // Удаляем результирующую выборку $result->close(); // Вывод ответа клиенту return $arr["color"]; } else return "данных нет"; } // Деструктор закрывает соединение с базой function __destruct() { $this->mysqli->close(); } } ?>

Метод fetch_array извлекает из выборки ассоциативный массив, но нам-то нужен один-единственный его элемент, поэтому мы просто извлекаем один элемент этого массива и отправляем его клиенту. Запрос к базе данных направляем на небольшую проверку разумности результатов, и, если получены подходящие данные, метод getcolor возвращает цвет автомобиля. В конце сценария пишем деструктор, который разорвет соединение с MySQL по окончании работы сценария. Ну, хорошо, класс мы создали. Напишем теперь PHP-сценарий, к которому будет обращаться клиентский JavaScript. Сценарий этот должен создавать объект класса car и запрашивать данные из базы (листинг 11.5 и файл на компакт-диске). Листинг 11.5. Сценарий requestMySQL.php <?php require_once('car_class.php'); // Запрет кэширования


Часть II. Создание AJAX-приложений

116

header('Expires: Wed, 01 Dec 1980 00:30:00 GMT'); // time in the past header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); // Читаем параметр запроса $name = $_GET['name']; if ($name != '') { // Создаем объект класса cars $avto = new car(); // Отправляем запрос в базу $color = $avto->getcolor($name); // Отправляем ответ клиенту if ($color!="данных нет"){ echo $color; } } else { echo 'Ошибка на сервере'; } ?>

Первое, что мы делаем в сценарии, — это подключаем сценарий класса car и запрещаем кэширование результатов в браузере клиента. Затем читаем передаваемый в запросе параметр: он должен содержать имя владельца автомобиля. Затем создаем объект класса car и отправляем запрос с именем владельца в базу данных. Полученные данные отправляем в клиентский сценарий, не забыв предусмотреть возможность появления ошибок. Наконец, надо рассмотреть работу сценария requestMySQL.js, который представлен в листинге 11.6. Листинг 11.6. Сценарий requestMySQL.js function http_zapros() { var serverAddress = "requestMySQL.php?name=" + encodeURIComponent(document.getElementById("сar").value); if (xmlHttp) {


Глава 11. Запрос данных с сервера MySQL

117

try { xmlHttp.open("GET", serverAddress, true); xmlHttp.onreadystatechange = obrabotka; xmlHttp.send(null); } catch(e) { alert("Невозможно :\n" + e.toString()); } } } function obrabotka () { if (xmlHttp.readyState == 4) { if (xmlHttp.status == 200) { try { var response = xmlHttp.responseText; myDivel = document.getElementById("myDiv"); if (response!="данных нет"){ myDivel.innerHTML = "<div style='width:50px;height:50px;background-color:" + response + " ' />"; } } catch(e) { alert("Ошибка при обработке данных сервера:\n" + e.toString()); } } else { alert(xmlHttp.statusText); } } }


Часть II. Создание AJAX-приложений

118

Рис. 11.1. Web-страница запроса данных с сервера MySQL

Посмотрите на рис. 11.1, где дан вид полученной страницы, и оцените, насколько информативно сообщение Firebug об асинхронном запросе. При взгляде на этот сценарий, читатель сразу понимает, что нового в нем очень мало, т. к. в нем снова отправляется асинхронный запрос, ответ сервера представлен в виде текста, что приводит к простой программе обработки полученных данных. Но как только речь заходит об обработке более объемных данных, сразу становится очевидно, что применение XML-формата данных в ответе сервера приводит к резкому усложнению программ обработки, и взоры программистов обращаются к формату JSON, что сделаем и мы на следующем примере.

Передача данных в формате JSON

Изменим немного предыдущий сценарий, запросив из той же базы данных всю информацию об автомобиле. Будем теперь получать данные, задав номер автомобиля. Данные должны отображаться в виде блока с текстом на цветном фоне. Web-страница будет теперь выглядеть, как в листинге 11.7. Листинг 11.7. Запрос данных JSON <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head>


Глава 11. Запрос данных с сервера MySQL

119

<title>AJAX</title> <meta http-equiv="Content-Language" content="ru"> <meta http-equiv="Content-Type" content="text/html; UTF-8"> <script type="text/javascript" src="requestobject.js"></script> <script type="text/javascript" src="generatejson.js"></script> </head> <body > Выберите номер автомобиля и узнайте его характеристики: <ul> <?php $avto = array(1, 2, 3, 4); for ($i=0; $i<4;$i++) { echo '<li><a href="javascript:void(0);" onclick="http_zapros(\''.$avto[$i].'\');" id="'.$avto[$i].'">'.$avto[$i].'</a></li>'; } ?> </ul> <div id="otvet" /> </body> </html>

Начнем разбор действий с серверного сценария, точнее, с класса, который представлен в листинге 11.8. Листинг 11.8. Сценарий adv_car_class.php: создание класса adv_car <?php require_once ('car_class.php'); class adv_car extends car{ // Вызываем явно конструктор базового класса function __construct($id){ parent::__construct(); $this->car_id=$id; $this->get_array($id); } function get_array ($iden){ $request = "SELECT * FROM cars where car_id = $iden"; $result = $this->mysqli->query($request); $massiv = $result->fetch_array(MYSQLI_ASSOC); foreach($massiv as $key=>$value){


Часть II. Создание AJAX-приложений

120 $this->$key = $value; } $result->close(); } } ?>

Сценарий adv_car_class.php создания класса демонстрирует нам, что класс adv_car расширяет класс car. Вот тут-то становится ясно, зачем свойство mysqli, указывающее на соединение с сервером, определялось как защищенное. Сделай мы его частным, и уже не удастся применить его в классенаследнике. Конструктор класса adv_car явно вызывает конструктор базового класса, а затем выполняет собственные задачи, вызывая метод get_array, запрашивающий для нас с сервера MySQL все необходимые данные. Затем в цикле foreach создается массив свойств генерируемого объекта. Итак, объект создан. Посмотрим теперь, где он используется (листинг 11.9). Листинг 11.9. Сценарий generatejson.php <?php require_once('adv_car_class.php'); // Запрет кэширования header('Expires: Wed, 01 Dec 1980 00:30:00 GMT'); // Время в прошлом header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); // Читаем параметр запроса $id = $_GET['id']; if ($id != '') { // Создаем объект класса car $avto = new adv_car($id); // Отправляем ответ клиенту echo json_encode($avto); } else { echo 'Ошибка на сервере'; } ?>


Глава 11. Запрос данных с сервера MySQL

121

Комментировать в нем, в общем-то, и нечего. Для понимания его работы достаточно вспомнить, что данные JSON формируются из PHP-объекта функцией json_encode. Теперь самое время посмотреть, как эти данные обрабатываются на клиентской стороне в сценарии generatejson.js (листинг 11.10). Листинг 11.10. Обработка на стороне клиента function http_zapros(el) { if (xmlHttp) { try { // Читаем текст элемента var params = "id=" + el; xmlHttp.open("GET", "generatejson.php?" + params, true); xmlHttp.onreadystatechange = requestControl; xmlHttp.send(null); } catch (e) { alert("Невозможно соединиться с сервером:\n" + e.toString()); } } } // Функция, вызываемая при изменении состояния запроса function requestControl() { // Если readyState равно 4, то мы готовы обрабатывать ответ сервера if (xmlHttp.readyState == 4) { // Продолжаем, только если статус HTTP равен "OK" if (xmlHttp.status == 200) { try { // Обрабатываем ответ сервера obrabotka();


Часть II. Создание AJAX-приложений

122 } catch(e) {

alert("Ошибка при обработке ответа сервера: " + e.toString()); } } else { // Показываем статус ответа сервера

alert("Проблема с получением данных от сервера:\n" + xmlHttp.statusText);

} } } //

Обработка ответа сервераfunction obrabotka()

{ // Ожидаем, что ответ пришел в формате JSON var xmlResponse = xmlHttp.responseText; var data

= eval("(" + xmlResponse

+ ")");

myDiv = document.getElementById("otvet"); info=""; info+="<div style='width:200px;height:200px;background-color:" + data.color + "'>"; info+="Владелец -" + data.owner;

info+="<br />Марка -" + data.make;

info+="<br />Модель -" + data.model; info+="</div>"; myDiv.innerHTML = info; }

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


Глава 11. Запрос данных с сервера MySQL

123

при более или менее значительном размере данных становится просто удручающим. К счастью, в настоящее время нет необходимости проходить все эти испытания вновь и вновь. Мировым сообществом программистов создано значительное количество библиотек, позволяющих ускорить Web-разработку. Мало кто из разработчиков придерживается до сих пор того аскетичного подхода, который исключает возможность применения одной или нескольких библиотек. Поэтому следующие главы посвящены применению таких библиотек, и именно с их помощью мы покажем, как создавать красивые и удобные Web-приложения на основе технологии AJAX.

Рис. 11.2. Страница с открытой консолью Firebug


Часть III

Библиотеки для работы с AJAX


Глава 12

Обзор библиотек для создания AJAX-приложений До этого момента мы обсуждали применение AJAX, исходя из того, что на клиентской стороне работает сценарий JavaScript, а на серверной — PHP. Но в настоящее время на серверной стороне могут использоваться также Java, .NET, Ruby on Rails, Python, ColdFusion, Perl и др. Что касается клиентского сценария, то, полагаем, читатель уже убедился, что создание AJAX-приложений на чистом JavaScript — это разработка очень длинного кода с долгой отладкой его в различных браузерах. К счастью, в настоящее время в распоряжении разработчика есть большой набор библиотек, позволяющих ускорить разработку Web-приложений. Часто такие библиотеки называют фреймворками (англ. Framework), используя термин, обозначающий более широкое и весьма расплывчатое понятие. Мы все-таки будем использовать английский термин. Framework может включать вспомогательные программы, библиотеки кода, язык сценариев и другое программное обеспечение, облегчающее разработку и объединение разных компонентов большого программного проекта. В отличие от библиотеки, которая объединяет в себе набор близкой функциональности, Framework содержит в себе большое число разных по тематике библиотек. Все же термин "Framework" тяжеловат для русского слуха, и в дальнейшем мы будем чаще использовать термин "библиотека". Основная работа при разработке AJAX-приложений ложится на клиентскую сторону, причем в силу различной поддержки браузеров стандарта DOM, объем программирования может оказаться весьма значительным. Для ускорения процесса разработки существуют два подхода. Серверные библиотеки, позволяющие генерировать код JavaScript из интерфейса, созданного на языке серверного программирования. Можно привести два примера: • Google Web Toolkit (GWT) позволяет создавать интерфейс на языке программирования Java, а GWT компилирует исходный код в тщательно оптимизированный JavaScript;


128

Часть III. Библиотеки для работы с AJAX

• XAJAX — библиотека PHP, позволяющая писать требуемые функции на PHP, а затем функции библиотеки генерируют JavaScript-код, выполняя созданные программистом функции.

В данной книге мы не будем подробно рассматривать этот класс библиотек. JavaScript-библиотеки, создающие кроссбраузерный код JavaScript, выполняющий всю рутинную работу по созданию, отправке и обработке AJAX-запросов и многое другое.

В этой и последующих главах мы рассмотрим наиболее интересные библиотеки второй из перечисленных групп библиотек. Эти библиотеки в той или иной степени расширяют стандартную объектную модель JavaScript, позволяя оперировать понятиями класса, наследования и полиморфизма. В некоторых из них реализованы общедоступные и частные свойства и методы классов. С точки зрения компонентов, обычно в библиотеке есть все или часть перечисленных ниже средств ускорения разработки Web-приложений: поддержка AJAX-запросов и обработка результатов; запрос и обработка данных в формате JSON; средства автодополнения форм путем запроса данных с помощью AJAX; усовершенствованная поддержка событий; средства генерации HTML; создание иерархических деревьев; генерация тем (themes, skins);

создание функциональности, реализующей эффект нажатия кнопки Назад в браузере; простые визуальные эффекты, например, изменение размеров, прозрачности или видимости фрагмента страницы; анимация и связанные с ней более сложные визуальные эффекты; drag & drop;

виджеты для ускорения ввода данных и проверка форм; создание редактируемых таблиц данных (grids) и др. В каждой оконной системе существует свой набор элементов, с интерфейсом для работы с ними. Элементы интерфейса — это примитивы графического интерфейса пользователя, имеющие стандартный внешний вид и выполняющие стандартные действия. Такие элементы известны также под именем


Глава 12. Обзор библиотек для создания AJAX-приложений

129

виджеты (от англ. widget) и элементы управления. Обычно виджетами называют элементы следующих типов: кнопка (button); список (list box); выпадающий список (combo box); флажок (check box); радиокнопка или переключатель (radio button); поле редактирования (textbox, edit field); значок (icon); панель инструментов (toolbar); панель (строка) состояния (status bar); всплывающая подсказка (tooltip, hint); полоса прокрутки (scrollbar); вкладка (tab); элемент для отображения табличных данных (grid view); меню (menu); окно (window); панель (panel); диалоговое окно (dialog box); модальное окно (modal window). Все популярные JavaScript-библиотеки сопровождаются подробной документацией и демонстрационными примерами. Обычно такие библиотеки распространяются под двойной лицензией с возможностью как свободного, так и коммерческого использования. Перечислим некоторые библиотеки, завоевавшие популярность на момент написания данной книги.

Dojo (доджо) — свободная модульная JavaScript-библиотека. Разработка библиотеки была начата Алексом Расселом в 2004 году. Библиотека находится под двойной лицензией: BSD License и Academic Free License. Dojo используется в Zend Framework, начиная с версии 1.6.0. Сайт: http://dojotoolkit.org/. ExtJS — JavaScript Framework для разработки Web-приложений и пользовательских интерфейсов, изначально задуманный как расширенная версия Yahoo! UI Library. Использует адаптеры для доступа к библиотекам YUI, jQuery или Prototype/script.aculo.us. Распространяется по условиям


130

Часть III. Библиотеки для работы с AJAX

трех лицензий: Commercial License, Open Source License и OEM/Reseller License. Сайт: http://extjs.com.

jQuery — JavaScript Framework, фокусирующийся на взаимодействии JavaScript и HTML. Сайт: http://Jquery.com. MooTools — это свободный JavaScript Framework для разработки кроссбраузерных Web-приложений и Web-сервисов. Сайт: http://Mootools.net. The Yahoo! UI Library (YUI) — библиотека с открытым кодом. Сайт: http://developer.yahoo.com/yui/. Prototype — JavaScript Framework. Сайт: prototypejs.org. Script.aculo.us — JavaScript-библиотека для разработки пользовательского интерфейса Web-приложений, построенная на Framework Prototype. Обычно используется программистами вместе с Ruby on Rails, однако также доступна в виде отдельной библиотеки и присутствует в составе некоторых каркасов для разработки сайта. Сайт: http://script.aculo.us.


Глава 13

Библиотека Prototype Prototype — это библиотека, созданная на JavaScript и предназначенная для упрощения генерации динамических Web-приложений. Prototype была создана Сэмом Стефенсоном в 2005 г. в качестве проекта с открытым кодом. Свежую версию Prototype можно скачать с сайта http://www.prototypejs.org, ее версию 1.6 читатель найдет на диске, приложенном к данной книге. Если вы посмотрите на оглавление документации по Prototype, то увидите, что эта библиотека состоит из двух неравных частей: утилиты и классы, что сразу дает представление о назначении Prototype: утилиты служат для упрощения обычных операций JavaScript, а классы расширяют стандартную объектную модель языка, позволяя создавать объекты, располагающие большим числом методов. Расширение это выполнено таким образом, что программирование на JavaScript с помощью Prototype становится похожим на работу с объектами по классической схеме: можно оперировать понятием класса, что невозможно в классическом JavaScript, класса-наследника и т. п. В связи с этим Prototype вызвала ряд критических замечаний, суть которых сводится к тому, что превращение JavaScript в слабое подобие C++ убивает мощь и гибкость, заложенные в JavaScript исходно. Но каковы бы ни были достоинства и недостатки Prototype, следует признать, что она стала первой JavaScript-библиотекой, получившей действительно широкое распространение среди разработчиков, и послужила основой для развития других библиотек, например, script.aculo.us, речь о которой пойдет в следующей главе. Prototype — это библиотека, содержащая, в основном, низкоуровневые средства для Webразработки. То есть она не включает в себя наборы визуальных эффектов и готовые решения для создания пользовательских интерфейсов, но обеспечивает более удобную работу с HTML-элементами документа, снабжает методами обработки списков элементов, управления стилевыми классами документа, ускоряет работу с формами и AJAX-запросами и т. п.


Часть III. Библиотеки для работы с AJAX

132

Раз речь идет о программе на JavaScript, то и подключать Prototype надо как обычные сценарии JavaScript. Копируем Prototype в каталог scripts и включаем в HTML-документ: <script src="scripts/prototype.js" type="text/javascript"></script>

После этого мы можем вызывать функции, определенные в Prototype, в тексте нашего сценария.

Полезные методы в Prototype Рассмотрим методы Prototype, собранные в группу "Utility methods" и упрощающие программирование на JavaScript (табл. 13.1). Таблица 13.1. Полезные методы в Prototype

Название

Возвращаемое значение

$(id | element)

HTMLElement

$$(cssRule...)

HTMLElement

$A(iterable)

actualArray

$F(element)

value

Описание Если аргументом служит строка (значение идентификатора), то возвращает элемент с указанным значением идентификатора. Может принимать несколько аргументов Принимает список CSS-селекторов, возвращает массив элементов, соответствующих селекторам, в том порядке, в каком они указаны в документе Принимает любую пронумерованную коллекцию и возвращает массив. Является псевдонимом метода Array.from Возвращает значение поля ввода формы. Является удобным псевдонимом метода

Form.Element.getValue $H([obj])

EnumerableHash

$R(start, end[, exclusive = false])

ObjectRange

$w(String)

Array

Try.these (Function...)

firstOKResult

Метод получения хэша. Возвращает новый объект со свойствами объектов Hash и Enumerable Создает новый объект ObjectRange Разбивает строку на массив, используя пробел в качестве разделителя Принимает в качестве параметра список функций и возвращает результат выполнения первой, не вызвавшей ошибку


Глава 13. Библиотека Prototype

Метод

$()

это

133

краткий синоним известного метода Все просто — посмотрите листинг 13.1.

document.getElementById().

Листинг 13.1. Метод $() <html> <head> <script src="prototype.js"></script> <script> function show_element(){ fragment = $("firstDiv"); alert(fragment.innerHTML); } </script> </head> <body onclick="show_element();"><div id="firstDiv">Текст</div></body> </html>

Для применения метода $$() надо знать, как именно обозначаются параметры этого метода. Приведем несколько коротких примеров: $$('div') — указываем имя тега, метод возвращает массив всех элементов, созданных тегом div; $$('#contents') — указываем значение идентификатора, и, несмотря на то, что в документе может быть только один элемент с таким идентификатором, метод $$() все равно возвращает массив, состоящий, правда, из одного элемента; $$('.faux') — выбираем все элементы, относящиеся к стилевому классу faux;

$$('li.faux')

выбираем все элементы списка li, относящиеся к сти-

левому классу faux. Метод f() — первый из методов Prototype, на которых начинаешь чувствовать, что жизнь становится легче и приятнее. Посмотрите пример, приведенный в листинге 13.2, и вы поймете, что это действительно так. Здесь метод f() возвращает строку, содержащую значение поля ввода формы, длину полученной строки мы читаем из ее свойства length.


Часть III. Библиотеки для работы с AJAX

134

Листинг 13.2. Метод f() в Prototype <html> <head> <title>Prototype</title> <script src="scripts/prototype.js" language="JavaScript" type="text/javascript" ></script> <script language="Javascript"> function f() { alert($F('zip').length); } </script> </head> <body

onLoad="f()">

<form> <input type="text" name="zip" id="zip" value="slovo" /> </form> </body> </html>

Часть остальных методов, приведенных в табл. 13.1, служит для того, чтобы преобразовать HTML-элемент или список элементов в объекты классов, определенных в Prototype, позволяя таким образом применять к ним ряд удобных методов. При определении некоторого клиентского метода мы можем с помощью метода Try.these указать, что надо выполнить первую из функций, перечисленных в качестве параметра этого метода, и не вызывающую ошибки. Таким образом, метод Try.these позволяет, например, при создании объекта запроса не заботиться о том, какой браузер используется на клиентском компьютере. getTransport: function() { return Try.these( function() { return new XMLHttpRequest() }, function() { return new ActiveXObject('Msxml2.XMLHTTP') }, function() { return new ActiveXObject('Microsoft.XMLHTTP') } ) || false; }


Глава 13. Библиотека Prototype

135

Класс Element

Класс Element предоставляет пользователю ряд методов, которые можно сгруппировать по их ролям. Управление стилевыми свойствами элемента. Эти методы могут читать и модифицировать отдельные свойства CSS, приписывать элемент к какомулибо стилевому классу, анализировать стилевые таблицы документа и извлекать элементы на основании их принадлежности к тому или иному стилевому классу. Методы, создающие, модифицирующие и удаляющие HTML-элементы. Эти методы оперируют непосредственно с понятиями DOM, включая такие, как родительские элементы и элементы-наследники, предыдущие и последующие элементы (previous and following siblings) и т. д. Методы, получившие в официальной документации титул расширяющих DOM. Эти методы превращают объекты JavaScript, в котором есть понятие объекта и его прототипа, но нет классов, в объекты, которыми можно манипулировать в соответствии с классовой объектной моделью. Следующая группа методов облегчает работу с событиями в JavaScript, делая ее более элегантной, чем, впрочем, могут похвастаться и многие другие методы Prototype. И, наконец, набор приятных мелочей, например, позволяющих прокручивать окно браузера так, как окажется удобным. В табл. 13.2 описаны некоторые методы объекта Element, они представлены не полностью, но в объеме, позволяющем понять функциональность и назначение обсуждаемого класса. Таблица 13.2. Методы объекта Element

Метод Управление стилями CSS toggle(elem1 [, elem2 [, elem3 [...]]]) show(elem1 [, elem2 [, elem3 [...]]]) hide(elem1 [, elem2 [, elem3 [...]]]) addClassName(element, className)

Описание Изменяет видимость данных объектов Показывает элементы, устанавливая их свойство display в значение '' (т. е. пустая строка) Прячет элементы, устанавливая их стилевое свойство display в значение 'none' Добавляет элементу стилевой класс


Часть III. Библиотеки для работы с AJAX

136

Таблица 13.2 (окончание)

Метод

Описание

setStyle(element, styles) -> HTMLElement

Устанавливает стилевое свойство для указанного элемента (см. листинг 13.8) Удаляет указание стилевого класса элемента

removeClassName(element, className) hasClassName(element, className) getHeight(element) toggleClassName(element, className) -> HTMLElement getDimensions(element) -> {height: Number, width: Number} hasClassName(element, className) -> Boolean DOM-

функции

remove(element) replace(element[, html]) -> HTMLElement update(element[, newContent])

Расширение DOM

Возвращает true, если элемент относится к указанному классу Возвращает свойство offsetHeight элемента Меняет CSS-класс элемента и возвращает сам элемент Вычисляет ширину и высоту элемента (width и height) и возвращает их в виде литерального объекта Проверяет, относится ли элемент к указанному классу CSS Удаляет элемент из документа Заменяет элемент на содержимое аргумента html и возвращает удаленный элемент Заменяет содержимое элемента на значение аргумента newContent и возвращает элемент

extend(element)

Дает возможность применять все методы класса Element к указанному HTML-элементу

addMethods([methods])

Принимает список методов и делает их методами того объекта, к которому применяется сам addMethods

cleanWhitespace(element)

Управление событиями

Убирает все пробельные текстовые узлы из списка детей данного элемента

observe(element, eventName, handler[, useCapture = false]) -> HTMLElement

Привязывает обработчик события к элементу и возвращает элемент

stopObserving(element, eventName, handler) -> HTMLElement

Удаляет обработчик события и возвращает элемент

Управление окном браузера scrollTo(element) -> HTMLElement

Прокручивает окно так, что элемент оказывается на верху области видимости окна


Глава 13. Библиотека Prototype

137

Приведем пример использования метода hide() (листинг 13.3). Листинг 13.3. Метод hide() <html> <head> <title>Prototype</title> <script src="scripts/prototype.js" language="JavaScript" type="text/javascript"></script> <script language="Javascript"> function hide_el() { $('smth2').hide(); } </script> </head> <body

onclick="hide_el()">

<div id="smth">Блок 1</div> <div id="smth2"> <b>Щелкни по этому элементу, и он исчезнет.</b>

Блок 2</div> <div id="smth3">Блок 3</div> </body> </html>

Класс Array Метод $$(), как мы уже упоминали, возвращает массив DOM-элементов в том порядке, в каком они встречаются в документе. А раз это массив, то и обращаться с ним можно как с объектом Array из Prototype. В табл. 13.3 описаны основные методы этого объекта. Эти методы представляют собой расширение набора функций JavaScript обработки массивов: сортировка, удаление повторяющихся элементов, преобразование массива в другие форматы данных и т. п. Таблица 13.3. Методы объекта Array

Метод clone compact each

Описание Возвращает копию массива, исходный массив не изменяется Удаляет из массива пустые элементы Перебирает все элементы массива


Часть III. Библиотеки для работы с AJAX

138

Таблица 13.3 (окончание)

Метод first last size toJSON

Описание Выбирает первый элемент массива Выбирает последний элемент массива Возвращает размер массива Возвращает элементы массива в формате toJSON

Вот теперь можно на небольшом примере продемонстрировать краткость кода, который получается при использовании утилит и методов Prototype. Начнем с того, что создадим стилевые классы в файле styles.css (листинг 13.4), указав различные классы для заголовков и простых ячеек таблицы.

Листинг 13.4. Файл styles.css .Table1 { border: DarkGreen 1px solid; background-color: LightGreen; width : 300px; } .TableHead1 { font-family: Verdana, Arial; font-weight: bold; font-size: 10pt; } .TableContent1 { font-family: Verdana, Arial; font-size: 10pt; } .Table2 { border: DarkBlue 1px solid; background-color: LightBlue; width : 300px; } .TableHead2 { font-family: Verdana, Arial;


Глава 13. Библиотека Prototype

139

font-weight: bold; font-size: 10pt; } .TableContent2 { font-family: Verdana, Arial; font-size: 10pt; }

Создадим теперь страницу, подключив в нее стилевой файл и библиотеку Prototype (листинг 13.5). На странице у нас будут три таблицы и три кнопки. Щелчок по кнопке вызывает соответствующую функцию. Функция setStyle1 начинает свои действия с того, что методом $$() выбирает таблицы со стилевым классом Table1, создавая список объектов. Затем методом first выбираем первый объект из полученного массива и методом toggleClassName меняем его стилевой класс. Листинг 13.5. Утилиты и методы Prototype <html> <head><title>Prototype</title> <script src="scripts/prototype.js" language="JavaScript" type="text/javascript" > </script> <link href="styles.css" type="text/css" rel="stylesheet"/> <script language="Javascript"> function setStyle1() { var spisok =

$$('table.Table1');

spisok.first().toggleClassName('Table2'); } function setStyle2() { var spisok = $$('th'); spisok.last().remove(); } function setStyle3() { var spisok = $$('th'); for(i=0; i<spisok.length;i++){ spisok[i].toggleClassName('Table2');}


Часть III. Библиотеки для работы с AJAX

140 } } </script> </head> <body> <table class="Table1">

<tr><th>Заголовок первой таблицы</th></tr> <tr><td>Ячейка первой таблицы</td></tr> <tr><td>Ячейка первой таблицы</td></tr> </table> <table class="Table2"> <tr><th>Заголовок второй таблицы</th></tr> <tr><td>Ячейка второй таблицы</td></tr> <tr><td>Ячейка второй таблицы</td></tr> </table> <table class="Table1"> <tr><th>Заголовок третьей таблицы</th></tr> <tr><td>Ячейка третьей таблицы</td></tr> <tr><td>Ячейка третьей таблицы</td></tr> </table> <br /> <input type="button" value="Изменить CSS-класс первой таблицы" onclick="setStyle1();" /> <br /> <input type="button" value="Удалить последний из заголовков таблиц" onclick="setStyle2();" /> <br /> <input type="button" value="Изменить стили заголовков" onclick="setStyle3();" /> </body> </html>

Функция setStyle2 из списка заголовков таблиц выбирает последний и удаляет его из дерева документа. Функция setStyle3 отбирает все заголовки и, перебирая их в цикле, меняет их стили. Как легко заметить, работа с методами библиотеки Prototype легка и проста по синтаксису. Напоследок еще один пример, демонстрирующий применение метода A(), создающего из коллекции элементов массив, к которому можно применить метод each() (листинг 13.6). Сам же метод each(), перебирая элементы массива в цикле, применяет к каждому элементу анонимную функцию, которая тут же и определена.


Глава 13. Библиотека Prototype

141

Листинг 13.6. Применение метода each() <html> <head> <title>Класс Array</title> <script src="scripts/prototype.js"></script> <link href="styles.css" type="text/css" rel="stylesheet"/> <script> function showOptions(){ var someNodeList = $('lstEmployees').getElementsByTagName('option'); var nodes = $A(someNodeList); nodes.each(function(node){ alert(node.nodeName + ': ' + node.innerHTML); }; } </script> </head> <body> <select id="lstEmployees" size="3" > <option value="5">Алексей</option> <option value="8">Анна</option> <option value="1">Марк</option> </select><br /> <input type="button" value="перебрать опции списка" onclick="showOptions();" > </body> </html>

AJAX в Prototype Для создания AJAX-запросов с помощью Prototype можно использовать объекты классов, наследующих базовому классу, определенному в Prototype — это класс Ajax.Base. Приведем фрагмент его кода, чтобы читателю стало ясно, какие опции запросов установлены с самого начала: Ajax.Base = function() {}; Ajax.Base.prototype = { setOptions: function(options) { this.options = { method: asynchronous:

'post', true,


Часть III. Библиотеки для работы с AJAX

142 contentType:

'application/x-www-form-urlencoded',

encoding:

'UTF-8',

parameters:

''

} } }

Итак, начиная с этого момента, никакого произвола с выбором кодировки: в Prototype, как и во всех остальных рассматриваемых в данной книге библиотеках, можно использовать только UTF-8. Но реальная работа по отправке запроса и обработке его результатов выполняется не объектом класса Ajax.Base, а объектами иных классов. Посмотрим, как это происходит.

Класс Ajax.Request Класс

наследует классу Ajax.Base. Объект класса обычно используется в том случае, когда сервер возвращает данные в формате XML, что, естественно, требует дальнейшей обработки. Синтаксис тут прост: Ajax.Request Ajax.Request

new Ajax.Request(url[, options])

Конструктор Ajax.Request создает один экземпляр объекта и принимает два параметра: url — адрес запрашиваемого ресурса, выбранный URL, подвергается проверке браузера на безопасность. Во многих случаях браузер не будет совершать запрос, если URL не принадлежит тому же хосту, что и текущая страница; options — опции запроса, частично это те, что унаследованы от базового класса, кроме того, в опциях можно задать callback-функции, которые вызываются при изменении состояния запроса. Одни и те же типы callback-функций используются во всех классах, реализующих AJAX-запросы и наследующих классу Ajax.Base. Приведем их в табл. 13.4. Таблица 13.4. Callback-функции AJAX-запросов Callback-функция Описание onComplete onException

Выполняется в конце жизненного цикла запроса Выполняется при возникновении ошибки объекта XMLHttpRequest


Глава 13. Библиотека Prototype

143

Таблица 13.4 (окончание)

функция

Callback-

onFailure onSuccess onXYZ

Описание

Функция, которая будет вызвана, если AJAX-запрос завершится с ошибкой. Выполняется перед onComplete Функция, которая будет вызвана, если AJAX-запрос завершится успешно. Выполняется перед onComplete XYZ — статус HTTP-ответа сервера. Предотвращает выполнение onSuccess/onFailure. Выполняется перед

onComplete

Рассмотрим пример использования объекта Ajax.Request: var req=new Ajax.Request( 'myData.xml', { method: 'get', parameters: { name:'Анна', likes:'шоколад' }, onLoaded: function(){ alert('Загружено!'); }, onComplete: function(){alert('Готово!' + req.transport.responseText);} } );

Первый параметр объекта — адрес запроса, запрос будет выполнен, если заданный здесь адрес указывает на тот же хост, откуда была получена текущая страница. Второй параметр вызова Ajax.Request представляет собой анонимный объект. Это значит, что мы передаем объект, который имеет свойство method, содержащее строку 'get', свойство parameters, содержащее строку параметров HTTP-запроса и свойства onLoaded и onComplete, содержащее ссылки на соответствующие функции. Поскольку в Prototype по умолчанию используется метод POST, то метод GET приходится указывать явно. Обработчики onLoaded и onComplete представляют собой callback-функции, которые выполняются, когда свойство readyState объекта xmlHttpRequest изменяется. Переменная req.transport.responseText в функции onComplete представляет собой свойство responseText объекта xmlHttpRequest, точнее, объекта, обертывающего объект xmlHttpRequest. Объект этот называется Ajax.Response и заслуживает отдельного рассмотрения.

Класс Ajax.Response Объект этого класса передается в качестве первого аргумента (если таковые вообще передаются) всем callback-функциям AJAX-запросов. Как только что было упомянуто, класс Ajax.Response обертывает объект XMLHttpRequest, обеспечивая работу с ним некоторыми дополнительными возможностями.


Часть III. Библиотеки для работы с AJAX

144

Свойства класса Ajax.Response описаны в табл. 13.5. Таблица 13.5. Свойства объекта Ajax.Response

Свойство

Описание

status

Код HTTP-ответа сервера

statusText

Текстовое сообщение о статусе HTTP-ответа сервера

readyState

Текущее состояние AJAX-запроса. Значения такие же, как у объекта xmlHttpRequest

responseText

Текст ответа

responseXML

XML-ответ сервера, если значение заголовка Content-type — application/xml, иначе — null

responseJSON

Ответ в формате JSON, если значение заголовка Contenttype — application/json, иначе — null

request

Сам объект запроса (экземпляр класса Ajax.Request или класса Ajax.Updater)

transport

Сам объект xmlHttpRequest

Класс Ajax.Updater

Как сказано в документации, класс Ajax.Updater является конкретизацией класса Ajax.Request, и все, что справедливо для Ajax.Request, справедливо и для Ajax.Updater. Синтаксис создания объекта этого класса таков: new Ajax.Updater(container, url[, options])

Здесь container — тот элемент, внутрь которого будет помещен ответ сервера. Ajax.Updater помещает текстовый ответ сервера в выбранную ветвь DOM и используется, когда запрошенный серверный ресурс возвращает фрагмент HTML, который вы хотите напрямую вставить в определенный элемент на странице. Для этого класса используются две специфические опции: insertion — функция, которая будет вызвана, чтобы вставить полученный текст внутрь элемента. Она будет вызвана с двумя аргументами: элементом, который надо обновить, и текстом ответа; evalScript — определяет, будут ли выполнены блоки <script>, полученные в составе ответа сервера. Пример применения Ajax.Updater: var myAjax = new Ajax.Updater( {success: 'placeholder'}, url, {method:'get', parameters:' ', onFailure: reportError} );


Глава 13. Библиотека Prototype

145

Элемент со значением id="placeholder" должен обновляться только в случае удачного запроса. Для этого используем свойство success, которое будет использовано, если все сработает успешно. Опция onFailure указывает на функцию, которая будет обрабатывать ошибочные состояния.

Класс Ajax.PeriodicalUpdater

Объект этого класса создается периодически и использует объект Ajax.Updater для обновления элементов на странице или для любых других действий, которые может выполнять Ajax.Updater. Здесь нам понадобятся еще две специфические опции: frequency — период между обновлениями в секундах. По умолчанию 2 секунды; decay — скорость уменьшения частоты запросов. Дело в том, что при инициализации нового запроса сценарий проверяет, отличался ли последний ответ сервера от предыдущего. Если ответы оказались одинаковыми, то новый запрос отправляется через более длительное время, равное значению опции frequency, умноженной на значение decay. То есть если decay=2, то при получении одинаковых ответов каждый новый запрос будет отправляться через промежуток времени в 2 раза длиннее, чем предыдущий. П РИМЕЧАНИЕ На просторах Рунета (http://habrahabr.ru/blogs/Ajax/29156/) нашлось такое важное уточнение: для того чтобы периодический запрос работал корректно в Internet Explorer, следует использовать метод POST: new Ajax.PeriodicalUpdater('items', '/items', { method: 'post', frequency: 3, decay: 2 });

Использование AJAX-запросов в Prototype

Рассмотрим, наконец, полноценные работающие примеры применения AJAX-запросов с помощью объектов библиотеки Prototype. Запросим у клиента снова его номер телефона (листинг 13.7). Листинг 13.7. Применение объекта класса Ajax.Updater <html> <head> <title>Ajax.Updater </title> <script src="scripts/prototype.js" language="JavaScript"


146

Часть III. Библиотеки для работы с AJAX

type="text/javascript"> </script> <script type="text/javascript" language="JavaScript"> function check_function() { if($F('tel').length == 7) { var url = 'check.php'; var params = 'tel=' + $F('tel'); var request = new Ajax.Updater( {success: 'Result'}, url, {method: 'get',parameters: params, onFailure: reportError} ); } } function reportError(request) { $('Result').innerHTML = "Ошибка при соединении с сервером"; } </script> </head> <body> <form> Укажите номер телефона (7 цифр): <br /> <input type="text" name="tel" id="tel" onkeyup="check_function();" /> <div id="Result"></div> </form> </body> </html>

Подготовим форму с полем, идентифицируемым значением tel, для ответа сервера создадим блок с идентификатором Result. Каждый раз, когда пользователь пишет что-либо в поле формы, а значит, и щелкает по клавиатуре, вызывается функция check_function, которая для начала проверяет, не равна ли семи длина поля tel, если же равна, то создается объект запроса класса Ajax.Updater, отправляющий запрос к сценарию check.php, методом GET. Серверный сценарий check.php ясен и краток: <?php

echo "Вы указали ".$_GET['tel']; ?>

В случае неудачного запроса будем вызывать функцию reportError, принимающую объект запроса в качестве параметра. При удаче ответ сервера записывается внутрь блока, идентификатор которого указан в опции success. В следующем примере AJAX-запрос возвращает данные в формате JSON (листинг 13.8).


Глава 13. Библиотека Prototype

147

Листинг 13.8. Запрос данных в формате JSON <html> <head> <title>AJAX Приложение </title> <style type="text/css"> .mydiv { background-color:#cccccc; width:100; height:50; } </style> <script src="scripts/prototype.js" language="JavaScript" type="text/javascript"></script> <script type="text/javascript" language="JavaScript"> function ask() { var url = "json.php"; var Ajax = new Ajax.Request( url, {method: "get", onSuccess: function(transport){ var data = eval("(" + transport.responseText + ")"); $("update_me").setStyle({backgroundColor:data.colorcode}); } } ); } </script> </head> <body> <h2 onclick="ask();">Щелкни здесь!</h2> <div id="update_me" class="mydiv"></div> </body> </html>

Попросим пользователя щелкнуть по тексту внутри заголовка h2, для которого определена обработка события click. Щелчок приведет к созданию AJAX-запроса, а результаты запроса будут обработаны и отображены в блоке с идентификатором update_me.


148

Часть III. Библиотеки для работы с AJAX

Функция ask, вызываемая при щелчке, создает объект Ajax.Request. При конфигурировании этого объекта укажем, что запрос методом GET отправляется к серверному сценарию json.php. Мы определяем, что при успешном завершении запроса должна выполняться анонимная функция (описанная в опции onSuccess), которая в качестве параметра получает объект запроса. В начале работы этой функции вызывается eval, обрабатывающая текстовый ответ сервера, хранящийся в свойстве transport.responseText. Обратите внимание на синтаксис параметра, передаваемый этой функции. Как мы уже упоминали в главе 4, для того чтобы функция eval корректно обработала данные, надо поместить их в дополнительные скобки. Функция вернет объект JavaScript, который мы и запишем в переменную data. Затем метод $() находит элемент с идентификатором update_me. К возвращаемому этим методом элементу применяем метод setStyle, который дает новое значение цвету фона (свойство backgroundColor). Само присваиваемое значение берется из свойства colorcode объекта data. Для того чтобы понять, откуда взялось такое свойство, надо посмотреть на серверный сценарий json.php, который очень похож на тот, что рассматривался в главе 4. Приведем его здесь (листинг 13.9). Листинг 13.9. Сценарий json.php <?php class car{ public $make = "Ford"; public $model = "Focus"; public $colorcode = "#ff0000"; public $color = "красный"; public $date = array("year"=>2008, "month"=>"may"); } $test = new car; echo json_encode($test); ?>

Как видите, свойство colorcode вполне подходит для того, чтобы его значение стало цветом квадратика на нашей странице. Подводя итоги этой главы, можно сказать, что Prototype позволяет облегчить жизнь Web-программиста, хотя и не содержит поражающие воображение средства создания пользовательских интерфейсов. Несмотря на некоторые недостатки официальной документации, использовать эту библиотеку несложно.


Глава 14

Библиотека script.aculo.us Script.aculo.us представляет собой библиотеку JavaScript с открытым кодом для ускорения разработки пользовательских интерфейсов для Web-сайтов. Она включает в себя механизм реализации визуальных эффектов, библиотеку средств drag & drop, ряд элементов управления (controls), основанных на AJAX, в том числе автодополнение, слайдеры (sliders), редактирование текста по месту (in-place editing) и ряд других средств.

Вы можете скачать script.aculo.us со страницы http://script.aculo.us/ downloads.

Поскольку script.aculo.us использует библиотеку Prototype, последняя понадобится вам также. И конечно, script.aculo.us использует кодировку UTF-8. Весь комплект библиотек, который потребуется нам в этой главе, состоит из следующих файлов: prototype.js; scriptaculous.js; builder.js; effects.js; dragdrop.js; slider.js; controls.js.

Поместите эти файлы, например, в каталог scripts и подключайте к вашей странице, добавив к ней следующие теги: <script src="scripts/prototype.js" type="text/javascript"> </script> <script src="scripts/scriptaculous.js" type="text/javascript"> </script>

Сценарий scriptaculous.js автоматически подгружает все остальные файлы этой библиотеки, необходимые для создания визуальных эффектов,


Часть III. Библиотеки для работы с AJAX

150

слайдеров и т. п. Если вы не хотите, чтобы подгружались все файлы, то можете ограничить список загружаемых файлов, указав их через запятую следующим образом: <script src="scriptaculous.js?load=effects,dragdrop" type="text/javascript"></script>

Так можно указать файлы библиотек builder, effects, dragdrop, controls и slider. Обратите внимание, что для обеспечения их правильной работы сценарии нужно подключать в указанном порядке.

Эффекты Библиотека визуальных эффектов (effects.js) содержит средства анимации для Web-страниц. Анимация выполняется строго в соответствии с указанными временными параметрами, не зависящими от скорости отображения эффекта в браузере клиента. Эффекты работают в Firefox, Internet Explorer, Safari, iPhone и других браузерах. Библиотека содержит средства для создания базовых эффектов (Core Effects), комбинационных эффектов (Combination Effects), представляющих собой комбинации базовых эффектов и эффекты очередей (Queues Effects), последние представляют собой средства для построения очередности выполнения эффектов, создавая анимацию. Имеется

семь

базовых

эффектов,

составляющих

основу

библиотеки

script.aculo.us: Effect.Opacity

— изменение прозрачности;

Effect.Scale

— изменение размеров;

Effect.Morph

— изменение свойств CSS для элемента;

Effect.Move

— движение элемента;

Effect.Highlight Effect.Parallel Effect.Tween

— подсветка элемента;

— объединение нескольких эффектов;

— установка параметров.

Базовый синтаксис создания эффекта таков: new Effect.EffectName(element, required-params, [options]);

Здесь element может быть либо значением идентификатора элемента, либо элементом DOM. Параметры required-params зависят от конкретного эффекта и не являются обязательными. Опции options используются для настройки параметров конкретного эффекта.


Глава 14. Библиотека script.aculo.us

151

Например, задание параметров для создания эффекта прозрачности может выглядеть так: new Effect.Opacity('my_element', { duration: 2.0, transition: Effect.Transitions.linear, from: 1.0, to: 0.5 });

Общие параметры базовых эффектов представлены в табл. 14.1 Таблица 14.1. Параметры базовых эффектов script.aculo.us

Опция duration fps from to delay

Описание Длительность эффекта в секундах. По умолчанию 1.0 Сколько кадров в секунду будет показано. По умолчанию 25. Не может превышать 100 Задает начальную точку перехода. Возможные значения: от 0.0 до 1.0. По умолчанию 0.0 Задает конечную точку перехода. Возможные значения: от 0.0 до 1.0. По умолчанию 1.0 Задает паузу в секундах перед началом эффекта. По умолчанию 0.0

transition

sync queue

Задает функцию, которая меняет временной режим анимации. Поддерживаются такие режимы анимации, как линейный, синусоидальный, пульсация, обратный порядок и пр. Устанавливает, будет отображение анимации выполняться автоматически или вручную с помощью специальных функций Задает опции порядка выполнения эффекта

Кроме того, дополнительные параметры могут задавать callback-функции, вызываемые в ходе работы эффекта. Этим функциям надо передавать ссылку на объект, к которому применяют эффект. Укажем только существующие типы функций такого рода: beforeStart, beforeUpdate, afterUpdate, afterFinish. Экземпляры объекта Effect располагают рядом полезных свойств и методов, таких как: effect.element — элемент, к которому применяют эффект; effect.options — опции выполняемого эффекта;


Часть III. Библиотеки для работы с AJAX

152 effect.currentFrame

— номер последнего показанного кадра;

— время в миллисекундах, когда начался эффект и когда он завершится; effect.effects[] — массив эффектов при их параллельном выполнении; effect.cancel() — остановить эффект; effect.inspect() — получить отладочную информацию. Посмотрите на примере, приведенном в листинге 14.1, как просто создать эффект изменения видимости элемента с помощью вызова методов объекта effect.startOn, effect.finishOn

Effect.

Листинг 14.1. Эффект Effect.toggle в script.aculo.us <!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>Эффект toggle</title> <script src="scripts/prototype.js" type="text/javascript"> </script> <script src="scripts/scriptaculous.js?load=effects" type="text/javascript"> </script> </head> <body> <a href="javascript:void Effect.toggle('d1', 'blind');">

Показать/Спрятать (blind)</a>

<div id="d1" style="background-color:green; height:50px; width: 100px">

Первый раздел.</div> <br />

<a href="#" onclick="Effect.toggle('d2','slide',{ duration: 1.0 }); return false;">Свернуть/Развернуть (slide)</a> <div id="d2" style="display:none;"> <div style="background-color:#ff8080;width:100px;border:2px solid red;padding:10px;">

Второй раздел.</div></div> <br /> <a href="#" onclick="Effect.toggle('d3','appear'); return false;">Показать/Спрятать (appear)</a>


Глава 14. Библиотека script.aculo.us

153

<div id="d3" style="display:none;"> <div style="background-color:#ff8080;width:100px;border:2px solid red;padding:10px;">

Третий раздел.</div></div> </body> </html>

Легко понять, что для создания эффекта достаточно вызвать метод toggle объекта Effect, указать ему идентификатор элемента, к которому надо применить эффект, и тип эффекта, задав значение опции blind, slide или appear. Первый прямоугольник при щелчке по нему сворачивается или разворачивается при каждом щелчке по очереди. Второй прямоугольник разворачивается или сворачивается наподобие свитка, а последний появляется или исчезает, постепенно набирая или теряя яркость. Поэкспериментируйте в этом примере с временными параметрами, заданными в опции duration.

Перетаскивание и сортировка (Draggable & Sortable) Продемонстрируем теперь сразу несколько эффектов (изменение прозрачности, подсвечивание элемента) в совокупности с сортировкой и перетаскиванием элементов мышью. Effect.Opacity меняет прозрачность элемента. Например, для того чтобы элемент за полсекунды стал прозрачнее на 30%, надо задать параметры эффекта так: new Effect.Opacity('id_of_element', { from: 1.0, to: 0.7, duration: 0.5 });

Метод Effect.Highlight меняет фоновый цвет элемента на короткое время, создавая эффект вспышки. Он используется для привлечения внимания к элементам, которые претерпевают какие-либо изменения. Ему можно задать следующие опции: startcolor — цвет фона на время вспышки. По умолчанию #ffff99 (светло-желтый); endcolor — цвет последнего кадра вспышки. По умолчанию #ffffff (белый); restorecolor — цвет фона после вспышки. По умолчанию — цвет фона до вспышки.


Часть III. Библиотеки для работы с AJAX

154

Теперь разберемся с перетаскиванием и сортировкой. Для того чтобы применить эффект перетаскивания к элементу, надо создать новый экземпляр класса Draggable, используя следующий синтаксис: new Draggable('id_of_element', [options]);

Основные опции класса Draggable даны в табл. 14.2. Таблица 14.2. Опции класса Draggable

Опция

Описание

zindex

Z-индекс перетаскиваемого элемента

constraint

Если задано horizontal или vertical, то перетаскивание осуществляется только по горизонтали или по вертикали

starteffect

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

endeffect

Определяет эффект, применяемый при окончании перетаскивания. По умолчанию Effect.Opacity

revert

Задает действие по окончании перетаскивания. Если указать значение true, то элемент после окончания перетаскивания вернется на исходное место. По умолчанию false

Расширить функциональность Draggable можно, применив методы класса Класс Sortable дает способ организовать сортировку нескольких перетаскиваемых элементов, вложенных внутрь одного контейнерного элемента. Синтаксис создания сортируемых элементов таков:

Sortable.

Sortable.create('id_of_container',[options]);

Метод Sortable.create можно применять только к контейнерному элементу, содержащему блочные элементы, за исключением TABLE, THEAD, TBODY и TR. Таково техническое ограничение в современных браузерах. Для получения детальной информации и, возможно, обновлений, снимающих такие ограничения, обращайтесь к документации по этой библиотеке в Интернете. Некоторые опции метода Sortable.create приведены в табл. 14.3. Таблица 14.3. Опции метода Sortable.create

Опция

Описание

tag

Тег, который будет сортироваться. По умолчанию li

only

Позволяет ограничить сортируемые элементы теми, что относятся к заданному классу CSS. По умолчанию не указывается, т. е. ограничений нет


Глава 14. Библиотека script.aculo.us

155

Таблица 14.3 (окончание)

Опция

Описание

overlap

vertical или horizontal. По умолчанию vertical

constraint

Ограничение движения элементов. По умолчанию vertical

format

Формат задания имени идентификатора. По умолчанию: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/

Callback-функции, которые следующих типов: onChange onUpdate

могут быть связаны с этим методом, могут быть

— вызывается при перетаскивании;

— вызывается при окончании перетаскивания.

Посмотрим, наконец, как все это работает в листинге 14.2. Для начала создадим функцию, которая будет вызываться в начале каждого перемещения элемента. Ссылка на нее будет храниться в переменной myStartEffect. Эта функция сначала определяет текущую прозрачность элемента, потом в течение 0,2 сек меняет ее и подсвечивает перетаскиваемый элемент светложелтым фоном. Листинг 14.2. Drag & Drop с применением script.aculo.us <!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></title> <script type="text/javascript" src="scripts/prototype.js"></script> <script src="scripts/scriptaculous.js?load=effects,dragdrop" type="text/javascript"></script> <script type="text/javascript"> var myStartEffect = function(element) { element._opacity = element.getOpacity(element); new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); new Effect.Highlight(element, {}); } </script>


Часть III. Библиотеки для работы с AJAX

156 <style type="text/css">

h1 {font-size: 1.2em; text-align:center;} li { margin: 5px auto; padding: 2px; width: 200px; text-align:center; list-style-type:none; border: 2px solid #779; background-color: #dde } div { margin: 5px auto; padding: 2px; width: 300px; text-align:center; border: 2px solid #797; background-color: #ded } </style> </head> <body> <h1>Сортировка без задержки</h1> <ul id="sort1"> <li id="s1_1">Первый</li> <li id="s1_2">Второй</li> <li id="s1_3">Третий</li> </ul> <h1>Сортировка с паузой 500 мс</h1> <ul id="sort2"> <li id="s2_1">Первый</li> <li id="s2_2">Второй</li> <li id="s2_3">Третий</li> </ul> <h1>Перетаскивание без задержки</h1> <div id="drag1">

Тащим этот элемент </div> <h1>Перетаскивание с задержкой 1000 мс</h1>


Глава 14. Библиотека script.aculo.us

157

<div id="drag2">

Тащим этот элемент </div> <script type="text/javascript"> Sortable.create('sort1', {starteffect: myStartEffect}); Sortable.create('sort2', {starteffect:myStartEffect, delay:500}); new Draggable('drag1', {starteffect:myStartEffect}); new Draggable('drag2', {starteffect:myStartEffect, delay:1000}); </script> </body> </html>

В теле документа есть два контейнера с идентификаторами sort1 и sort2. Элементы, вложенные в эти контейнеры, можно будет перетаскивать, но после окончания перетаскивания они вернутся на свое место, причем установится исходный порядок их расположения на странице. Элементы с идентификаторами drag1 и drag2 можно перетаскивать по экрану и бросать в любом месте, обратно они не вернутся. Задание параметра delay приводит к тому, что надо немного подождать после выбора элемента для перетаскивания, пока не начнется выполнение эффекта. Эксперименты с работающим примером дадут вам всю необходимую информацию для практического использования этих эффектов.

AJAX в script.aculo.us

Автодополнение

Класс Ajax.Autocompleter позволяет создать автодополнение, т. е. выводит подсказку в виде выпадающего списка, как только пользователь начинает набирать текст в поле ввода. Текст подсказки формируется как дополнение к символам, введенным пользователем, образуя словарные слова. Данные для подсказки запрашиваются с сервера AJAX-запросом. Класс Ajax.Autocompleter расширяет класс Ajax.Request библиотеки Prototype, наследуя его свойства. Для создания объекта автодополнения надо задать идентификатор поля id_of_text_field, откуда будут считываться символы, вводимые пользователем, идентификатор id_of_div_to_populate того блока, где будут отображаться данные с сервера, адрес запрашиваемого ресурса на сервере url и, необязательно, дополнительные опции. Синтаксис создания автодополнения таков: new Ajax.Autocompleter(id_of_text_field, id_of_div_to_populate, url, options);


Часть III. Библиотеки для работы с AJAX

158

Опции создания объекта даны в табл. 14.4. Таблица 14.4. Опции автодополнения в Ajax.Autocomplete

Опция

Значение Описание по умолчанию

paramName

Имя элемента

frequency

0.4

minChars

1

indicator

null

Имя параметра, передаваемого на сервер. Это имя задается в элементе input, в который пользователь заносит текст для автодополнения Промежуток в секундах между запросами AJAX Минимальное число символов, которые надо ввести для того, чтобы сгенерировать запрос AJAX Id элемента, который отображается в процессе запроса AJAX. По окончании запроса элемент будет скрыт

Рассмотрим работу автодополнения на примере (листинг 14.3). Листинг 14.3. Автодополнение в script.aculo.us <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Ajax автодополнение</title> <script src="scripts/prototype.js" type="text/javascript"></script> <script src="scripts/scriptaculous.js?load=effects,controls" type="text/javascript"></script> </head> <body style="padding:30px;"> <style type="text/css"> div.autocomplete { position:absolute; width:200px; background-color:white; border:1px solid #888; margin:0px; padding:0px;


Глава 14. Библиотека script.aculo.us

159

} div.autocomplete ul { list-style-type:none; margin:0px; padding:0px; } div.autocomplete ul li.selected { background-color: #ffb;} div.autocomplete ul li { list-style-type:none; display:block; margin:0; padding:2px; cursor:pointer; background:none; } </style> <input type="text" id="city" name="city" style="width:200px" value="" /> <span id="indicator" style="height:11px; display:none;"> <img src="1.gif" width="43" height="11" align="absmiddle" alt="Загрузка..." /></span> <div id="variants" class="autocomplete"></div> <script type="text/javascript"> new Ajax.Autocompleter("city", "variants", "cities.php", {paramName: "city", minChars: 1, indicator: 'indicator'}); </script> </div> </body></html>

Страница начинается с поля ввода с идентификатором city. Затем указываем рисунок, который послужит индикатором загрузки данных с сервера, в качестве такого индикатора обычно используется анимированный GIF, в нашем случае это файл 1.gif. Индикатор помещаем внутри элемента span с идентификатором indicator. Блок div с идентификатором variants отводим под отображение вариантов подсказки, пришедших с сервера. Вернемся теперь по коду чуть повыше и посмотрим на таблицу стилей. Задание стилей CSS для блока div и списка ul является важным фрагментом программы. Сервер должен прислать неупорядоченный список подсказок,


Часть III. Библиотеки для работы с AJAX

160

которые отобразятся с применением указанных стилей. Для появления подсказки пользователь должен набрать хотя бы одну русскую букву (это определяется параметром minChars и набором слов в конкретно нашем серверном сценарии). Слова подсказки отобразятся в блоке с идентификатором variants в виде выпадающего списка в стиле li.selected. Имя стилевого класса autocomplete не является зарезервированным словом, но задать стили надо, иначе подсказка будет иметь вид простого неупорядоченного списка, а не выпадающего списка, как выглядит выпадающий список, созданный элементом select. Сценарий cities.php, выполняющий работу на сервере, дан в листинге 14.4.

Листинг 14.4. Сценарий cities.php, формирующий автодополнение <?php $cities = array('Астрахань', 'Барнаул', 'Брянск', 'Волгоград', 'Гатчина', 'Дармштадт', 'Екатеринбург', 'Железноводск', 'Загреб', 'Иваново', 'Курск', 'Москва', 'Луга', 'Новгород', 'Омск', 'Пермь', 'Симферополь', 'Санкт-Петербург', 'Тула', 'Уфа', 'Харьков', 'Ярославль'); $return = array(); $str = mb_strtolower($_POST['city']); function str_srch($item, $key) { global $return,$str; if(mb_strtolower(mb_substr($item,0,mb_strlen($str))) == $str) $return[] = $item; } array_walk($cities,'str_srch'); if (count($return)>0) echo '<ul><li>'. implode('</li><li>', $return). '</li></ul>'; else echo '<span></span>'; ?>

Сценарий cities.php содержит список городов в массиве $cities. Затем сценарий читает присланную клиентом букву из элемента $_POST['city'] и выбирает все подходящие элементы списка. Обратите внимание на то, что русскоязычные строки в кодировке UTF-8 надо сравнивать, применяя функции, имена которых начинаются на mb (multi-byte). Работа автодополнения в браузере будет выглядеть так, как показано на рис. 14.1.


Глава 14. Библиотека script.aculo.us

161

Рис. 14.1. Автодополнение с помощью Ajax.Autocompleter

Класс Ajax.InPlaceEditor

Класс Ajax.InPlaceEditor позволяет редактировать текст и сохранять его на сервере. Текст может загружаться в область редактирования из серверного файла или создаваться пользователем с нуля. Синтаксис создания объекта: new Ajax.InPlaceEditor(element, url, {options});

Конструктор класса принимает три параметра: HTML-элемент element, в котором выполняется редактирование, адрес ресурса url, куда отправляется отредактированный текст, при этом сервер может проверять введенные данные и обрабатывать полученный от пользователя текст. Третьим параметром служит набор опций. Серверный сценарий получает данные, отправленные методом POST. Опции конструктора приведены в табл. 14.5. Таблица 14.5. Опции объекта Ajax.InPlaceEditor

Название

По умолчанию

Описание

okControl

button

Тип кнопки подтверждения ввода отредактированного текста (button, link, false)

cancelControl

link

Тип кнопки отмены при редактировании (button, link, false)

cancelText

cancel

Текст в поле отмены редактирования

savingText

Saving...

Текст, отображаемый при загрузке текста на сервер


Часть III. Библиотеки для работы с AJAX

162

Таблица 14.5 (окончание)

Название

По умолчанию

Описание

clickToEditText

Click to edit

Текст, показываемый, пока указатель мыши находится на редактируемом тексте

rows

1

Количество строк в поле ввода

cols

none

Количество символов в строке в поле ввода

highlightcolor

Ajax.InPlaceEditor.def aultHighlightColor

Цвет фона элемента

callback

function(form) {Form.serialize(form)}

Функция, выполняемая перед отправкой текста на сервер

clickToEditText

Click to edit

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

onComplete

function(transport, element) {new Effect. Highlight(element, {startcolor: this.options.highlight color});}

Код, выполняемый при удачном обновлении текста на сервере

onFailure

function(transport) { alert("Error communicating with the server: " + transport.responseText. stripTags());}

Код, выполняемый при неудаче

savingClassName

inplaceeditor-saving

Класс CSS, добавляемый к элементу при отображении сообщения о сохранении текста

Создадим страницу с возможностью редактирования in-place, используя класс Ajax.InPlaceEditor. Поле для редактирования у нас будет содержать 15 строк по 40 символов в строке. Отредактированные данные будут передаваться в сценарий demoajaxreturn.php. Пока идет запись на сервер, будет отображаться уже знакомый по предыдущим примерам анимированный GIF загрузки. По умолчанию в Ajax.InPlaceEditor данные передаются в параметре value, но мы зададим свое имя для переменной — myparam. Посмотрите листинг 14.5, чтобы понять, как создается такое поле редактирования.


Глава 14. Библиотека script.aculo.us

163

Листинг 14.5. Ajax.InPlaceEditor <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Ajax.InPlaceEditor </title> <script src="scripts/prototype.js" type="text/javascript"></script> <script src="scripts/scriptaculous.js?load=effects,controls" type="text/javascript"></script> <style type="text/css" media="screen"> .inplaceeditor-saving { background: url('1.gif') bottom right norepeat; } </style> </head> <body style="padding:30px;"> <div class="inplaceeditor-saving"></div> <p id="editme">Щелкай здесь, чтобы начать редактирование! </p> <script type="text/javascript"> new Ajax.InPlaceEditor('editme', 'demoajaxreturn.php',{rows:15, cols:40, cancelControl:"button", savingText: "Сохраняем...", callback: function(form, value) { return 'myparam=' + encodeURIComponent(value)}}); </script> </body> </html>

Индикатор загрузки текста на сервер помещается в блоке с идентификатором inplaceeditor-saving. Само поле редактирования помечено идентификатором editme. Затем создаем объект Ajax.InPlaceEditor, приводя для его конфигурирования указанные только что опции. Кроме того, мы определяем callback-функцию, которая сработает при завершении редактирования и отправит на сервер переменную myparam с текстом, отредактированным пользователем. Серверный сценарий в нашем примере предельно прост и предназначен исключительно для проверки того, что данные действительно пришли на сервер в параметре myparam: <?php

echo "Вы отправили на сервер: <br />".$_POST['myparam'];

?>

Если вы тестируете этот пример на своем домашнем компьютере, то вряд ли заметите процесс загрузки. Тогда добавьте в начало серверного сценария функцию sleep(2), которая сделает паузу длиной две секунды перед отправкой текста клиенту, а вы сможете увидеть работу анимированного рисунка заставки.


Часть IV

Библиотека ExtJS


Глава 15

Структура и идеология библиотеки Вокруг уже шуршали пески и шумели водопады милой моему сердцу Внутренней Монголии. В. Пелевин

ExtJS — это библиотека, написанная на JavaScript и расширяющая возможности этого языка. В 2006 г. Джек Слокум (Jack Slocum) начал разрабатывать расширения для библиотеки Yahoo! User Interface (YUI). К концу года эти расширения превратились в популярную библиотеку, которая была названа ExtJS. ExtJS использует двойную модель лицензирования. Она доступна как под открытой LGPL, так и под коммерческой лицензией. В настоящее время можно посоветовать очень мало мест, где можно узнать про ExtJS. Но вот главные сайты: http://extjs.com/ — основной сайт библиотеки ExtJS, с которого можно скачать свежую версию библиотеки, документацию и примеры; http://techwork.ru/extjs-book/, http://extjs.ru/ — русскоязычные ресурсы, посвященные ExtJS. На компакт-диске, приложенном к книге, читатель найдет версию ExtJS-2.2. В каталоге extjs-2.2 находятся следующие файлы:

ext-all.js — сжатая версия всех компонентов библиотеки; ext-all-debug.js — все компоненты библиотеки без комментариев (код доступен для просмотра и отладки); ext-core.js — сжатая версия ядра библиотеки; ext-core-debug.js — основные компоненты без комментариев (код доступен для просмотра и отладки). В том же каталоге extjs-2.2 лежит англоязычная документация, вольное переложение которой и составляет теоретическую часть данной главы. Кроме того, там много прекрасных примеров, часть которых мы тоже разберем здесь.


168

Часть IV. Библиотека ExtJS

Сразу хочется отметить, что русскоязычная терминология, необходимая в этой главе, далека от состояния стабильности. Большинство разработчиков пользуется выражениями, являющимися кальками английских терминов. Авторы настоящей книги далеки от желания перевести любой термин на русский язык, не считаясь с тем, что в следующей же статье или книге читателю попадется другой перевод, только отдаленно напоминающий исходное английское слово. На компакт-диске находится полная документация на английском языке extdocs-2.2.air, работать с которой можно после установки ее в систему с помощью программы AdobeAirInstaller. После установки окно с документацией откроется в виде, представленном на рис. 15.1.

Рис. 15.1. Документация по ExtJS

Библиотека ExtJS представляет собой наиболее последовательное расширение языка JavaScript, формирующее объектную модель, привычную для программистов, которые оперируют классами, наследованием и инкапсуляцией. Ранее в главе 3 мы рассмотрели эти аспекты работы с JavaScript, и освоивший ее читатель достаточно подготовлен к встрече с ExtJS. С другой стороны, в ExtJS широко используется и обработка событий, составляющая гвоздь почти любой программы на JavaScript.


Глава 15. Структура и идеология библиотеки

169

Соглашения об именах

При создании библиотеки ExtJS были выработаны следующие соглашения об именах объектов: имена классов начинаются с прописных букв (GridPanel, Observable и т. д.); имена событий состоят из строчных букв (click, dblclick и т. д.); константы полностью указываются в верхнем регистре (DAY, HOUR и т. д.); все прочие идентификаторы имеют смешанный регистр (ext, doSomething, myValue и т. д.).

Конфигурирование ExtJS и первый пример применения

В начале каждой страницы, применяющей ExtJS, надо подключать файлы этой библиотеки. Это выглядит так, как показано в листинге 15.1. Листинг 15.1. Подключение библиотеки ExtJS <html> <head> <title>Введение в Ext</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script> <script type="text/javascript" src="ext-2.2/ext-all-debug.js"></script> <script type="text/javascript" src="ExtStart.js"></script> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"> <link rel="stylesheet" type="text/css" href="ExtStart.css"> </head> <body> <h1>Введение в Ext</h1> <div id="content"> <p>Это стартовая страница.</p> </div> </body> </html>


Часть IV. Библиотека ExtJS

170

П РИМЕЧАНИЕ Обратите внимание, что, как и в других библиотеках JavaScript, в ExtJS используется исключительно кодировка UTF-8.

В файле ExtStart.js у нас будет следующий код (листинг 15.2). Листинг 15.2. Сценарий ExtStart.js Ext.onReady(function() { alert("Поздравляю! Ваша Ext сконфигурирована правильно!"); });

Открыв в браузере эту первую HTML-страницу, вы должны увидеть то, что представлено на рис. 15.2.

Рис. 15.2. Вид стартовой страницы в браузере

Итак, наш первый сценарий на ExtJS начинается с обращения к объекту Ext. Объект этот является экземпляром синглетона Ext. Синглетон Ext обеспечивает нам доступ к функциональности этого класса, задавая при этом общее пространство имен. Метод Ext.onReady вызывается на том этапе загрузки документа в браузер, когда объектная модель документа уже построена и готова к работе, но, возможно, не все картинки и другие фрагменты страницы уже получены браузером. Если модальное окно с поздравлением не появилось на вашем экране, проверяйте, правильно ли указаны пути до библиотек.


Глава 15. Структура и идеология библиотеки

171

Далее в этой главе будет много теории. При первом чтении ее можно пропустить, но после разбора примеров следует возвращаться к ней и перечитывать различные фрагменты, что приведет к более глубокому пониманию логики ExtJS.

Объект Ext.Element

Вся работа в JavaScript начинается с выбора элемента HTML-страницы. Мы запрашиваем его, вызывая привычный метод getElementById: var myDiv = document.getElementById('myDiv');

Возвращаемый объект является узлом DOM и предоставляет не слишком много удобных методов для работы с ним. Приходится писать еще длинный код, позволяющий выполнять нужные действия. Объект Ext.Element библиотеки ExtJS содержит метод, позволяющий получать доступ к элементам и выполнять действия над ними. Метод Ext.get (сокращенное обозначение метода Ext.Element.get) возвращает объект с указанным идентификатором. Вот как выглядит вызов этого метода: Ext.onReady(function() { var myDiv = Ext.get('myDiv'); });

Отметим некоторые примечательные черты объекта Element: объект Element обертывает многие привычные нам методы и свойства DOM, создавая удобный кроссбраузерный интерфейс; метод Element.get создает внутренний кэш, так что многократные обращения к одному и тому же объекту выполняются очень быстро; многие действия, которые обычно приходится совершать над элементом DOM, встроены в кроссбраузерные методы объекта Element, например, удаление или добавление стилевых классов, позиционирование, перетаскивание, удаление или добавление обработчиков событий и др. Все это позволяет сократить объем создаваемого кода. Вот, например, что можно сделать с элементом, запрошенным методом get, с помощью различных методов ExtJS: myDiv.highlight() — фон элемента станет желтым, а потом поблекнет; myDiv.addClass('red') — добавляем к элементу пользовательский класс CSS (например, определенный в присоединенном к документу стилевом файле); myDiv.center() — помещаем элемент в центр видимой области; myDiv.setOpacity(.25) — делаем элемент частично прозрачным.


172

Часть IV. Библиотека ExtJS

Firebug — запаситесь выжигателем жучков FireBug — это расширение для браузера Firefox, являющееся отладчиком JavaScript. Firebug показывает в консоли функцию, вызвавшую ошибку, параметры запросов к серверу, дерево DOM документа и много другой отладочной информации. Если у вас до сих пор не установлен этот удобный инструмент, то перейдите на сайт http://getfirebug.com/, скачайте и настройте у себя Firebug. Посмотрите приложение 2, чтобы узнать о некоторых возможностях Firebug.

Контекст Мы будем применять термин "контекст" для обозначения области действия или области видимости свойства (переменной) или метода (функции). Термин этот соответствует английскому "scope". Для демонстрации контекста выполните следующий код, делать это удобнее всего с помощью Firebug. Перейдите в Firebug на вкладку Script и напечатайте слово "window" в строке "New watch expression..." в правой части Firebug, затем нажмите клавишу <Enter>. Создадим два объекта (o1 и o2). Их свойства и методы имеют одинаковые имена, но разные значения. var o1 = {testvar:22, fun:function() { alert('o1: ' + this.testvar); }}; var o2 = {testvar:33, fun:function() { alert('o2: ' + this.testvar); }};

Запускать код на выполнение в Firebug можно щелчком по кнопке Run. Вызвать метод fun(), просто обратившись к нему, нельзя, он не является методом глобального объекта, и попытка приведет к сообщению об ошибке в любом из указанных здесь способов: fun(); window.fun(); this.fun();

Еще бы! Объект window не имеет метода fun. Следующий же код вполне корректен, ведь теперь метод вызывается в контексте того объекта, где он действительно объявлен: o1.fun(); o2.fun();

А сейчас самое интересное. Пусть наши объекты имеют методы fun1 и fun2. var o1 = {testvar:22, fun1:function() { alert('o1: ' + this.testvar); }}; var o2 = {testvar:33, fun2:function() { alert('o2: ' + this.testvar); }};


Глава 15. Структура и идеология библиотеки

173

Вызовем метод fun1 объекта o1, но так, чтобы он выполнялся для того значения свойства testvar, которое определено в объекте o2: o1.fun1.call(o2);

// Будет выведено o1: 33

Выполнив указанные действия, вы должны увидеть в окне браузера картину, представленную на рис. 15.3. Вот это и называется выполнить функцию o1.fun1 в контексте объекта o2. Таким образом, от того, в каком контексте выполняется функция, будет зависеть значение свойства объекта.

Рис. 15.3. Запуск методов с помощью Firebug

Задание контекста в ExtJS Контекст можно рассматривать как особый параметр каждой функции. Javaзадает его при каждом вызове. Функция может быть определена как элемент некоторого объекта. В этом случае функция запускается в контексте этого объекта. Если функция не является элементом объекта, то контекстом

Script


Часть IV. Библиотека ExtJS

174

служит контекст глобального объекта (в браузере глобальным объектом является объект window). Выполним с помощью Firebug следующий код на JavaScript (листинг 15.3).

Листинг 15.3. Вложенные функции var testvar = 'window property'; var o3 = { testvar:'3', testvar2:'3**', fun:function(){ alert('o3: '+this.testvar);

// пишет '3'

var inner = function(){ alert('o3-inner: '+this.testvar);

// выводит 'window property'

alert('o3-inner: '+this.testvar2); // выводит 'undefined' }; inner(); } }; o3.fun();

Обратите внимание на то, что функция inner выполняется не в том контексте, что функция fun. Библиотека ExtJS дает нам возможность задать по своему усмотрению контекст, в котором будут выполняться функции. Переменные, созданные внутри блоков (таких как, например, цикл for или оператор if), продолжают существовать до конца функции. Например, запустим такую функцию: function foo() { var i=123; for (var i=0; i<3; i++) {

}

alert(i); } foo();

Будет выведено число 3. Это происходит потому, что переменная i определена внутри функции, но не локально внутри блока for. Поэтому i, которая используется внутри цикла, — это та же переменная i, что была определена вне цикла.


Глава 15. Структура и идеология библиотеки

175

Адаптеры и пространство имен

Одна из задач, которую ставили себе разработчики библиотеки ExtJS, была возможность сосуществования с другими JavaScript-библиотеками. Для этого были добавлены методы, помогающие разработчику создавать, наследовать и сопровождать код различных классов. ExtJS использует специальные адаптеры для доступа к функциям библиотек YUI, jQuery, Protoype, Scriptaculous.

В дистрибутиве ExtJS включены следующие адаптеры: ext-base — встроенный адаптер самой библиотеки для выполнения низкоуровневых операций; jQuery — библиотека jQuery версии 1.2.3 и ext-jquery-adapter, сам адаптер; Prototype — библиотека Prototype, адаптер к ней, Scriptaculous. Базовые визуальные эффекты реализуются с помощью расширения Effects для Scriptaculous;

YUI — адаптер, использующий компоненты Yahoo! User Interface.

Для применения встроенного адаптера в код страницы надо добавить строку <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script>

Для того чтобы создаваемый программистом код не конфликтовал с другими библиотеками, надо использовать пространство имен, которое в ExtJS является объектом, содержащим список определений других объектов. В ExtJS существует метод Ext.namespace, который позволяет определить все пространства имен за один вызов. Метод Ext.namespace создает пространство имен, которое будет использовано для задания области действия переменных и классов. Например: Ext.namespace('Company', 'Company.data'); Company.Widget = function() { ... } Company.data.CustomStore = function(config) { ... }

Механизм наследования в ExtJS

Класс Ext включает в себя метод extend, который позволяет реализовать механизм наследования в библиотеке ExtJS. Он дает возможность менять или расширять функциональность любого класса, не внося изменение непосредственно в код класса, что обычно и трактуется как создание производного класса, иначе говоря, реализует механизм наследования.


Часть IV. Библиотека ExtJS

176

Для того чтобы создать новый класс, наследующий существующему классу, надо сначала объявить конструктор нового класса, а затем вызвать метод extend, чтобы определить свойства нового класса, которые будут общими для всех экземпляров этого нового класса. JavaScript не имеет механизма автоматического вызова конструктора базового класса, так что приходится вызывать его явно (листинг 15.4). При этом первый аргумент всегда должен быть this, чтобы конструктор выполнялся в контексте вызывающей его функции. Листинг 15.4. Создание класса-наследника в ExtJS MyNewClass = function(arg1, arg2, etc) { // Явный вызов конструктора базового класса MyNewClass.superclass.constructor.call(this, arg1, arg2, etc); }; Ext.extend(MyNewClass, SomeBaseClass, { theDocument: Ext.get(document), myNewFn1: function() { // Необходимые действия }, myNewFn2: function() { // Еще какие-то нужные действия } });

Вызов метода базового класса

Если в классе-наследнике был перегружен некоторый метод someFunction, а потом захотелось вызвать метод someFunction базового класса, то надо сделать так: MyClass = Ext.extend(Ext.SomeClass, { someFunction : function(arg1, arg2){ // Какой-то фрагмент кода функции. // Вызов базового класса

MyClass.superclass.someFunction.call(this, arg1, arg2); // Еще один фрагмент кода } );

Для корректного задания контекста не забывайте указывать this в качестве первого параметра функции call.


Глава 15. Структура и идеология библиотеки

177

Обработка событий в ExtJS Событие —

это некоторое сообщение, генерируемое программой, которую называют источником события. Событие адресовано другой программе, которую называют по-английски "event listener", что можно перевести как "слушатель события". Этот дословный перевод имеет, по крайней мере, то достоинство, что из него становится понятна логика действий программы. Но хочется все-таки говорить по-русски, поэтому будем называть эту программу обработчиком. Хм... С русским как-то не очень получается. Ладно. События генерируются в результате реакции на изменение в источнике события. Последний не зависит от обработчиков и генерирует событие даже в том случае, когда никто не готов его обрабатывать. Обработчик же готов предпринять некоторые действия в том случае, если его оповестят о событии. В ExtJS есть два типа событий: события DOM и события JavaScript или программные события.

События DOM Браузеры применяют механизм обработки событий при отображении XHTML-страниц и создают события, когда пользователь выполняет какиелибо действия над элементами DOM. Привычный способ обработки выглядит так: <div id="mydiv" onclick="alert('Щелкнули!')">Щелкни здесь!</div>

Объект Ext.Element позволяет объединить элемент DOM вместе с его событиями с помощью метода on, и обработка того же самого, что и в предыдущем примере, события выглядит так: Ext.get('mydiv').on('click', function() {alert('Щелкнули!');});

Можно сказать, что события DOM передаются из объектной модели обработчику при помощи объекта Ext.Element.

События JavaScript Элементы DOM — не единственные источники событий. Вполне естественно распространить логику генерации и обработки событий на любой объект JavaScript. Многие объекты ExtJS могут извещать обработчиков о наступлении события.


Часть IV. Библиотека ExtJS

178

Если у вас есть объект какого-либо класса из библиотеки ExtJS, например панель, и надо предпринять какие-то действия при изменении размера панели, то можно задать обработчик события таким образом: // Создаем панель var myPanel = new Ext.Panel({...}); // Задаем обработчик изменения размера myPanel.on('resize', function(panel, w, h) { alert('теперь размеры панели ' + w + 'x' + h); });

Теперь любые изменения размера панели приведут к вызову функции обработки события. Абстрактный класс Ext.util.Observable предоставляет интерфейс для работы с событиями. Для того чтобы объект был возможным источником события, он должен наследовать классу Observable. Объект Panel, а также другие объекты, например, Grid, Form или Tree, являются наследниками класса Observable, так что они становятся источниками событий уже с момента своего создания. События, вызываемые созданным вами объектом, вызываются именно классом-предком созданного объекта. Одно и то же событие можно привязать к нескольким элементам сразу. В следующем примере событие вызывается при щелчке по любому параграфу документа: Ext.onReady(function() { Ext.select('p').on('click', function() { alert("You clicked a paragraph"); }); });

При этом используется анонимная функция-обработчик события. Зададим теперь обработчик с помощью именованной функции, которую впоследствии можно будет снова вызвать, указав то же самое имя: Ext.onReady(function() { var paragraphClicked = function() { alert("You clicked a paragraph"); } Ext.select('p').on('click', paragraphClicked); });

В функцию-обработчик события можно передать сам объект события, точнее, его кроссбраузерную реализацию в ExtJS — объект Event. Передается


Глава 15. Структура и идеология библиотеки

179

он в качестве параметра, а в обработчике можно прочитать характеристики события, выбрав соответствующие свойства объекта Event. Например, узел DOM, на котором произошло событие, можно определить так: Ext.onReady(function() { var paragraphClicked = function(e) { Ext.get(e.target).highlight(); } Ext.select('p').on('click', paragraphClicked); });

Свойство target возвращает DOM-узел, поэтому сначала мы должны получить соответствующий объект Element и уже на нем производить действия.

Пользовательские события Весьма часто требуется добавить пользовательское событие, которое не встроено в Framework ExtJS. Рассмотрим, например, два класса: Employee (Сотрудники) и OrgChart (Структура фирмы). Хочется устроить так, чтобы можно было простым перетаскиванием элементов менять принадлежность сотрудника к тому или иному подразделению, меняя при этом и должность этого сотрудника. В следующем примере мы создадим и такие обработчики событий, которые будут рассылать письма, оповещающие сотрудников о новых назначениях. Вот как это делается (листинг 15.5).

Листинг 15.5. Пользовательские события OrgChart = Ext.extend(Ext.Panel, { initComponent:function() { // Вызываем компонент родительского класса OrgChart.superclass.initComponent.apply(this, arguments); // Добавляем пользовательские события this.addEvents('assigned', 'dismissed'); } ,assign:function(employee, position) { // Делаем все, что необходимо, для назначения сотрудника

// на должность.

// Вызываем событие назначения.


Часть IV. Библиотека ExtJS

180

this.fireEvent('assigned', this, employee, position); } ,dismiss:function(empoyee, position) { // Делаем все для снятия сотрудника с должности. // Вызываем событие увольнения. this.fireEvent('dismissed', this, employee, position); } });

В функции initComponent мы сообщаем классу Observable, что собираемся генерировать новые события, так что ему пора подготовить все необходимое для этого. П РИМЕЧАНИЕ

Если же мы используем объект Panel, то будет задействована цепочка наследования Observable → Component → BoxComponent → Container → Panel.

Теперь в функциях назначения на должность assign и снятия с должности dismiss мы вызываем наши события после выполнения всех необходимых действий по назначению или снятию, которые выполнялись с заданными значениями параметров empoyee и position. Когда мы вызываем функцию fireEvent, Observable смотрит, есть ли обработчики для данного события, и вызывает их с аргументами, которые мы передали при вызове функции fireEvent. Если же обработчиков нет, то никакие действия не происходят. Подытожим: событие — это сообщение, посылаемое источником события обработчику для того, чтобы оповестить о наступлении события; источник события — это объект, вызывающий событие; обработчик события — это функция, вызываемая при генерации события; для того чтобы обрабатывать события, надо определить функциюобработчик;

для создания источника события надо использовать методы и ireEvent класса Observable.

addEvents

Xtypes Xtype —

это псевдоним класса, только и всего. Есть, например, у нас класс Это обычное имя класса, которе мы используем, создавая

Ext.ux.MyGrid.

объект.


Глава 15. Структура и идеология библиотеки

181

Но в дополнение к имени класса вы можете указать так: Ext.reg('mygrid', Ext.ux.MyGrid);

Значение xtype (псевдонима) в этой записи — mygrid. Такой строчкой мы задаем новый xtype и связываем его с классом Ext.ux.MyGrid. Отлично. А зачем это надо? Представьте себе, что у нас большое приложение. Работая с ним, пользователь щелкает по кнопке, в результате чего создается и отображается таблица. Для того чтобы все это работало, надо создать объект и хранить его в памяти на клиентской машине. А если таких объектов сотни? Если же в вашем распоряжении есть механизм псевдонимов xtype, то достаточно просто включить в код ссылку такого вида: {xtype:'mygrid", border:false, width:600, height:400, ...}

Это называется ленивым созданием объекта (от англ. lazy instantiation). Когда пользователь щелкает по кнопке, Ext обнаруживает, что затребованный объект до сих пор не создан, но рецепт его создания имеется, так что менеджер компонентов выполнит такой код: create : function(config, defaultType){ return new types[config.xtype || defaultType](config); }

Иными словами: return new Ext.ux.MyGrid(config);

То есть наша таблица создается и отображается, но только та, что запрошена.

Классы ExtJS Опишем некоторые важные классы ExtJS, к ним надо будет обращаться либо явно, либо по цепочке наследования.

Класс Component

Класс Component предоставляет унифицированную модель для создания компонентов страницы, их отображения, управления ими и, наконец, уничтожения.


Часть IV. Библиотека ExtJS

182

Жизненный цикл компонента

Многие этапы такого цикла протекают незаметно для разработчика. Но все же авторы библиотеки считают, что понимание жизненного цикла важно для разработчиков, так что мы кратко его рассмотрим. 1. Инициализация компонента. При создании объекта класс Component получает все конфигурационные параметры создаваемого объекта. Компонент регистрируется менеджером компонентов ComponentMgr. Затем вызывается метод initComponent, который выполняет всю основную работу: вызывается класс, объект которого создается, прослеживается вся цепочка наследования этого класса, выстраивается логика работы создаваемого компонента. 2. После этого подгружаются плагины, если они нужны. Затем если компонент подразумевает наличие состояний, то они устанавливаются. Если необходимо, то созданный компонент встраивается в объектную модель документа. 3. Рендеринг. Термин "рендеринг" в разговоре об ExtJS мы будем использовать часто, подразумевая при этом встраивание созданного компонента в структуру страницы, отображение на этой странице. На этапе рендеринга создается контейнер для формируемого компонента (при необходимости). Применяются методы класса и стили, заданные пользователем. 4. Уничтожение компонента. Удаляются все обработчики событий, связанные с удаляемым элементом, сам элемент удаляется из DOM. Уничтожается вся цепочка наследования методов для удаляемого компонента. Удаляется регистрация компонента у менеджера ComponentMgr. Xtypes

класса Component

Каждый компонент имеет свой псевдоним (xtype), вот некоторые из них: box — псевдоним для Ext.BoxComponent; button

— Ext.Button;

component

— Ext.Component;

container

— Ext.Container;

grid

— Ext.grid.GridPanel;

panel

— Ext.Panel;

slider

— Ext.Slider;


Глава 15. Структура и идеология библиотеки viewport window form

183

— Ext.Viewport;

— Ext.Window;

— Ext.FormPanel;

label

— Ext.form.Label;

radio

— Ext.form.Radio;

textfield

— Ext.form.TextField.

Любой компонент может быть создан неявно указанием его псевдонима. При этом выделение памяти для этого компонента и вообще его инициализация откладываются до момента непосредственного обращения пользователя к этому объекту. // Явное создание объекта: var panel = new Ext.Panel({ ... items: [ new Ext.Button({ text: 'OK' }) ] }; // Неявное указание на объект с помощью псевдонима: var panel = new Ext.Panel({ ... items: [{ xtype: 'button', text: 'OK' }] };

В первой части кода кнопка создается в момент инициализации панели. Во втором случае кнопка не будет создана до отображения панели в окне браузера.

Класс BoxComponent

Этот важный класс расширяет функциональность класса Component и обеспечивает кроссбраузерное внедрение каждого компонента, который должен быть отображен и может использоваться в компоновке страницы. Класс BoxComponent управляет установкой размеров и позиционированием компонентов корректно для всех браузеров. Все классы контейнеров расширяют класс BoxComponent.


184

Часть IV. Библиотека ExtJS

Класс Container

Класс Container обеспечивает логику компоновки (layout) и представления (rendering) для определения размеров и вложения компонентов, предоставляя также механизм добавления компонентов в контейнер. Данный класс никогда не используется сам по себе, но служит базой для всех визуальных компонентов.

Класс Panel

Большинство задач по компоновке страницы решается с помощью этого класса. Панель может быть абсолютно невидимым контейнером, используемым для компоновки. Панель также снабжает блоки, из которых строится страница, панелями инструментов (toolbars), полосами прокрутки, кнопками, верхними и нижними колонтитулами (headers, footers). Следующие потомки класса Panel являются главными интерфейсными элементами окна или виджетами (от англ. widgets) в Ext 2.0: GridPanel; TabPanel; TreePanel; FormPanel.

Окно (Window)

Окно — это специализированная панель, размеры которой можно изменять, сворачивать, разворачивать, а само окно можно перетаскивать.

Окно просмотра (Viewport)

Класс Viewport встраивает компонент в тело документа и подстраивает свои размеры под окно просмотра браузера. Помните, что Viewport может встраиваться только к тегу body, и, следовательно, на странице можно использовать только один экземпляр класса Viewport.

Компоновка (layout) В Ext 2.0 создана целая система управления компоновкой элементов. Имеется 10 отдельных менеджеров компоновки, задающих практически все возможные способы компоновки содержимого страницы. Схемы компоновки не вызываются явно new. Вместо этого они создаются и используются классами контейнеров. Сами контейнеры ничего не знают


Глава 15. Структура и идеология библиотеки

185

о компоновке, они просто передают задачи построения макета страницы тому классу layout, который указан при конфигурировании контейнера. Каждый раз, когда вы создаете контейнер, вы описываете стиль компоновки и параметры компоновки в свойстве layoutConfig. Например: var panel = new Panel({ title: 'My Accordion', layout: 'accordion', // Стиль компоновки для этой панели layoutConfig: { animate: true // Параметры конфигурации компоновки } // Прочие опции класса Panel });

Каждый класс layout поддерживает свои опции конфигурации. Вы можете познакомиться с вариантами компоновки в табл. 15.1.

Таблица 15.1. Варианты компоновки в ExtJS

Изображение

Опция конфигурации ContainerLayout

CardLayout

AbsoluteLayout

ColumnLayout

AccordionLayout

Описание Это базовый класс для всех менеджеров компоновки. Такой макет используется для всех контейнеров, где компоновка не задана явно. Эта схема компоновки не имеет визуального представления, ContainerLayout просто включает компоненты в нужном месте, выводя их и изменяя при этом их размеры до необходимых для отображения в нужном месте Здесь контейнер содержит несколько элементов, но только один из них надо отображать в каждый конкретный момент Это простой макет, позволяющий точно позиционировать элементы, задавая координаты X и Y Этот макет позволяет создавать структуру в виде нескольких столбцов, где ширина столбца задается в процентах или пикселах, а высота определяется размером содержимого Этот макет содержит группу панелей, которые расположены одна над другой и могут быть развернуты или свернуты для отображения содержимого


Часть IV. Библиотека ExtJS

186

Таблица 15.1 (окончание)

Изображение

Опция конфигурации FitLayout

AnchorLayout

FormLayout

Описание Этот макет позволяет отобразить один компонент в соответствии с размерами контейнера Это макет для элементов, которые нужно привязывать к разным сторонам контейнера Это макет для отображения форм. Объекты FormPanel должны использовать layout:'form'

BorderLayout

TableLayout

Этот макет наиболее часто используется в бизнес-приложениях, позволяя вкладывать сворачиваемые панели, разделяя видимую область на части с помощью рамок Это макет для стандартной таблицы HTML с поддержкой параметров colspan и rowspan


Глава 16

Поиск элементов: класс DomQuery Обычно работа с ExtJS начинается с отбора элементов в HTML-документе, затем для этих элементов назначают обработчики событий, меняют стили и другие свойства отобранных элементов. Рассмотрим подробно методы такого отбора. Обычным способом отбора в JavaScript является отбор по значению идентификатора элемента. Но часто неудобно или невозможно выбирать узлы по их идентификатору. Допустим, вам более удобно отбирать узлы по значению какого-то иного их атрибута или имени стилевого класса. Для решения подобных задач ExtJS включает мощный и удобный класс DomQuery. Класс DomQuery работает HTML- и XML-документами и позволяет оперировать фильтрами узлов, содержащими CSS-селекторы пути до элемента и использующими синтаксис XPath. Метод Ext.DomQuery.select(), который мы будем сокращенно обозначать как Ext.query(), принимает два параметра: первый — строка селектора, второй — идентификатор элемента, в котором осуществляется поиск.

Выбор узлов DOM

Класс DomQuery можно использовать и сам по себе, но чаще его применяют к элементам посредством метода Element.select, который вызывает DomQuery для поиска элементов. Например, применим метод highlight ко всем параграфам нашего документа: Ext.select('p').highlight();

Этот пример демонстрирует удобный аспект работы с методом Element.select — он возвращает объект класса CompositeElement, обеспечивающий доступ ко всем вложенным элементам без необходимости перебирать все элементы в цикле. Посмотрим, как производится отбор элементов с помощью функции query.


Часть IV. Библиотека ExtJS

188

Селекторы элементов Ext.query("span")

— функция вернет массив элементов span;

Ext.query("span" "foo") Ext.query("#foo") Ext.query(".foo") Ext.query("*")

— элементы с идентификатором foo; — отобрать элементы класса foo;

— все элементы документа;

Ext.query("div span")

тов div.

— элементы span с идентификатором foo;

отобрать все элементы

span

внутри элемен-

Селекторы атрибутов

Эти селекторы позволяют отобрать элементы по значениям их атрибутов: Ext.query("*[class]") — все элементы с атрибутом class; Ext.query("*[class = bar]") — элементы класса bar; Ext.query("*[class! = bar]") — все элементы кроме тех, что из класса bar;

— элементы класса, имя которого начинается с b, т. е. при отборе можно использовать регулярные выражения. Ext.query("*[class^ = b]")

Отбор элементов CSS Value selectors

Эти селекторы отбирают элементы по значению атрибута такой:

style.

Формат

element{attribute operator value}

Примеры: Ext.query("*{color = red}")

— текстовые элементы красного цвета;

— все розовые элементы, являющиеся наследниками красных; Ext.query("*{color! = red}") — отобрать все элементы кроме красных; Ext.query("*{color^ = yel}") — элементы, название цвета которых начинается с "yel", т. е. снова можно применять регулярные выражения; Ext.query("*{color* = ow}") — все элементы, в значении атрибута color которых есть подстрока "ow". Ext.query("*{color = red} *{color = pink}")


Глава 16. Поиск элементов: класс DomQuery

189

Продемонстрируем, как можно выбирать элементы, используя термины объектной модели документа: Ext.query("span:first-child") — выбираем первый из вложенных элементов span; Ext.query("a:last-child") — ищем элемент a, являющийся последним из элементов-ссылок своего уровня; Ext.query("span:nth-child(2)") — элемент span, являющийся вторым ребенком элемента span; Ext.query("tr:nth-child(odd)") — нечетные элементы tr; Ext.query("li:nth-child(even)") — четные элементы li; Ext.query("a:only-child") — отбираем те элементы a, которые являются единственными детьми своих родителей; Ext.query("input:checked") — отбираем элементы input, выбранные пользователем; Ext.query("tr:first") — первый из тегов tr; Ext.query("td:nth(2)") — второй тег td; Ext.query("div:has(a)") — отбираем те div, которые имеют вложенные теги a; Ext.query("td:next(td)") — каждый элемент td, за которым снова идет td;

— каждый элемент label, перед которым стоит Посмотрите пример в листинге 16.1. Мы будем подсвечивать желтым цветом элементы, отбираемые по указанным правилам: Ext.query('li:first') — отобрать первый элемент списка (тег <li>); Ext.query('li:even') — отобрать все нечетные элементы списка (нумерация в JavaScript начинается с 0, а не с 1); Ext.query('li').splice(0,3) — показать три первых элемента списка. Счет начинаем с 0, а не с 1; Ext.query('li:not(.goofy)') — отобрать элементы 1, 2 и 4, поскольку они не относятся к классу goofy; Ext.query('p a[href* = #]') — все ссылки внутри параграфа, у которых атрибут href содержит символ #; Ext.query('code, li.goofy') — все элементы code и элементы списка класса goofy; Ext.query("label:prev(input)") input.


Часть IV. Библиотека ExtJS

190

Ext.query('ul .goofy > strong') — все элементы strong, вложенные в элементы класса goofy, но только те, что вложены в упорядоченный

список;

Ext.query('li + li > a[@href$ = pdf]') — все ссылки, заканчивающиеся на буквы "pdf", при этом ссылки должны быть вложены в такие элементы li, перед которыми идут другие элементы списка; Ext.query("span{display = none}") Ext.query("li:nth(4)")

— все скрытые элементы span;

— 4-й элемент списка.

Листинг 16.1. Отбор элементов <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv = "Content-Type" content = "text/html; charset = utf-8"> <title>DomQuery</title> <style> .yellow{background-color:yellow;} </style> <script type = "text/javascript" src = "ext-2.2/adapter/ext/ext-base.js"> </script> <script type = "text/javascript" src = "ext-2.2/ext-all.js"></script> <script> var domquery = function() { return { init: function() { Ext.select("div.dom-traversal-toggles").on("click", function(e, el){ var id = el.id; id = id.replace("dt-link", ""); if (id ! =

""){

var toggler = function(e){ var highlight = function(els){ for (var x = 0 ; x < els.length; x ++){ var el = Ext.get(els[x]);


Глава 16. Поиск элементов: класс DomQuery

191

if (el) { if el.hasClass("yellow")) { el.removeClass("yellow"); }else{ el.addClass("yellow"); } } } return els; }; var highlightHidden = function(els){ var elmts = highlight(els); for (var x = 0 ; x < elmts.length; x ++){ var el = Ext.get(elmts[x]); if (el) { if (el.hasClass("yellow")){ el.fadeIn(); }else{ el.fadeOut(); el.removeClass("yellow"); } } } } switch(e){ case "1":

highlight(Ext.query('li:first', "extdt"));

break; case "2": highlight(Ext.query('li:even', "extdt")); break; case "3": highlight(Ext.query('li', "extdt").splice(0,3)); break; case "4": highlight(Ext.query('li:not(.goofy)', "extdt")); break; case "5":

highlight(Ext.query('p a[href* = #]', "extdt"));

break; case "6": highlight(Ext.query('code, li.goofy', "extdt")); break; case "7": break;

highlight(Ext.query('ul .goofy > strong', "extdt"));


Часть IV. Библиотека ExtJS

192

case "8": highlight(Ext.query('li+li>a[href$ = pdf]', "extdt")); break; case "9": if(Ext.query("span{display = none}","extdt").length >0) highlightHidden(Ext.query("span{display = none}", "extdt")); }else { highlightHidden( Ext.query("span{display}", "extdt")); } break; case "10": highlight( Ext.query("li:nth(4)", "extdt") ); break; } }(id); } if (e = = "x"){ } } ); } } }(); Ext.onReady( function(){ domquery.init(); } ); </script> </head> <body id = "body"> <!— Фрагмент, в котором ищем элементы - - > <div style = "border: 1px solid rgb(0, 0, 0); padding: 1em; width: 400px;" id = "extdt"> <p class = "goofy"> Это <em>параграф</em> с <strong>текстом</strong> класса class = "goofy." В нем есть <a title = "http://www.englishrules.com" class = "external text" href = "http://www.englishrules.com">внешняя ссылка</a> , какой-то <code>код</code> и <a title = "" href = "#dt-link3_same-page_link">ссылка на #dt-link3 </a>. </p> <ul> <li>Это первый элемент списка со ссылкой на


Глава 16. Поиск элементов: класс DomQuery

193

<a title = "Silly.pdf" class = "new" href = "/action/edit/Silly.pdf"> silly.pdf</a>. </li> <li class = "goofy"> <em>Это второй <strong> элемент</strong> списка</em> класса class = "<strong>goofy</strong>". </li> <li >Это третий элемент списка. <span style = "display:none;"> Сюрприз!</span> </li> <li> <strong>Это четвертый элемент списка</strong> с совсем уже глупой ссылкой на <a title = "Silly.pdf silly.pdf" class = "new" href = "/action/edit/Silly.pdf_silly.pdf">silly.pdf_silly.pdf</a>. </li> </ul> </div> <!— Конец фрагмента, в котором мы ищем элементы - - > <!— Кнопки для подсветки выбранных элементов - - > <div class = "dom-traversal-toggles"> <ul> <li><input type = "submit" value = "Переключить" id = "dt-link1" /> <code>Ext.query('li:first')</code> </li>

Выглядеть в браузере это должно

<li><input type = "submit" value = "Показать" id = "dt-link2" /> <code>Ext.query('li:even')</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link3"/> <code>Ext.query('li').splice(0,3)</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link4"/> <code>Ext.query('li:not(.goofy)')</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link5"/> <code>Ext.query('p a[href* = #]')</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link6"/> <code>Ext.query('code, li.goofy')</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link7"/> <code>Ext.query('ul .goofy > strong')</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link8"/> <code>Ext.query('li + li > a[@href$ = pdf]')</code> </li> <li><input type = "submit" value = "Показать" id = "dt-link9"/> <code>Ext.query("span{display = none}")</code> .</li> <li><input type = "submit" value = "Показать" id = "dt-link10"/> <code>Ext.query("li:nth(4)")</code> </li> </ul> </div> </body> </html>


194

Часть IV. Библиотека ExtJS

Рис. 16.1. Применение метода query

Выглядеть у вас в браузере это должно так, как показано на рис. 16.1. Код получился довольно длинным, объясним его здесь, но при первом чтении это объяснение можно пропустить, вернувшись впоследствии, когда разберетесь с некоторыми методами, описываемыми в следующих главах. Начнем разбор с конца сценария. В самом конце вызывается функция Ext.onReady, срабатывающая после загрузки DOM. Она вызывает функцию domquery.init().

Эта функция и выполняет всю основную работу. Она привязывает событие click к элементам раздела div класса dom-traversal-toggles. Каждый


Глава 16. Поиск элементов: класс DomQuery

195

щелчок по кнопке приводит к тому, что выбираются некоторые элементы, выбранные элементы подсвечиваются желтым цветом, а повторный щелчок по той же кнопке удаляет подсветку. При щелчке запускается функция, определяющая элемент, по которому щелкнули, затем метод replace удаляет из идентификатора элементаисточника события все, кроме последней цифры. Эта цифра записывается в переменную и используется для определения того, какие именно элементы надо подсвечивать функцией highlight. Сам процесс отбора можно увидеть в конструкции switch, в ней также вызывается и сама функция highlight, определенная в программе несколько раньше. Функции highlight в качестве параметров передается массив отобранных элементов, относительно которых методом hasClass проверяется, к какому стилевому классу они относятся. Затем класс либо удаляется функцией removeClass, либо наоборот, добавляется функцией addClass, в зависимости от исхода проверки. Кроме того, для 9-й кнопки нам потребуется функция highlightHidden, которая умеет не только подкрашивать элемент, но и применять к нему методы fadeIn или fadeOut для отображения или скрытия элемента, причем исчезает элемент при этом постепенно.


Глава 17

Панели и компоновка элементов Простая панель

Создадим на странице панель, которую можно будет сворачивать и разворачивать щелчком по встроенной в нее особой кнопке. Панель должна появиться в самом верху документа, поэтому составляющие ее элементы должны быть среди первых наследников элемента body. Сделаем так: добавим в начало документа новый пустой div, а к нему прикрепим панель. Вид простой панели в браузере представлен на рис. 17.1. Код создания простой панели дан в листинге 17.1.

Рис. 17.1. Простая панель


Глава 17. Панели и компоновка элементов

197

Листинг 17.1. Сворачиваемая панель <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>ExtJS</title> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script> <script type="text/javascript" src="ext-2.2/ext-all.js"> </script> <script type="text/javascript" > Ext.onReady(function(){ Ext.get(document.body).update('<div id="test"></div>'); new Ext.Panel({ renderTo:

'test',

width:

'200px',

title:

'Панель',

html:

'Содержимое панели',

collapsible: true }); }); </script> </head> <body><div id="div1"></div></body> </html>

Используем здесь статический метод get, возвращающий элемент по указанному идентификатору или имени элемента. Будем оперировать методами класса Element: метод update изменяет свойство innerHTML только что выбранного нами элемента, добавляя в него HTML-фрагмент '<div id="test"></div>'.

Затем создаем объект Panel (панель). Панель — это основной контейнер для контента страницы. Панель может содержать верхние и нижние линейки инструментов (toolbars), собственные заголовки, может отображать содержимое в пределах различных секций. Пользователь может сворачивать и разворачивать эти секции. Панели просто разместить в любом контейнере.


Часть IV. Библиотека ExtJS

198

В нашем примере использованы некоторые конфигурационные параметры объекта Panel: html — HTML-фрагмент, который будет служить контентом панели; renderTo — идентификатор узла, к которому будет присоединена панель; title — текст заголовка панели; width — ширина панели в пикселах; collapsible — значение true для этого параметра позволяет сворачивать и разворачивать панель с помощью кнопки управления, находящейся в заголовке панели.

Вложенные панели

Вложим теперь одну панель в другую (листинг 17.2). Листинг 17.2. Вложенные панели <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>extjs</title> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script> <script type="text/javascript" src="ext-2.2/ext-all.js"> </script> <script type="text/javascript"> Ext.onReady(function(){ panel1 = new Ext.Panel({ title:

'Внешняя панель',

collapsible: true, renderTo:

Ext.getBody(),

html:

'Содержимое внешней панели',

items: [ new Ext.Panel({ title:

'Внутренняя панель',

collapsible: true,


Глава 17. Панели и компоновка элементов html:

199

'Содержимое внутренней панели'

}) ] }); }); </script> </head> <body></body> </html>

Конфигурационный параметр items содержит информацию об объектах, вкладываемых в панель. Выглядит это в браузере так, как представлено на рис. 17.2.

Рис. 17.2. Вложенные панели

Компоновка панелей: создание аккордеона Web-страница

в ExtJS создается в результате компоновки объектов и отображения созданного макета. Поэтому все примеры в этой главе начинаются с создания панелей, т. е. объектов, но сейчас пришла пора научиться их компоновать с использованием макетов или компоновок (layout) и отображать с помощью специального объекта Viewport. Виды компоновок приводились в главе 15, там же говорилось, что контейнеры обращаются к ним, когда необходимо скомпоновать страницу. Рассмотрим весь этот процесс на примере


Часть IV. Библиотека ExtJS

200

одного из видов компоновки — аккордеона (accordion), как представлено в листинге 17.3.

Листинг 17.3. Аккордеон <html> <head> <title>Accordion Layout</title> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"/> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-2.2/ext-all.js"></script> <style type="text/css"> html, body { font:

normal 12px verdana;

margin:

0;

padding:

0;

border:

0 none;

overflow: hidden; height:

100%;

} .empty .x-panel-body { padding-top: 20px; text-align:

center;

font-style:

italic;

color:

gray;

font-size:

11px;

} </style> <script type="text/javascript"> Ext.onReady(function() { var item1 = new Ext.Panel({ title: 'Аккордеон. Элемент 1', html: cls:

'Какое-нибудь содержание',

'empty' // Указание применяемого стилевого класса

}); var item2 = new Ext.Panel({ title: 'Аккордеон. Элемент 2',


Глава 17. Панели и компоновка элементов html:

'Какое-нибудь содержание',

cls:

'empty'

}); var item3 = new Ext.Panel({ title: 'Аккордеон. Элемент 3', html:

'Какое-нибудь содержание',

cls:

'empty'

}); var item4 = new Ext.Panel({ title: 'Аккордеон. Элемент 4', html:

'Какое-нибудь содержание',

cls:

'empty'

}); var item5 = new Ext.Panel({ title: 'Аккордеон. Элемент 5', html:

'Какое-нибудь содержание',

cls:

'empty'

}); var accordion = new Ext.Panel({ region:

'west',

margins: '5 0 5 5', split:

true,

width:

210,

layout:

'accordion',

items:

[item1, item2, item3, item4, item5]

// Задание компоновки

}); var viewport = new Ext.Viewport({ layout: 'border', items:[ accordion, { region:

'center',

margins:

'5 5 5 0',

cls:

'empty',

bodyStyle: 'background:#f1f1f1', html: }] }); });

'<br/><br/>Содержимое центральной панели'

201


Часть IV. Библиотека ExtJS

202 </script> </head> <body></body> </html>

Создав пять простых панелей, мы объединяем их вместе в объекте accordion, указываем с помощью свойства region, что их всех надо позиционировать относительно левого края страницы, и задаем параметр layout для того, чтобы применить соответствующий вариант компоновки. Попробуйте применить другие значения, например, anchor или column. Посмотрите, как изменится компоновка панелей. С меньшим успехом, но также можно применить значения absolute, fit, absolute и table. На рис. 17.3 представлен вид страницы, скомпонованной в виде аккордеона.

Рис. 17.3. Аккордеон

Последнее действие в этом примере — это создание объекта Viewport. Класс Ext.Viewport описывает специализированные контейнеры, позволяющие отображать содержимое в браузере. Объект класса Viewport внедряет себя в тело документа, автоматически меняя свои размеры под размеры окна браузера, иначе говоря, он создает окно для демонстрации содержимого в рамках окна браузера. На странице можно создать только один экземпляр класса Viewport.

Сам Viewport не обеспечивает прокрутки содержимого, так что в панеляхнаследниках требуется указать необходимые параметры для реализации прокрутки (scrolling).


Глава 17. Панели и компоновка элементов

203

При создании объекта Viewport надо задать значение параметра layout и список отображаемых компонентов. Мы задали значение border для параметра layout, что означает использование компоновки BorderLayout. Отображать будем аккордеон и центральную панель, конфигурацию которой тут же и указываем. Обратите внимание на последнее обстоятельство: не обязательно заранее создавать объект для отображения, можно сделать это и на лету.

Панель с несколькими вкладками

Усовершенствуем нашу страницу, создав на ней несколько панелей-вкладок, на которых можно размещать содержимое различных файлов. Для начала напишем HTML-каркас (листинг 17.4). Листинг 17.4. Страница с несколькими панелями-вкладками <html> <head> <title>TabPanel Tutorial</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-2.2/ext-all.js"></script> <script type="text/javascript" src="tab_actions.js"></script> <style> #actions li { margin:.3em; } #actions li a { color:#666688; text-decoration:none; } </style> </head> <body> <ul id="actions" class="x-hidden"> <li><a id="use" href="#">Перейти на существующую вкладку</a></li>


204

Часть IV. Библиотека ExtJS

<li><a id="create" href="#">Создать новую вкладку</a></li> </ul> <div id="tabs"></div> </body> </html>

В приведенном коде есть два важных элемента. Во-первых, это список с идентификатором actions, который послужит перечнем выполняемых действий. Действия инициируются щелчком по пункту синего меню слева страницы (рис. 17.4). Во-вторых, раздел с идентификатором tabs станет контейнером для первой вкладки. В этой вкладке у нас будет отображаться файл 1.html.

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

Рис. 17.4. Вид панели с вкладками


Глава 17. Панели и компоновка элементов

205

Сценарий tab_actions.js в листинге 17.5 выполняет всю работу по созданию вкладок и размещению документов в них. Листинг 17.5. Сценарий tab_actions.js Ext.onReady(function(){ // Меню возможных действий пользователя var tabActions = new Ext.Panel({ frame:

true, // Отображать со скругленными рамочками

title: 'Действия',

// Разрешаем сворачивание панели по щелчку кнопкой в заголовке панели collapsible:

true,

// Указываем идентификатор элемента, который превращаем в панель contentEl:

'actions',

// Разрешаем сворачивание щелчком по любому месту заголовка панели titleCollapse: true }); // Родительская панель для меню var actionPanel = new Ext.Panel({ id:

'action-panel',

region:

'west',

split:

true,

collapsible:

true,

collapseMode: 'mini', width:

200,

minWidth:

150,

border:

false,

baseCls:

'x-plain',

items:

[tabActions]

}); // Главная панель для вкладок var tabPanel = new Ext.TabPanel({ region:

'center',

/* TabPanel использует Ext.layout.CardLayout для управления

вкладками. Здесь мы указываем, что вкладка первый раз отобразится только после того, как к ней обратятся. */

deferredRender: false, autoScroll:

true,


Часть IV. Библиотека ExtJS

206 margins:

'0 4 4 0',

activeTab:

0,

items:[{ id:

'tab1',

contentEl:

'tabs',

title:

'Главная',

closable:

false,

autoScroll:

true

}] }); // Конфигурируем Viewport viewport = new Ext.Viewport({ layout: 'border', items:

[actionPanel,tabPanel]});

// Добавляем вкладку в центральную панель function addTab(tabTitle, targetUrl){ tabPanel.add({ // Добавляем компонент в контейнер title:

tabTitle,

iconCls: 'tabs', // Получаем указанный URL с помощью AJAX-запроса autoLoad: {url: targetUrl, callback: this.initSearch, scope: this}, closable: true }).show();

// Показываем компонент

} // Обновляем содержимое вкладки, при его отсутствии

- создаем

function updateTab(tabId,title, url) { var tab = tabPanel.getItem(tabId); // Отбираем по идентификатору if (tab){ tab.getUpdater().update(url); tab.setTitle(title); }else{ tab = addTab(title,url); } tabPanel.setActiveTab(tab); } // Связываем идентификаторы и функции var count = 0;

// Запрашиваем указанный url


Глава 17. Панели и компоновка элементов var actions = { 'create': function(){ addTab("Новая вкладка",'1.html'); }, 'use': function(){ updateTab('tab1','Заменялось' + count + (count%2) + '.html'); count++; } };

207

'раз','sample' +

function doAction(e, t){ /* Останавливаем распространение события и предотвращаем его стандартную обработку */ e.stopEvent(); actions[t.id](); } // После задания Viewport привязываем событие mousedown actionPanel.body.on('mousedown', doAction, null, {delegate:'a'}); });

Весь наш код обернут в метод Ext.onReady, так что он не будет выполняться до того, как в браузер будут загружены все элементы HTML-документа. Первое, что мы делаем, — создаем объект tabActions, представляющий собой панель для меню. Оно будет отображаться слева в окне на синем фоне. Для этого потребуется конвертировать список <ul> с идентификатором actions в панель tabActions. Значением конфигурационного параметра contentEl как раз и должен быть идентификатор подходящего для этой цели списка. Параметр frame со значением true позволяет указать, что панель должна обрамляться скругленными рамочками. Затем создадим родительскую панель actionPanel для меню; значение параметра items должно указывать на имя панели tabActions, подключаемой внутрь родительской панели. Панель actionPanel будет позиционирована на странице с помощью специального менеджера, так что нам потребуется указать значение параметра region при конфигурировании. Параметр baseCls указывает на то, какой стилевой класс будет использован для панели (по умолчанию это 'x-panel'). Третий компонент, который надо создать, — главная панель TabPanel с вкладками. Она должна располагаться в середине страницы, что соответствует


208

Часть IV. Библиотека ExtJS

значению 'center' параметра region. Кроме того, мы передаем список объектов, которые следует отображать во вкладках. Наконец, отображаем все эти панели с помощью Viewport. После предыдущего примера трудности для читателя этого представлять не должно. Теперь, когда все элементы созданы, мы можем добавить методы для обновления панели TabPanel. Создадим три файла, содержимое которых будет отображаться во вкладках: 1.html, sample0.html и sample1.html. Неважно, какое именно будет у них содержимое, но оно должно позволить отличить загрузку одного файла от другого. Функция addTab добавляет новую вкладку в центральную панель, вызывая метод add. На этой вкладке должно отображаться содержимое файла 1.html. При вызове данной функции придется передавать имя отображаемого файла. Функция updateTab принимает параметр tabId, который позволяет проверить, существует ли уже нужная вкладка. Если существует, то вызывается метод Updater, который обновляет содержимое панели. В противном случае вызывается метод addTab для создания вкладки. В конце скрипта мы определяем обработчик события mousedown, вызывающий функцию doAction при наступлении события. Событие должно возникнуть в пределах панели actionPanel, в которую помещено меню. Функция doAction предотвращает выполнение действий по умолчанию для этого события методом stopEvent и вызывает функцию actions. Но в функции on есть еще опция delegate:'a', заставляющая передавать в функцию actions идентификаторы элементов списка, из которых мы сделали меню и по которым происходит щелчок. В зависимости от идентификатора, который может быть либо create, либо use, вызывается либо addTab, либо updateTab.


Глава 18

Формы Создание элемента формы

Для создания любого элемента формы в ExtJS существует соответствующий объект. Создадим, например, кнопку (листинг 18.1). Листинг 18.1. Кнопка <html> <head> <title>Введение в Ext</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"/> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-2.2/ext-all.js"></script> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"> <script type="text/javascript"> button = new Ext.Button({ text: 'Кнопка', renderTo: Ext.getBody(), handler: function() { Ext.MessageBox.alert("На меня нажали!", "Сообщение"); } }) </script> </head> <body> </body> </html>


Часть IV. Библиотека ExtJS

210

Надпись на кнопке формируется свойством text, местоположение на странице (в данном случае кнопка вкладывается непосредственно в элемент body) определяется значением свойства renderTo, обработчик события задается в свойстве handler. Как это будет выглядеть на экране, показано на рис. 18.1.

Рис. 18.1. Кнопка

Компоновка формы Теперь на очереди создание формы и компоновка ее вызовом соответствующего способа макетирования. Применим здесь компоновку Absolutelayout, создавая форму (листинг 18.2). Листинг 18.2. Форма с Absolutelayout <html> <head> <title>Absolute Forms</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"/> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script> <script type="text/javascript" src="ext-2.2/ext-all.js"></script> <script type="text/javascript" src="absform.js"></script>


Глава 18. Формы

211

</head> <body> <h1>Absolute Layout with Forms</h1> </body> </html>

Вид формы в браузере представлен на рис. 18.2.

Рис. 18.2. Форма с компоновкой Absolutelayout

Разберем, как работает сценарий из листинга 18.3. FormPanel является стандартным контейнером формы и выполняет компоновку с помощью макетов (layouts).

По умолчанию данные формы в ExtJS передаются AJAX-запросом, используя объект Ext.form.Action. Параметр url в листинге 18.3 задает адрес, по которому эти данные будут отправлены. В контейнер формы надо включить один или несколько компонентов, задавая их с помощью параметра items. Если компонентов несколько, то формат их определения таков: items: [{...}, {...}]. Каждый компонент описывается внутри фигурных скобок и может относиться к одному и типов xtypes, описанных в документации по классу Ext.Component. Перечень xtypes, используемых в формах, включает в себя: form, checkbox, combo, datefield, field, fieldset, hidden, htmleditor, label, numberfield, radio, textarea, textfield, timefield trigger.

и


Часть IV. Библиотека ExtJS

212

Позиционируем элементы с помощью соответствующего макета и встраиваем форму в новое окно. Затем отображаем это окно с помощью функции show. Листинг 18.3. Сценарий absform.js Ext.onReady(function() { var form = new Ext.form.FormPanel({ // Применяемый класс CSS

baseCls: 'x-plain',

// Тип компоновки

layout:

'absolute',

url:

'save-form.php',

// Куда отправлять данные формы

defaultType: 'textfield', // Задание полей формы

items: [{ x: 0, y: 5, xtype: 'label', text:

'Кому:'

// xtype создаваемого элемента

},{ x: 60, y: 0, name:

'to',

anchor: '100%'

// Ширина в процентах

},{ x: 0, y: 35, xtype: 'label', text:

'Тема:'

},{ x: 60, y: 30, name:

'subject',

anchor: '100%'

// Ширина в процентах

},{ x:0, y: 60, xtype:

'textarea',

name:

'message',

anchor: '100% 100%' }] });

// Ширина и высота


Глава 18. Формы

213

var window = new Ext.Window({ title:

'Размеры этого окна можно менять',

width:

500,

height:

300,

minWidth:

300,

minHeight: 200, layout:

'fit',

plain:true, bodyStyle:'padding:5px;', buttonAlign:'center', items: form, buttons: [{ text: 'Отправить' },{ text: 'Отменить' }] }); window.show(); });

Передача данных формы на сервер методом submit

Сделаем форму, которая будет отправлять на сервер данные, указанные пользователем. Серверный сценарий проверяет имя и пароль пользователя, в случае успеха надо перенаправить пользователя на следующую страницу. Требуемый HTML-документ не заслуживает распечатки и похож на тот, что показан в листинге 18.2, только ссылка в нем дана на скрипт login.js. Читатель найдет этот файл на компакт-диске под именем login.html. Проверка нашей формы будет осуществляться серверным сценарием login.php (листинг 18.4). Листинг 18.4. Сценарий проверки формы login.php <?php $loginUsername = isset($_POST["loginUsername"]) ? $_POST["loginUsername"] : ""; if ($loginUsername == "user"){


Часть IV. Библиотека ExtJS

214 echo "{success: true}"; } else {

echo "{success: false, errors: { reason: 'Login failed. Try again.' }}"; } ?>

То есть мы проверяем, действительно ли пользователь указал имя user. В противном случае не регистрируем его. Еще нам потребуется сценарий test.php, на который наша форма перебросит пользователя после успешной регистрации. Внесем в него немудрящее содержание: <?php echo "Привет прошедшим регистрацию!"; ?>

Теперь дошла очередь до самого сценария login.js (листинг 18.5). Логика действий в нем такова: сначала создаем панель с формой Ext.FormPanel. Конфигурационные параметры панели таковы: labelWidth — ширина пометки на форме; url — куда отправляем данные формы; frame — скругляем углы рамочек; monitorValid — значение true заставляет форму отслеживать клиентские события и запускать необходимые обработчики; items — поля формы, в них атрибут name задает имя переменной, которая будет отправлена на сервер; buttons — кнопки. Создав форму, мы отображаем ее в новом окне, компонуемом с помощью fitlayout. Обратите внимание на параметр items, в котором и указано, что следует отображать в окне. Листинг 18.5. Сценарий генерации формы login.js Ext.onReady(function(){ var login = new Ext.FormPanel({ labelWidth: url: frame:

80, 'login.php', true,

title:

'Зарегистрируйтесь, пожалуйста',

defaultType:

'textfield',

monitorValid:

true,


Глава 18. Формы

215

items:[{ fieldLabel: 'Логин', name: 'loginUsername', allowBlank: false },{ fieldLabel: 'Пароль', name: 'loginPassword', inputType: 'password', allowBlank: false }], buttons:[{ text: 'Отправить', formBind: true, handler: function(){ login.getForm().submit({ method: 'POST', waitTitle: 'Соединение с сервером', waitMsg: 'Отправка данных...', success:function(){ Ext.Msg.alert('Status', 'Успешно!', function(btn, text){ if (btn == 'ok'){ var redirect = 'test.php'; window.location = redirect; } }); }, failure:function(form, action){ if(action.failureType == 'server'){ obj = Ext.util.JSON.decode(action.response.responseText); Ext.Msg.alert('Регистрация не прошла!', obj.errors.reason); }else{ Ext.Msg.alert('Warning!', 'Сервер недоступен : ' + action.response.responseText); } login.getForm().reset(); } }); } }] }); var win = new Ext.Window({


216

Часть IV. Библиотека ExtJS

layout: 'fit', width: 300, height: 150, // Не показываем кнопку, позволяющую закрыть это окно closable: false, resizable: false, // Запрещаем пользователю менять размер окна plain: true, // Создаем окно с прозрачным фоном border: false, items: [login] }); win.show(); });

Разберемся теперь, как устроены поля формы. Обработчик события кнопки описан в свойстве handler. Методом getForm мы получаем в обработчике доступ к форме, затем для этой формы вызываем метод submit, с его помощью отправляем методом POST данные на сервер, в сценарий login.php. В случае успеха вызываем функцию, определенную в параметре success, которая выводит диагностическое сообщение и перенаправляет пользователя на страницу test.php. Параметр failure задает действия в случае неудачи. Здесь нам придется оперировать объектом action. Форма создает объект этого класса при отправке данных и передает его в callback-функции, вызываемые при успехе или неудаче передачи данных на сервер. В случае неудачи свойство failureType содержит одно из следующих значений: CLIENT_INVALID, SERVER_INVALID, CONNECT_FAILURE или LOAD_FAILURE. Поэтому дальнейшие действия предусматривают анализ ответа сервера и вывод сообщений на основании ответа. Посмотрите на результат наших трудов в браузере (рис. 18.3).

Рис. 18.3. Форма с проверкой данных на сервере


Глава 18. Формы

217

Проверка форм с помощью класса VTypes. Календарь-подсказка

Особенно эффектно выглядит работа с формами в ExtJS, когда дело доходит до проверки дат и времени. Уже сам вид полей для ввода дат и времени DataField и TimeField с выпадающими календарными подсказками производит сильное впечатление, а уж дополнительные возможности проверки методами класса VTypes и вовсе переводят обработку форм на принципиально иной уровень. Стандартные текстовые подсказки, появляющиеся около полей формы, хранятся в отдельном файле. Кроме того, в ExtJS встроены календари, с помощью которых легко выбрать дату. Для русификации текстовых и календарных сообщений надо найти и подключить файл ext-lang-ru.js: <script type="text/javascript" src="ext-lang-ru.js"></script>

Рис. 18.4. Проверка полей формы с помощью VTypes


Часть IV. Библиотека ExtJS

218

Все, дорогой читатель! Теперь у вас есть красивый русскоязычный календарь! Сначала полюбуйтесь, как все это выглядит в браузере (рис. 18.4), а потом начнем разбираться. Итак, снова HTML-файл adv-vtypes.html со ссылкой на скрипт adv-vtypes.js можно найти на компакт-диске, но его мы здесь не приводим по причине тривиальности, а сам сценарий JavaScript — в листинге 18.6. Листинг 18.6. Сценарий adv-vtypes.js для усовершенствованной проверки форм /* Синглетон Ext.form.VTypes определяет параметры проверки форм.

Начинаем здесь задавать типы объектов формы: daterange и password */

Ext.apply(Ext.form.VTypes, { daterange : function(val, field) { // Метод parseDate преобразует дату в заданный параметром формат var date = field.parseDate(val); if (!date){ return; } if (field.startDateField && (!this.dateRangeMax || (date.getTime() != this.dateRangeMax.getTime()))) { var start = Ext.getCmp(field.startDateField); // Устанавливает максимально допустимое значение даты start.setMaxValue(date); start.validate(); this.dateRangeMax = date; } else if (field.endDateField && (!this.dateRangeMin || (date.getTime() != this.dateRangeMin.getTime()))) { var end = Ext.getCmp(field.endDateField); // Устанавливает минимально допустимое значение даты end.setMinValue(date); // Проверяет поля на допустимость значений end.validate(); this.dateRangeMin = date; } /* Всегда возвращает true, поскольку мы используем этот VType

только для установки допустимых значений */

return true;


Глава 18. Формы

219

}, password : function(val, field) { if (field.initialPassField) { var pwd = Ext.getCmp(field.initialPassField); return (val == pwd.getValue()); } return true; }, passwordText : 'Указаны различные пароли' }); Ext.onReady(function(){ Ext.QuickTips.init(); // Включаем отображение ошибки во всплывающем окне Ext.form.Field.prototype.msgTarget = 'side'; var bd = Ext.getBody(); /* ================

Диапазон дат ====================== */

var dr = new Ext.FormPanel({ labelWidth: 125, frame: title:

true, 'Диапазон дат',

bodyStyle: 'padding:5px 5px 0', width: defaults:

350, {width: 175},

defaultType: 'datefield', items: [{ fieldLabel:

'Начальная дата',

name:

'startdt',

id:

'startdt',

vtype:

'daterange',

endDateField:

'enddt' // Идентификатор поля конечной даты

// Имя поля начальной даты

},{ fieldLabel:

'Конечная дата',

name:

'enddt',

id:

'enddt',

vtype:

'daterange',

// Имя поля конечной даты

startDateField: 'startdt' // Идентификатор поля начальной даты }] }); dr.render('dr');


Часть IV. Библиотека ExtJS

220 /* ===============

Проверка пароля ======================

*/

var pwd = new Ext.FormPanel({ labelWidth:

125,

frame:

true,

title:

'Проверка пароля',

bodyStyle:

'padding:5px 5px 0',

width:

350,

defaults: { width: inputType:

175, 'password' // Задаем тип поля ввода

}, defaultType:

'textfield',

items: [{ fieldLabel:

'Пароль', // Задаем метку поля

name:

'pass',

id:

'pass'

},{ fieldLabel:

'Подтвердите пароль',

name:

'pass-cfrm',

vtype:

'password',

// Задаем тип поля

initialPassField: 'pass' // Идентификатор первого поля пароля }] }); pwd.render('pw'); // Отображаем панель pwd с проверяемой формой });

Для того чтобы понять логику сценария, проще начать рассматривать его с середины — с функции Ext.onReady. Действия в ней начинаются с инициализации механизма всплывающих подсказок. Эту задачу решает функция Ext.QuickTips.init.

Мы собираемся во всплывающей подсказке оповещать о несовпадающих паролях, поэтому следующее, что надо сделать, — это задать область, где будут выводиться диагностические сообщения. Для этого надо установить значения конфигурационной опции msgTarget для класса Ext.form.Field. Изменим это значение, обратившись к прототипу этого класса. Допустимыми значениями опции являются: qtip — отображать подсказку, когда пользователь наводит курсор на поле; title — отображать дефолтовое для браузера окно сообщения;


Глава 18. Формы under side

221

— добавлять блок div с текстом в начало поля, содержащего ошибку;

— добавлять значок с сообщением об ошибке справа от поля;

— добавлять текст ошибки прямо в свойство innerHTML выбранного элемента. [element id]

Затем создаем две формы. Первая форма предназначена для ввода дат, причем первая дата должна быть более ранней, чем вторая. Вторая форма предполагает, что пользователь дважды введет пароль, а проверка проводится для того, чтобы исключить возможность ввода различных значений. Как только создаем формы, так сразу отображаем их методом render. Параметр vtype полей этих форм связывает их с функциями daterange и password, определенными в начале скрипта. А в начале сценария вызывается метод apply, который копирует все указанные ему опции в конфигурируемый объект. Опциями как раз и будут две упомянутые функции, а также текст диагностического сообщения, указанный в опции passwordText. А вот объект — это объект класса Ext.form.VTypes, предоставляющий возможность проверок данных форм, причем проверок, характер которых можно настраивать.


Глава 19

Визуальные эффекты. Drag & drop

Свертывание и развертывание блока

Начнем с фрагмента текста, который сворачивается или разворачивается при щелчке по ссылке. Воспользуемся для этого встроенными в библиотеку ExtJS методами. Наша страница будет состоять из нескольких блоков div и небольшого текста (листинг 19.1). Листинг 19.1. toggle.html <!DOCTYPE HTML PUBLIC "-// W3C// DTD HTML 4.01 Transitional// EN" "http:// www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Слайдер - элемент с переменными размерами</title> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js">

</script>

<script type="text/javascript" src="ext-2.2/ext-all.js"> </script> <script type="text/javascript" src="slidingtext.js"> </script> <style type="text/css"> .click_div { background-color: #CC00CC; padding: }

20px;


Глава 19. Визуальные эффекты. Drag & drop

223

</style> </head> <body> <h2>Щелкай по ссылкам!</h2> <ul> <li><a id="textup" href="javascript:;">Текст сворачивается вверх </a> </li> <li><a id="textdown" href="javascript:;">Текст сворачивается вниз </a> </li> <li><a id="texttoggle" href="javascript:;">Показываем или прячем текст </a> </li> </ul> <p> </p> <div class="click_div" id="div1">Я div с ID = 1</div> <div id="slider">Слайдер</div> <div class="click_div" id="div2">Другой div</div> <div id="noslide">Здесь слайдера нет</div> </body> </html>

Теперь напишем сценарий JavaScript (листинг 19.2). Вначале сделаем так, чтобы сценарий не запускался до загрузки всех элементов DOM в браузер (кроме рисунков). Для этого применим функцию Ext.onReady(). Дальше следует перехватить событие, возникающее при щелчке по ссылке. При щелчке по элементу textup должна вызываться функция slideText с параметрами up и slider, а затем привлекаем внимание к элементу textup, используя визуальный эффект из класса ExtJS.FX. Класс FX снабжает разработчика анимационными визуальными эффектами. Применяемый нами метод frame создает неяркую расширяющуюся рамочку вокруг слов, по которым щелкает пользователь. Постарайтесь разглядеть ее на рис. 19.1 и в своем браузере. В теле функции onReady() сначала идет определение анонимной функции, которая принимает два параметра, передаваемые ей обработчиком событий Ext.get().on. Это параметры — событие event и целевой объект target, с их помощью можно управлять обработкой события и узнать, по какому элементу был выполнен щелчок.


Часть IV. Библиотека ExtJS

224

Рис. 19.1. Подсветка активного элемента

Листинг 19.2. sliding.js // Удостоверьтесь, что путь до файла здесь указан правильно! Ext.BLANK_IMAGE_URL = 'ext-2.2/resources/images/default/s.gif'; // Вызывается после построения дерева DOM Ext.onReady(function() { // Задаем функцию, выполняющую всю анимационную работу var slideText = function(direction,element){ var slideMe = Ext.get(element); switch(direction){ // Определяем направление движения case 'up' : // Проверяем, виден ли элемент if (slideMe.isVisible()) { // Раз мы сюда попали, значит, элемент виден slideMe.slideOut('t', { easing: 'easeOut', duration: .5, // Продолжительность эффекта remove: false, useDisplay: true


Глава 19. Визуальные эффекты. Drag & drop

225

}); } break; case 'down' : // Если элемент не виден, не делаем ничего if (!slideMe.isVisible()) { // Раз мы сюда попали, значит, элемент виден slideMe.slideIn('t', { easing: 'easeOut', duration: .5 }); } break; default : // Переключаем видимость элемента на противоположную slideMe.toggle(); break } } Ext.get('textup').on('click',function(e,t){ // Заставляем элемент сворачиваться вверх slideText('up','slider'); Ext.get(t.id).frame('cccccc',1); }); Ext.get('textdown').on('click',function(e,t){ // Заставляем элемент сворачиваться вниз slideText('down','slider'); Ext.get(t.id).frame('cccccc',1); }); Ext.get('texttoggle').on('click',function(e,t){ // Переключаем видимость элемента на противоположную slideText('toggle','slider'); Ext.get(t.id).frame('cccccc',1); }); });

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


Часть IV. Библиотека ExtJS

226

Итак, в конце скрипта три раза вызывается метод Ext.get, каждый раз выбирая одну из ссылок вверху страницы. Методом on мы для каждой ссылки задаем обработчик события click. Обработчик вызывает функцию slideText с необходимыми параметрами и функцию frame, создающую серую (цвета #cccccc) подвижную рамку. Перейдем теперь выше по тексту, к функции slideText. Получив элемент, на котором произошло событие, мы быстренько записываем его в переменную slideMe. Дальше у нас три дороги: параметр direction может принимать одно из следующих значений — up down или toggle. Последняя — дорога по умолчанию. При выборе любой из трех дорог мы вначале проверяем, виден ли раздел слайдера, а потом выполняем необходимые в данном случае действия, вызывая функции slideIn, slideOut или toggle. Параметр t сообщает методам slideIn или slideOut, что эффект надо применять, начиная с верхнего края элемента. Можно было указать, например, l — для применения к левому краю. При задании конфигурационных параметров метода slideOut мы указали easing:'easeOut'. Это приводит к тому, что эффект длится полсекунды, элемент не удаляется из DOM и разрешает соседним элементам занять место, освободившееся после скрытия нашего элемента, создавая таким образом требуемый анимационный эффект.

Изменение размеров блока

Создадим блок, размеры которого можно менять, просто ухватившись за правую или нижнюю границу блока и потащив эту границу в нужную сторону (листинг 19.3). Листинг 19.3. Изменение размеров блока <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Resizable Examples</title> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"></script>


Глава 19. Визуальные эффекты. Drag & drop

227

<script type="text/javascript" src="ext-2.2/ext-all.js"></script> <script language="javascript" src="basic.js"></script> <link rel="stylesheet" type="text/css" href="examples.css" /> <style type="text/css"> #basic{ border: color:

1px solid #c3daf9; #1e4e8f;

font:

bold 14px tahoma,verdana,helvetica;

text-align:

center;

padding-top: 20px; } </style> </head> <body> <h1>Пример изменения размеров</h1> <p> <b>Базовый пример</b><br />

Для изменения размеров поместите курсор мыши вблизи правой или нижней границы элемента. </p> <div id="basic">Я могу измениться!</div> </body> </html>

Тут главное — не заблудиться среди стилевых таблиц и скриптов. Но если справитесь с правильным указанием путей, то наградой вам будет div, размер которого можно менять, ухватив и потащив за его край. Так, давайте двигаться дальше, добавляем сценарий (листинг 19.4). Листинг 19.4. Сценарий basic.js var ResizableExample = { init : function(){ var basic = new Ext.Resizable('basic', { width: 200, height: 100, minWidth:100,


Часть IV. Библиотека ExtJS

228 minHeight:50 }); } };

Ext.EventManager.onDocumentReady(ResizableExample.init, ResizableExample, true);

Для того чтобы можно было изменять размеры блока с идентификатором basic, надо создать объект Ext.Resizable, указав ему идентификатор и параметры блока. Мы задали исходные размеры (width и height) и те размеры, до которых можно уменьшать блок (minWidth и minHeight). В этом случае растягивать блок можно по всем направлениям и до сколь угодно большого размера на странице. Направление растяжения можно задать с помощью не указанного в приведенном примере параметра handles, который может ограничивать направление растяжения или сжатия, если задать ему значения из следующего списка: 'n' (north) — вверх по экрану; 's' (south) — вниз по экрану; 'e' (east) — можно двигать только правую границу; 'w' (west) — можно двигать только левую границу; 'nw' (northwest), 'sw' (southwest), 'se' (southeast), 'ne' (northeast) — попарно объединяем направление изменения размеров; 'all' — все, действует по умолчанию. Метод onDocumentReady(Function fn, [Object scope], [boolean options]) : void

вызывается, когда объектная модель загружаемого документа готова. В качестве первого параметра передаем функцию, которая инициализирует наш объект Ext.Resizable. С созданным объектом связываются события, которые положено для него обрабатывать, т. е. манипуляции мышью должны приводить к изменению размеров блока. Во втором параметре задаем контекст исполнения. Имеется в виду, что надо указать тот объект, для которого будет обрабатываться событие нажатия и движения мыши. И, наконец, задание значения true для третьего параметра приведет к тому, что к объекту будут применяться обработчики событий. Все! Вид блока дан на рис. 19.2.


Глава 19. Визуальные эффекты. Drag & drop

229

Рис. 19.2. Изменение размеров блока

Drag & drop Создадим блок-контейнер, содержащий несколько вложенных в него блоков. Зададим для них идентификаторы и стили, как представлено в листинге 19.5. Листинг 19.5. Страница с перетаскиваемыми элементами (drag &drop) <!DOCTYPE HTML PUBLIC "-// W3C// DTD HTML 4.01 Transitional// EN" "http:// www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"> <link rel="stylesheet" type="text/css" href="Ext.ux.IconCombo.css"> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script> <script type="text/javascript" src="ext-2.2/ext-all-debug.js"> </script> <script type="text/javascript" src="dd.js"></script> <script type="text/javascript">Ext.onReady(Tutorial.dd.init, Tutorial.dd); </script>


Часть IV. Библиотека ExtJS

230 <title> Drag & Drop Tutorial</title> <style type="text/css"> body { font-size: 11px; } .dd-ct { position:absolute; border: 1px solid silver; width: 180px; height: 180px; top: 32px; background-color: #ffffc0; } #dd1-ct { left: 64px; } #dd2-ct { left: 256px; } .dd-item { height: 18px; border: 1px solid #a0a0a0; background-color: #c4d0ff; vertical-align: middle; cursor: move; padding: 2px; z-index: 1000; } .dd-ct .dd-item { margin: 2px; } .dd-proxy { opacity: 0.4; -moz-opacity: 0.4; filter: alpha(opacity=40); } .dd-over { background-color: #ffff60; }


Глава 19. Визуальные эффекты. Drag & drop

231

</style> </head> <body> <div class="dd-ct" id="dd1-ct"> <div class="dd-item" id="dd1-item1">Элемент 1.1</div> <div class="dd-item" id="dd1-item2">Элемент 1.2</div> <div class="dd-item" id="dd1-item3">Элемент 1.3</div> </div> </body> </html>

Итак, у нас есть один контейнер класса dd-ct и три раздела div, которые можно будет перетаскивать. Теперь — JavaScript-код (листинг 19.6). Для начала задаем пространство имен для наших методов. Листинг 19.6. Сценарий dd.js для организации перетаскивания блоков Ext.BLANK_IMAGE_URL = 'ext-2.2/resources/images/default/s.gif'; Ext.namespace('Tutorial'); Tutorial.dd = function() { return { // Общедоступные методы init: function() { var dd11 = Ext.get('dd1-item1'); dd11.dd = new Ext.dd.DDProxy('dd1-item1', 'group'); var dd12 = Ext.get('dd1-item2'); dd12.dd = new Ext.dd.DDProxy('dd1-item2', 'group'); var dd13 = Ext.get('dd1-item3'); dd13.dd = new Ext.dd.DDProxy('dd1-item3', 'group'); } }; }();

Обратите внимание, что в начале скрипта задается пространство имен для последующих функций. Класс dd реализует технологию drag & drop, добавляя в документ элемент, который можно перемещать по экрану движением курсора мыши. Метод DDProxy выполняет основную работу, указывая в первом параметре идентификатор элемента, который надо сделать перетаскиваемым. Вот так мы и работаем!


232

Часть IV. Библиотека ExtJS

Усложним нашу задачу. Допустим, нам надо иметь два контейнера с элементами, перетаскивать и бросать блоки в любом месте страницы, но в случае если блок сброшен над каким-то контейнером, он аккуратно размещается в нем автоматически. Добавляем в предыдущую страницу еще одну область с перетаскиваемыми элементами (листинг 19.7). Листинг 19.7. Страница с двумя контейнерами перетаскиваемых элементов <!DOCTYPE HTML PUBLIC "-// W3C// DTD HTML 4.01 Transitional// EN" "http:// www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css"> <link rel="stylesheet" type="text/css" href="Ext.ux.IconCombo.css"> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"> </script> <script type="text/javascript" src="ext-2.2/ext-all-debug.js"> </script> <script type="text/javascript" src="dd2.js"></script> <script type="text/javascript">Ext.onReady(Tutorial.dd.init, Tutorial.dd);</script> <title>Custom Drag & Drop Tutorial</title> <style type="text/css"> // Те же стили, что и в предыдущем примере </style> </head> <body> <div class="dd-ct" id="dd1-ct"> <div class="dd-item" id="dd1-item1">Элемент 1.1</div> <div class="dd-item" id="dd1-item2">Элемент 1.2</div> <div class="dd-item" id="dd1-item3">Элемент 1.3</div> <div class="dd-ct" id="dd2-ct"> <div class="dd-item" id="dd2-item1">Item 2.1</div> <div class="dd-item" id="dd2-item2">Item 2.2</div> <div class="dd-item" id="dd2-item3">Item 2.3</div> </div> </div> </body> </html>


Глава 19. Визуальные эффекты. Drag & drop

233

Изменяем и скрипт (листинг 19.7), добавив в него метод override, который перегружает существующие методы, добавляя их измененный код в прототип используемого класса Ext.dd.DDProxy (листинг 19.8). В этом классе определены абстрактные методы, которые в этом сценарии и будут перегружены. Вот эти методы: startDrag(X, Y) — абстрактный метод, вызываемый после того, как пользователь щелкнул по перетаскиваемому объекту; параметры — координаты события; onDragOut(Event e, String|DragDrop[] id) — абстрактный метод, вызываемый после того, как событие hover завершилось; onDragOver(Event e, String|DragDrop[] id) — абстрактный метод, вызываемый, когда текущий элемент находится над другим перетаскиваемым элементом. Второй параметр у этого и предыдущего методов — элемент или идентификатор элемента; endDrag(Event e) — вызывается при завершении перетаскивания. Последние три метода в качестве первого параметра принимают произошедшее событие. Листинг 19.8. Сценарий dd2.js Ext.BLANK_IMAGE_URL = 'ext-2.2/resources/images/default/s.gif'; Ext.namespace('Tutorial'); Ext.override(Ext.dd.DDProxy, { startDrag: function(x, y) { var dragEl = Ext.get(this.getDragEl()); var el = Ext.get(this.getEl()); dragEl.applyStyles({border:'10px','z-index':2000}); dragEl.update(el.dom.innerHTML); dragEl.addClass(el.dom.className + ' dd-proxy'); }, onDragOver: function(e, targetId) { if ('dd1-ct' === targetId || 'dd2-ct' === targetId) { var target = Ext.get(targetId); this.lastTarget = target; target.addClass('dd-over'); } }, onDragOut: function(e, targetId) {


Часть IV. Библиотека ExtJS

234

if ('dd1-ct' === targetId || 'dd2-ct' === targetId) { var target = Ext.get(targetId); this.lastTarget = null; target.removeClass('dd-over'); } }, endDrag: function() { var dragEl = Ext.get(this.getDragEl()); var el = Ext.get(this.getEl()); if (this.lastTarget) { // Добавляем вложенный элемент Ext.get(this.lastTarget).appendChild(el); el.applyStyles({position:'', width:''}); } else { // Стилизуем и позиционируем элемент el.applyStyles({position:'absolute'}); el.setXY(dragEl.getXY()); el.setWidth(dragEl.getWidth()); } Ext.get('dd1-ct').removeClass('dd-over'); Ext.get('dd2-ct').removeClass('dd-over'); } }); Tutorial.dd = function() { return { // Общедоступные методы init: function() { // Области сбрасывания var dz1 = new Ext.dd.DropZone('dd1-ct', {ddGroup:'group'}); var dz2 = new Ext.dd.DropZone('dd2-ct', {ddGroup:'group'}); // Контейнер 1

var dd11 = Ext.get('dd1-item1'); // Отбираем элементы по id dd11.dd = new Ext.dd.DDProxy('dd1-item1', 'group'); var dd12 = Ext.get('dd1-item2'); dd12.dd = new Ext.dd.DDProxy('dd1-item2', 'group'); var dd13 = Ext.get('dd1-item3'); dd13.dd = new Ext.dd.DDProxy('dd1-item3', 'group'); // Контейнер 2 var dd21 = Ext.get('dd2-item1');


Глава 19. Визуальные эффекты. Drag & drop

235

dd21.dd = new Ext.dd.DDProxy('dd2-item1', 'group'); var dd22 = Ext.get('dd2-item2'); dd22.dd = new Ext.dd.DDProxy('dd2-item2', 'group'); var dd23 = Ext.get('dd2-item3'); dd23.dd = new Ext.dd.DDProxy('dd2-item3', 'group'); } }; }();

Мы создали две зоны (dz1 и dz2) для сбрасывания элементов из тех же контейнеров, которыми оперировали ранее. Кроме собственно перетаскивания сценарий применяет к блокам на различных этапах различные стили. В результате усовершенствований сценария блоки можно перетаскивать из их контейнера и помещать в любом месте на странице. При перетаскивании элемента по контейнеру последний вспыхивает более ярким цветом. На время перетаскивания блок бледнеет. Блок можно бросить и в исходный контейнер. Если блок сброшен чуть мимо контейнера, он автоматически сам аккуратно встраивается в контейнер. Вот как это выглядит в браузере (рис. 19.3).

Рис. 19.3. Перетаскиваемые элементы


Глава 20

Простые виджеты Авторы уверены, что термин "виджеты" лучше всего переводится на русский язык словом "прибамбасы", но едва ли удастся убедить какого-либо литературного редактора, что подобный термин уместен в тексте данной книги. Поэтому в данной книге так и применяется калька с английского слова widgets. Рассмотрим создание простых виджетов с помощью библиотеки ExtJs.

Всплывающие подсказки

Класс подсказок ToolTip расширяет класс Ext.Panel и дает возможность выводить на экран дополнительную информацию при наведении курсора на какой-либо элемент. HTML-документ, в котором работают подсказки, приведен в листинге 20.1. Листинг 20.1. HTML-документ с подсказками <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Tips Example</title> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-2.2/ext-all.js"></script> <script type="text/javascript" src="qtips.js"></script>


Глава 20. Простые виджеты

237

<style type="text/css"> .tip-target { width: 100px; text-align:center; padding: 5px 0; border:1px dotted #99bbe8; background:#dfe8f6; color: #15428b; cursor:default; margin:10px; font:bold 11px tahoma,arial,sans-serif; float:left; } </style> <link rel="stylesheet" type="text/css" href="examples.css" /> </head> <body> <h1>Примеры всплывающих подсказок</h1> <h3>Самая простая подсказка</h3>

<div id="tip1" class="tip-target">Базовая подсказка</div>

<div id="tip2" class="tip-target">Запрещено автоматически скры-

вать</div>

<div id="ajax-tip" class="tip-target">Подсказка с Ajax-запросом</div> <div id="track-tip" class="tip-target">Отслеживание курсора</div>

<div id="tip4" class="tip-target" ext:qtip="Мой QuickTip">Быстрая подсказка QuickTip</div> </body> </html>

В HTML-документе мы создали несколько блоков, указав им идентификаторы. В листинге 20.2 можно полюбоваться на код JavaScript, который и создает подсказки. Перечислим используемые в примере конфигурационные параметры объекта класса Ext.ToolTip: target — HTML-элемент, объект Ext.Element или идентификатор элемента, для которого требуется всплывающая подсказка; width — ширина всплывающего элемента в пикселах; autoLoad — URL загружаемого AJAX-запросом документа. Содержимое документа будет отображено в подсказке;


Часть IV. Библиотека ExtJS

238

— сколько миллисекунд надо подождать перед тем, чтобы закрыть подсказку (по умолчанию 5000); dismissDelay

— фрагмент HTML-документа, который используется в качестве подсказки; html

title

— текст в заголовке панели подсказки;

— значение true приведет к тому, что подсказка скроется после ухода курсора с целевого элемента или после истечения времени, указанного в dismissDelay; autoHide

— значение true приведет к включению кнопки закрытия подсказки (по умолчанию false); closable

draggable

— true для разрешения перетаскивать подсказку;

trackMouse

— true для разрешения подсказке двигаться вслед за кур-

сором.

Листинг 20.2. Сценарий qtips.js Ext.onReady(function(){ new Ext.ToolTip({ target: 'tip1', html:

'Очень простая подсказка'

}); new Ext.ToolTip({ target: 'ajax-tip', width:

200,

// Запрашиваем файл для отображения autoLoad:

{url: 'ajax-tip.html'},

dismissDelay: 15000 // Автоматически прятать через 15 сек }); new Ext.ToolTip({ target: 'tip2', html: 'Щелкни X, чтобы закрыть меня', title: 'My Tip Title', // Не прятать подсказку после ухода мыши с объекта autoHide: false, closable: true, draggable:true }); new Ext.ToolTip({


Глава 20. Простые виджеты

239

target: 'track-tip', title: 'Mouse Track', width:200, html: 'Эта подсказка будет следовать за курсором, пока он находится на элементе', trackMouse:true }); Ext.QuickTips.init(); });

Подсказка, возникающая около блока с идентификатором ajax-tip, отправляет AJAX-запрос к странице ajax-tip.html (там лежит HTML-фрагмент "<b>Подсказка Ajax</b><br />") и отображает ответ сервера. Всплывающие подсказки показаны на рис. 20.1.

Рис. 20.1. Всплывающие подсказки

Вот так! Едва ли какие-либо комментарии еще требуются.


Глава 21

Создание редактируемых таблиц Редактируемая таблица (grid) — одно из самых красивых и мощных средств визуализации данных в ExtJS. Данные для такой таблицы считываются из базы, размещаются на странице в рамках таблицы, оснащенной средствами прокрутки, сортировки, динамического управления видимостью отдельных столбцов, редактирования информации с записью обновлений в базу данных и т. д. Мы рассмотрим некоторые возможности работы с такими таблицами в этой главе.

Создание базы данных Для начала создадим базу данных, информация из которой будет отображаться на странице в виде таблицы. База называется tutorial (листинг 21.1) и состоит из двух таблиц: parties (перечень партий в разные эпохи в США) и presidents (список президентов США, пишем по-русски в кодировке UTF-8).

П РИМЕЧАНИЕ

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

Листинг 21.1. Создание базы данных tutorial CREATE TABLE IF NOT EXISTS `parties` ( `IDparty` int(11) NOT NULL auto_increment, `name`

varchar(40) NOT NULL,

PRIMARY KEY (`IDparty`) ); INSERT INTO `parties` (`IDparty`, `name`) VALUES


Глава 21. Создание редактируемых таблиц

241

(1, 'Нет'),

(2, 'Федералисты'),

(3, 'Демократы-Республиканцы'), (4, 'Демократы'), (5, 'Виги'),

(6, 'Республиканцы'); CREATE TABLE IF NOT EXISTS `presidents` ( `IDpresident` int(11) NOT NULL auto_increment, `IDparty` int(11) NOT NULL, `firstname` varchar(20) NOT NULL, `lastname` varchar(20)

NOT NULL,

`tookoffice` date NOT NULL, `leftoffice` date NOT NULL, `income` decimal(14,2) NOT NULL, PRIMARY KEY

(`IDpresident`)

); INSERT INTO `presidents` (`IDpresident`, `IDparty`, `firstname`, `lastname`, `tookoffice`, `leftoffice`, `income`) VALUES (1, 1, 'Джордж', 'Вашингтон', '1789-04-30', '1797-03-04', 135246.32), . . . (40, 6, 'Рональд', 'Рейган', '1981-01-20', '1989-01-20', 99867297.35),

(41, 6, 'Джордж', 'Буш', '1989-01-20', '1993-01-20', 92048204.24),

(42, 4, 'Билл', 'Клинтон', '1993-01-20', '2001-01-20', 12073975.24);

Серверный сценарий для запроса к базе и генерации ответа клиенту Затем напишем сценарий, запрашивающий эти данные и передающий их клиенту (листинг 21.2). Исходим из того, что в нашем распоряжении находится модуль PHP версии 5.2 или более новый, что позволяет применить функцию json_encode для передачи данных в формате JSON клиенту.

Листинг 21.2. Сценарий database.php — запрос данных из базы <?php $link = mysqli_connect("localhost", "root", "secret") or die("Could not connect: " . mysqli_error()); mysqli_select_db($link,"tutorial"); // Проверяем, что сценарий запрошен с определенным параметром


Часть IV. Библиотека ExtJS

242 $task = ''; if ( isset($_POST['task'])){ $task = $_POST['task']; } switch($task){ case "LISTING": getList(); break; default: echo "{failure:true}"; break; } function getList() { global $link;

$query = "SELECT * FROM presidents pr, parties pa WHERE pr.IDparty = pa.IDparty"; $result = mysqli_query($link,$query); $nbrows = mysqli_num_rows($result); if($nbrows>0){ while($rec = mysqli_fetch_array($result)){ // Преобразуем в формат даты ExtJS $rec['tookoffice']=codeDate($rec['tookoffice']); $rec['leftoffice']=codeDate($rec['leftoffice']); $arr[] = $rec; } $jsonresult = json_encode($arr); echo '({"total":"'.$nbrows.'","results":'.$jsonresult.'})'; } else { echo '({"total":"0", "results":""})'; } } // YYYY-DD-MM превращаем в DD/MM/YYYY function codeDate ($date) { $tab = explode ("-", $date); $r = $tab[1]."/".$tab[2]."/".$tab[0]; return $r; }


Глава 21. Создание редактируемых таблиц

243

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

Клиентская часть: HTML и сценарий JavaScript HTML-документ будет выглядеть так, как представлено в листинге 21.3.

Листинг 21.3. Страница с редактируемой таблицей <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; UTF-8" /> <title>Tutorial: Grid</title> <link rel="stylesheet" type="text/css" href="resources/css/ext-all.css"/> <script type="text/javascript" src="adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-all.js"></script> <script type="text/javascript" src="mainscript.js"></script> </head> <body> </body> </html>

Здесь главное — не забыть подключить все необходимое. Далее в этой главе не будут приводиться листинги соответствующих HTML-документов, т. к. они различаются только именами JavaScript-сценариев, на которые ссылаются. Мы просто будем указывать имена файлов HTML-документов, под которыми читатель найдет их на прилагаемом к книге компакт-диске, также каждый раз укажем, ссылка на какой сценарий JavaScript стоит в этом HTMLдокументе. Наконец, опишем самую интересную часть нашего кода: создание сценария и генерацию в нем таблицы (листинг 21.4).


Часть IV. Библиотека ExtJS

244

Листинг 21.4. Сценарий mainscript.js var PresidentsDataStore; // Задаем имена создаваемых объектов var PresidentsColumnModel; var PresidentListingEditorGrid; var PresidentListingWindow; Ext.onReady(function(){ /* Активизируем всплывающие подсказки, доступные для объектов,

которые мы будем создавать. */

Ext.QuickTips.init(); /* Класс data.Store организует массивы для хранения данных.

Объект такого класса может обрабатывать данные из любого источника, например, из XML-документов, данных в формате JSON. Класс data.Store создает записи, который могут отображаться в различных видах, например, как содержимое таблицы. */

PresidentsDataStore = new Ext.data.Store({ id: 'PresidentsDataStore', /* AJAX-запрос данных из серверного скрипта. Можно обращаться

только к тому же домену, откуда пришла HTML-страница. */

proxy: new Ext.data.HttpProxy({ url: 'database.php', method: 'POST' }), // Параметр task передается в HTTP-запросе baseParams:{task: "LISTING"}, /* Класс Ext.data.JsonReader позволяет создать массив объектов Ext.data.Record из данных ответа в формате JSON. */

reader: new Ext.data.JsonReader({ // Зададим свойство, содержащее массив объектов строк root: 'results', // Свойство, из которого можно узнать, сколько записей // в наборе данных

totalProperty: 'total', id: 'id' },[ /* Формирование свойств для JSON-объекта.

Задаем имя свойства, тип и указываем объект, из которого берем данные */

{name: 'IDpresident', type: 'int',

mapping: 'IDpresident'},


Глава 21. Создание редактируемых таблиц

245

{name: 'FirstName',

type: 'string', mapping: 'firstname'},

{name: 'LastName',

type: 'string', mapping: 'lastname'},

{name: 'IDparty',

type: 'int',

{name: 'PartyName',

type: 'string', mapping: 'name'},

{name: 'TookOffice',

type: 'date',

mapping: 'tookoffice'},

{name: 'LeftOffice',

type: 'date',

mapping: 'leftoffice'},

{name: 'Income',

type: 'float',

mapping: 'income'}

mapping: 'IDparty'},

]), // Сортируем поле по возрастанию sortInfo:{field: 'IDpresident', direction: "ASC"} }); /* Класс ColumnModel определяет, как задаются столбцы таблицы,

т. е. каков тип данных столбцов, можно ли редактировать эти данные и пр. */

PresidentsColumnModel = new Ext.grid.ColumnModel( [{ header:

'#', // Это поле не показываем

readOnly:

true,

dataIndex: 'IDpresident', width:

50,

hidden:

false

},{ header:

'Имя',

dataIndex: 'FirstName', width:

60,

/* Задаем текстовые поля, описываем в них маску, которая будет

фильтровать данные перед их отображением. Маска создается в виде шаблона регулярных выражений. */ /* Задаем параметры поля формы, т. е. объекта, который позволяет обрабатывать связанные с ним события, менять размер поля, управление вводимыми данными и пр. */ editor: new Ext.form.TextField({ allowBlank: false, maxLength:

20,

maskRe:

/([a-zA-Z0-9\s]+)$/ // Маска для текстового поля

}) },{ header:

'Фамилия',

dataIndex: 'LastName', width:

80,


Часть IV. Библиотека ExtJS

246 editor:

new Ext.form.TextField({

allowBlank: false, maxLength:

20,

maskRe:

/([a-zA-Z0-9\s]+)$/

}) },{ header: readOnly:

'ID party', true,

dataIndex: 'IDparty', width:

50,

hidden:

true

},{ header:

'Партия',

dataIndex: 'PartyName', width:

150,

readOnly:

true

},{ header:

'Пост принял',

dataIndex: 'TookOffice', width:

80,

// Зададим функцию отображения даты, которая может быть // вызвана в дальнейшем снова для форматирования даты. renderer: Ext.util.Format.dateRenderer('d.m.Y'), editor: format:

new Ext.form.DateField({ 'd-m-Y'

}), hidden: false },{ header:

'Пост сдал',

dataIndex: 'LeftOffice', width:

80,

renderer:

Ext.util.Format.dateRenderer('d.m.Y'),

editor: new Ext.form.DateField({ format: 'd.m.Y' }), hidden:

false

header:

"Доход",

},{ dataIndex: 'Income',


Глава 21. Создание редактируемых таблиц

247

width: 150, renderer: function(v){ return '$ ' + v; }, editor: new Ext.form.NumberField({ allowBlank: false, allowDecimals: true, allowNegative: false, blankText: '0', maxLength: 11 }) }] ); PresidentsColumnModel.defaultSortable= true; /* Наконец, все связываем вместе, создавая редактируемую таблицу с данными. */ PresidentListingEditorGrid = new Ext.grid.EditorGridPanel({ id: 'PresidentListingEditorGrid', store: PresidentsDataStore, cm: PresidentsColumnModel, // Задание модели для столбцов enableColLock: false, clicksToEdit: 1, selModel: new Ext.grid.RowSelectionModel({singleSelect:false}) }); /* Задаем Window - особую панель для размещения содержимого. Можно менять размеры этой панели, перетаскивать ее по экрану, группировать с другими объектами этого же класса так, чтобы создавать эффект наложения панелей одну на другую и пр. */ PresidentListingWindow = new Ext.Window({ id: 'PresidentListingWindow', title: 'Президенты США', closable: true, width: 700, // Ширина окна height: 350, // Высота окна plain: true, layout: 'fit', items: PresidentListingEditorGrid }); /* Загружаем кэш записей, полученных с сервера с помощью Proxy.


Часть IV. Библиотека ExtJS

248

При загрузке используем ранее сконфигурированный объект

Ext.data.JsonReader для обработки данных. */ PresidentsDataStore.load(); // Отображаем созданную панель Window PresidentListingWindow.show(); });

Для отображения редактируемой таблицы нужно иметь массив данных data.Store и модель отображения этих данных — объект Ext.grid.ColumnModel. Массив данных data.Store должен перезагружаться в случае внесения каких-либо изменений в базу данных. Следовательно, надо предусмотреть механизм отправки запроса к серверу. Класс Store создает на клиентской стороне кэш с записями данных, которые надо отображать в табличном виде. Объект Store использует сконфигурированный объект DataProxy для получения данных с сервера при помощи AJAX-запроса. Но сам объект Store не знает, в каком формате данные приходят с сервера. Объект Store использует сконфигурированный объект DataReader, в нашем примере для этого применяется JSONReader, для создания строк с данными. Эти данные получаются в результате обработки ответа сервера. Класс Data.Jsonreader создает массив записей, состоящих из объектов Ext.data.Record. Данные для записей берутся из JSON-ответа сервера и применяются в объекте на основании привязки, задаваемой конструктором Ext.data.Record (см. параметр mapping). Если вы хотите понять, каков ответ, приходящий с сервера, то поставьте в листинге 21.2 строку $task = LISTING; перед оператором switch и запустите этот сценарий отдельно. Часть того, что вы увидите на экране, будет выглядеть так: ({"total":"42", "results":[{"0":"1" ,"IDpresident":"1" ,"1":"1" ,"IDparty":"1" ,"2":"\u0414 . . . " ,"lastname":"\u0412 . . ." ,"4":"1789-04-30" ,"tookoffice":"04\/30\/1789" ,"5":"1797-03-04" ,"leftoffice":"03\/04\/1797" ,"6":"135246.32" ,"income":"135246.32"


Глава 21. Создание редактируемых таблиц

249

В этом фрагменте мы часть кодов русских букв заменили многоточием для удобочитаемости. Теперь, если вы соберете все вместе и обратитесь к файлу index.html, то к серверному сценарию database.php будет отправлен POST-запрос с параметром task = LISTING. Данные из базы будут переданы в клиентскую программу, обработаны, и вы увидите на экране следующее (рис. 21.1).

Рис. 21.1. Редактируемая таблица

Представление данных в таблице можно менять, выбирая опции, как указано на рисунке. Вы можете менять ширину столбцов, просто раздвигая их мышью, как при работе с таблицами в MS Excel, а также выбирать, какие столбцы следует спрятать, а какие отображать на экране. Можно даже редактировать значения полей, после редактирования они помечаются красным треугольником в левом верхнем углу, но не заносятся в базу данных на сервера. Следующий шаг — создать такую таблицу, в которой результаты редактирования полей будут сохраняться в базе данных, а значит, будут видны и после перезагрузки страницы.

Разработка динамически редактируемой таблицы Для обновления данных в базе в сценарий представленный в листинге 21.5.

PHP

надо добавить фрагмент,


Часть IV. Библиотека ExtJS

250

Листинг 21.5. Фрагмент сценария database2.php, обновляющий данные в базе switch($task){ case "LISTING": // Значение параметра task для запроса данных из базы getList(); break; case "UPDATEPRES": // Значение параметра task для обновления базы updatePresident(); break; default: echo "{failure:true}"; break; } // Функция обновления данных в базе function updatePresident() {global $link; $IDpresident = $_POST['IDpresident']; $FirstName = addslashes($_POST['FirstName']); $LastName = addslashes($_POST['LastName']); $PartyName = $_POST['PartyName']; $TookOffice = $_POST['TookOffice']; $LeftOffice = $_POST['LeftOffice']; $Income = $_POST['Income']; // Находим номер партии $IDparty $query = "SELECT IDParty FROM parties WHERE Name='".$PartyName."'"; $result = mysqli_query($link,$query); if(mysqli_num_rows($result)>0){ $arr = mysqli_fetch_array($result); $IDparty = $arr['IDParty']; } else { echo '0'; } // Инструкция обновления данных в таблице presidents $query = "UPDATE presidents SET firstname = '$FirstName', lastname = '$LastName', tookoffice = '$TookOffice', leftoffice = '$LeftOffice', IDparty = '$IDparty', income='$Income' WHERE IDpresident=$IDpresident"; $result = mysqli_query($link,$query); echo '1'; }


Глава 21. Создание редактируемых таблиц

251

Из приведенного только что фрагмента ясно, что, обратившись к серверному сценарию database2.php с параметром task = UPDATEPRES, можно записать новые данные в таблицу presidents. Для этого сами данные надо передать методом POST. Читатель найдет полный текст этого сценария в файле с именем database2.php на приложенном к книге компакт-диске. HTML-документ сохранен в файле index2.html и содержит ссылку на сценарий mainscript2.js в следующем теге: <script type="text/javascript" src="mainscript2.js"></script>

Посмотрим сам сценарий JavaScript (листинг 21.6), из которого, собственно, и передаются значения параметров для обновления базы данных. Сценарий этот непрост, и именно подобные сценарии содержат то, ради чего читатель и брал в руки эту книгу. Поэтому позволим себе привести его полностью. Листинг 21.6. Сценарий mainscript2.js для динамического обновления таблицы var PresidentsDataStore; var PresidentsColumnModel; var PresidentListingEditorGrid; var PresidentListingWindow; Ext.onReady(function(){ Ext.QuickTips.init(); // Функция сохранения изменений в базе данных function saveThePresident(oGrid_event){ // Создание AJAX-запроса для обновления данных в базе Ext.Ajax.request({ // Сообщение на период ожидания ответа waitMsg: 'Подождите, пожалуйста..', // Адрес запроса

url: 'database2.php', // Параметры запроса params: { task: "UPDATEPRES", // Имена полей для обновления таблицы в базе данных.

// Значение поля берется из элемента, на котором // произошло событие.

IDpresident: oGrid_event.record.data.IDpresident, FirstName: oGrid_event.record.data.FirstName,


Часть IV. Библиотека ExtJS

252

LastName: oGrid_event.record.data.LastName, PartyName: oGrid_event.record.data.PartyName, TookOffice: oGrid_event.record.data.TookOffice.format('Y-m-d'), LeftOffice: oGrid_event.record.data.LeftOffice.format('Y-m-d'), Income: oGrid_event.record.data.Income }, // Обработка в случае удачного завершения запроса success: function(response){ // Считываем ответ сервера var result=eval(response.responseText); switch(result){ case 1: // Выполняем обновление данных PresidentsDataStore.commitChanges(); // Обновляем данные в клиентском кэше PresidentsDataStore.reload(); break; default: // Выводим сообщение при невозможности записи обновления Ext.MessageBox.alert('Э-э-э...', 'Сохранить не удается...'); break; } }, failure: function(response){ var result=response.responseText; Ext.MessageBox.alert('error', 'не удается соединиться с сервером. Попытайтесь позже.'); } }); } // Формирование кэша данных, отображаемых в таблице PresidentsDataStore = new Ext.data.Store({ id: 'PresidentsDataStore', // Запрос данных с сервера proxy: new Ext.data.HttpProxy({ url: 'database2.php', method: 'POST' }), // Параметр, передаваемый в запросе HTTP baseParams:{task: "LISTING"},


Глава 21. Создание редактируемых таблиц

253

/* Обработка ответа сервера, приводящая к созданию объекта, содержащего данные ответа */ reader: new Ext.data.JsonReader({ // Задаем имя свойства, которое содержит массив литеральных объектов root: 'results', /* Задаем имя свойства, в котором хранится размер массива, заданного в предыдущем параметре */ totalProperty: 'total', // Имя свойства, в котором хранится номер литерального объекта id: 'id' },[ // Привязка полей ответа сервера к свойствам создаваемого объекта {name: 'IDpresident', type: 'int', mapping: 'IDpresident'}, {name: 'FirstName', type: 'string', mapping: 'firstname'}, {name: 'LastName', type: 'string', mapping: 'lastname'}, {name: 'IDparty', type: 'int', mapping: 'IDparty'}, {name: 'PartyName', type: 'string', mapping: 'name'}, {name: 'TookOffice', type: 'date', mapping: 'tookoffice'}, {name: 'LeftOffice', type: 'date', mapping: 'leftoffice'}, {name: 'Income', type: 'float', mapping: 'income'} ]), sortInfo:{field: 'IDpresident', direction: "ASC"} }); // Задание модели для формирования столбцов таблицы PresidentsColumnModel = new Ext.grid.ColumnModel( [{ // Столбец с номером записи, по умолчанию он не отображается header: '#', readOnly: true, // Значение этого поля нельзя менять dataIndex: 'IDpresident', // Откуда берем данные width: 40, hidden: false },{ // Столбец с именем президента header: 'Имя', dataIndex: 'FirstName', width: 60, // Задаем возможность редактирования значения поля editor: new Ext.form.TextField({ allowBlank: false, // Поле не должно быть пустым maxLength: 20, // Маска для ввода текста содержит английские и русские буквы


Часть IV. Библиотека ExtJS

254

maskRe: /([а-яА-Яa-zA-Z0-9\s]+)$/ }) },{ // Столбец с фамилией президента header: 'Фамилия', dataIndex: 'LastName', width: 80, editor: new Ext.form.TextField({ allowBlank: false, maxLength: 20, maskRe: /([а-яА-Яa-zA-Z0-9\s]+)$/ }) },{ // Столбец с номером партии, к которой принадлежит президент, // по умолчанию не отображается. header: 'ID party', readOnly: true, dataIndex: 'IDparty', width: 50, hidden: true },{ // Столбец с названием партии header: 'Party', dataIndex: 'PartyName', width: 150, /* Создание элемента интерфейса, позволяющего применять автодополнение поля, загрузку данных с сервера в это поле и другую функциональность, доступную объектам extJs */ editor: new Ext.form.ComboBox({ // Разрешаем автоподсказку данными из выпадающего списка typeAhead: true, /* Указываем, что по завершении редактирования надо выполнить запрос к серверу */ triggerAction: 'all', store: new Ext.data.SimpleStore({ fields: ['partyValue', 'partyName'], // Задание массива данных для выпадающего списка data: [['1','Нет'],['2','Федералисты'],['3','ДемократыРеспубликанцы'],['4','Демократы'],['5','Виги'],['6','Республиканцы']] }), /* Указываем, что в выпадающем списке будут отражены локальные данные, а не загруженные с удаленного хоста */ mode: 'local',


Глава 21. Создание редактируемых таблиц

255

displayField: 'partyName', valueField: 'partyValue', // Запрещаем отображать выпадающий список до щелчка по этому полю lazyRender:true, /* Определяем стилевой класс, который будет использован при отображении выпадающего списка */ listClass: 'x-combo-list-small' }) },{ // Дата вступления в должность header: 'Пост принял', dataIndex: 'TookOffice', width: 80, /* Форматирование даты. Задание функции, форматирующей дату в окне. Параметром служит формат даты. */ renderer: Ext.util.Format.dateRenderer('m/d/Y'), editor: new Ext.form.DateField({ /* Строка с форматом даты. Формат может быть изменен в соответствии с локализацией приложения. */ format: 'm/d/Y' }), hidden: false },{ // Дата отставки header: 'Пост сдал', dataIndex: 'LeftOffice', width: 80, renderer: Ext.util.Format.dateRenderer('m/d/Y'), editor: new Ext.form.DateField({ format: 'm/d/Y' }), hidden: false },{ // Доход президента header: 'Доход', dataIndex: 'Income', width: 100, // Задаем функцию, которая добавит значок доллара к сумме дохода renderer: function(v){ return '$ ' + v; }, editor: new Ext.form.NumberField({ // Задаем параметры поля, содержащего положительное // дробное число allowBlank: false, allowDecimals: true,


Часть IV. Библиотека ExtJS

256 allowNegative: false, blankText: '0', maxLength: 11 }) }] );

/* Разрешаем сортировку столбцов, для которых сортировка не задана по умолчанию */ PresidentsColumnModel.defaultSortable= true; // Создаем панель редактируемой таблицы PresidentListingEditorGrid = new Ext.grid.EditorGridPanel({ id: 'PresidentListingEditorGrid', // Указываем объект с данными для отображения store: PresidentsDataStore, // Задаем модель отображения столбцов cm: PresidentsColumnModel, enableColLock:false, clicksToEdit:1, selModel: new Ext.grid.RowSelectionModel({singleSelect:false}) }); // Создание окна для вывода таблицы PresidentListingWindow = new Ext.Window({ id: 'PresidentListingWindow', title: 'Президенты США', // Разрешаем закрывать таблицу щелчком по кнопке с крестиком closable: true, width: 700, height: 350, plain: true, layout: 'fit', // Задание компоновки // Указание объекта, из которого берутся данные для отображения items: PresidentListingEditorGrid }); // Сохранение данных в кэш и вывод данных на экран в новом окне PresidentsDataStore.load(); PresidentListingWindow.show(); // Задаем функцию-обработчик saveThePresident для события // afteredit - окончанию редактирования поля PresidentListingEditorGrid.on('afteredit', saveThePresident); });


Глава 21. Создание редактируемых таблиц

257

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

new Ext.grid.EditorGridPanel({

. . . , store: PresidentsDataStore, cm: PresidentsColumnModel, . . . });

Данные для таблицы загружаются из кэша, который хранится в объекте PresidentsDataStore. Данные попадают в кэш в результате запроса, отправляемого к серверному сценарию database2.php. При формировании кэша ответ сервера, приходящий в формате JSON, считывается и преобразуется в массив литеральных объектов. Столбцы таблицы организованы таким образом, что можно менять их вид, более того, можно изменить значение в столбце и записать это значение в серверную базу данных. Вся эта функциональность определяется объектом PresidentsColumnModel, в котором задаются все характеристики столбцов. Задание модели для формирования столбцов занимает самую большую часть рассматриваемого сценария. Здесь задаются имена столбцов и данные, которые следует загружать в них. Описываются опции редактирования и форматирования. Ну и, наконец, добравшись до самого верха программы, мы обнаруживаем то, о чем уже начали догадываться: функция saveThePresident обработки события отправляет на сервер запрос, обновляющий базу данных и вносящий изменения в отредактированные на клиентской стороне поля. Изучая в дальнейшем возможности редактируемых таблиц в ExtJS, читатель обнаружит, что эта глава является не полным их описанием, а лишь кратким предисловием к использованию этой интересной и очень перспективной библиотеки.


Часть V jQuery


Глава 22

Знакомство с jQuery jQuery — это JavaScript-библиотека, которая появилась в январе 2006 года благодаря стараниям Джона Ризига (John Resig) и команды jQuery и стала быстро завоевывать популярность и общее признание. На сегодняшний день ее используют Google и Mozilla, Dell и Bank of America, Wordpress, Drupal и многие другие компании. Библиотека совместима с большинством современных и популярных браузеров: IE 6.0+, FF 2+, Safari 2.0+, Opera 9.0+. В рамках этой книги не подразумевается детальное знакомство с JavaScriptбиблиотекой jQuery, но для того чтобы понимать и в полной мере использовать ее возможности для работы с AJAX-технологиями, необходимо общее понимание основ этой библиотеки. И с самого начала мы попробуем понять, что может дать эта библиотека разработчику. Библиотека позволяет повысить скорость разработки приложений, существенно упрощая работу с объектной моделью документа (DOM), созданием анимации, обработкой событий и конечно предоставляя прекрасные механизмы взаимодействия с AJAX. Но это только лозунги, а что на практике? А на практике библиотека jQuery почти полностью снимает с разработчика заботу об обеспечении совместимости программного кода с основными типами современных браузеров. Несомненно, очень важным моментом является и тот факт, что с помощью jQuery довольно просто отделить поведение элементов от структуры HTMLдокумента. Вы никогда не вешали обработчик события onclick непосредственно в HTML-разметке? Тогда вам очень повезло. Еще один важный момент — при использовании jQuery объем кода сокращается в разы! Стоит отметить, что в библиотеке предусмотрена возможность расширения функциональности, чем не замедлили воспользоваться многие разработчики. На момент написания книги количество плагинов к библиотеке jQuery перевалило далеко за тысячу.


Часть V. jQuery

262

Аргументов за то, чтобы заинтересоваться jQuery, пожалуй, уже достаточно. Если интересно — продолжим и попробуем разобраться, как же это работает? Первое, что делает практически любой jQuery-код, — выбирает элементы HTML-страницы в объект jQuery, который мы в дальнейшем будем называть набором элементов, для дальнейших манипуляций ими. Именно поэтому мы довольно подробно познакомимся не только с функциями ядра библиотеки, но и с разделом, рассказывающим о селекторах jQuery. Затем немного познакомимся с основными и наиболее востребованными в практическом программировании методами, с помощью которых можно легко манипулировать элементами объектной модели документа (а иначе, зачем было вообще создавать какие-то наборы?), посвятим некоторое время изучению событий jQuery и узнаем, как связывать наборы элементов jQuery c определенными событиями. И, наконец, уже понимая, как легко можно с помощью библиотеки jQuery воплощать в жизнь самые интересные задумки, приступим к изучению API (интерфейса программирования приложений) для работы с AJAX, который предлагает jQuery. В заключение мы разберем несколько совершенно реальных примеров, познакомившись через них с возможностью использовать множество написанных для jQuery плагинов. Не страшно, если пока что-то звучит несколько непонятно — все примеры, приведенные в книге, очень просты. Разобрав несколько примеров, вы с удивлением обнаружите, что уже и сами в состоянии написать какой-либо код, используя библиотеку jQuery.

Установка библиотеки

Практическое знакомство с библиотекой мы начнем с процесса ее установки, хотя это и звучит несколько громко. Для того чтобы подключить одинединственный файл, достаточно написать код, приведенный в листинге 22.1. Листинг 22.1. Подключение библиотеки jQuery <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Подключение библиотеки jQuery</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />


Глава 22. Знакомство с jQuery

263

<script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> </body> </html>

Как показано в листинге 22.1, библиотека jQuery подключается к нужной странице внешним файлом. Скачать самую последнюю версию библиотеки можно на сайте разработчиков по адресу: http://docs.jquery.com/Downloading_jQuery.

Что такое $()? — это псевдоним функции jQuery(). Возвращает специальный объект JavaScript — объект jQuery, содержащий массив элементов объектной модели документа (DOM), которые соответствуют указанному селектору. Такой объект (договоримся называть его набором элементов) имеет большое количество методов, которые воздействуют на каждый элемент этого набора. $()

Но давайте на одном примере ближе познакомимся с функцией $(), увидим своими глазами селектор, узнаем о событии ready и напишем несколько строк, с которых начинается практически любой код jQuery. $(document).ready(function () {... })

В этом подзаголовке мы видим функцию $(), которой в качестве селектора передан объект document. Другими словами, мы создали набор элементов, который содержит элементы объектной модели документа, и затем связали этот набор с событием ready(fn), имеющимся в арсенале библиотеки jQuery. С событием ready(fn) можно связать функцию, которая должна быть выполнена всякий раз, когда объектная модель документа (DOM) готова к обходу и манипуляциям с ее элементами. Лучше пояснить это на простейшем примере (листинг 22.2). Листинг 22.2. Использование события ready(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование события ready(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script>


264

Часть V. jQuery

</head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ alert("Hello, world!"); }); --> </script> </body> </html>

В примере, приведенном в листинге 22.2, в момент готовности объектной модели документа к работе, мы увидим знакомое сообщение: "Hello, world!". Это, вероятно, наиболее важная функция, включенная в модуль событий, т. к. она позволяет существенно уменьшить время отклика Web-приложения. Это хорошая замена использованию window.onload. Применение этого метода позволит вызывать необходимую функцию непосредственно в момент готовности объектной модели документа (DOM) к работе. То есть пользователю не придется ожидать окончания загрузки каких-либо данных с удаленных ресурсов (баннеры, счетчики и т. п.), чтобы приступить к работе с вашим приложением. Вот поэтому практически любой код jQuery и начинается с тех нескольких строк, которые были приведены в листинге 22.2. На странице можно использовать сколько угодно конструкций $(document).ready(). Но в этом случае необходимо учесть, что выполняться код будет в порядке его следования. И еще один важный момент: необходимо убедиться, что элемент body не имеет обработчика события onload, в противном случае конструкция $(document).ready() может и не сработать.


Глава 23

Функции ядра jQuery Операции обхода и преобразования объектной модели документа начинаются, как правило, с отыскания необходимых элементов. Если в "классическом" JavaScript для выбора элемента мы бы использовали, например, getElementsByTagName или getElementById, то в jQuery используется синтаксис, являющийся "гибридом" CSS и регулярных выражений. Говоря о функциях ядра jQuery, мы имеем в виду самые важные и наиболее часто используемые функции библиотеки. jQuery(expression, [context])

Эта функция принимает строку, содержащую CSS-селектор, который используется, чтобы создать набор элементов. Функция возвращает объект jQuery. Вокруг этой функции сконцентрирована вся функциональность ядра библиотеки jQuery. Параметры: expression — строка, содержащая поисковое выражение; context (необязательный аргумент) — элементы DOM, объекты document или jQuery, используемые для поиска в их контексте. В большинстве случаев, именно эта функция применяется для поиска элемента или набора элементов. По умолчанию, если не передан аргумент context, jQuery рассматривает элементы DOM в контексте всего HTMLдокумента. Если же аргумент context будет определен, например как элемент DOM или объект jQuery, то в соответствии с выражением, будут выбираться элементы именно в указанном контексте. Рассмотрим пример (листинг 23.1). Листинг 23.1. Использование функции jQuery(expression,

[context]) (вариант 1)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru">


266

Часть V. jQuery

<head> <title>Использование функции jQuery(expression, [context])</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <p>Один</p>

<div><p>Два</p></div> <p>Три</p>

<script type="text/javascript"> <!-$(document).ready(function(){ $("div > p").css("border", "1px solid #999"); }); --> </script> </body> </html>

Код, приведенный в листинге 23.1, отберет все элементы p, являющиеся прямыми потомками элементов div, и установит для них серую рамку толщиной в 1 пиксел. То есть в нашем случае серая рамка будет установлена для элемента p, в котором содержится текст "Два". Вы наверняка уже узнали функцию $(). Селектором в данном случае является выражение div > p — если вы даже немного знакомы с CSS, тут тоже все будет понятно. Дальше, с помощью одного из многочисленных методов jQuery мы воздействуем на CSS-свойство border всех элементов, которые были отобраны в набор. Теперь рассмотрим случай с передачей обоих возможных аргументов (листинг 23.2). Листинг 23.2. Использование функции jQuery(expression, (вариант 2)

[context])

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование функции jQuery(expression, [context])</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script>


Глава 23. Функции ядра jQuery

267

<style type="text/css"> form { border:1px dotted #00f; } </style> </head> <body> <form> <input type="text" /> <input type="checkbox" /> <input type="file" /> <input type="radio" /> <input type="text" /> </form> <form> <input type="text" /> <input type="checkbox" /> <input type="file" /> <input type="radio" /> <input type="text" /> </form> <script type="text/javascript"> <!-$(document).ready(function(){ $("input:text", document.forms[0]).css("background-color","#099"); }); --> </script> </body> </html>

Код, приведенный в листинге 23.2, реализует две абсолютно одинаковые формы, которые содержат элементы input разных типов. Наша задача — отыскать только поля для ввода текста, причем поиск должен осуществляться в контексте первой формы на текущей странице. Передавая в первом аргументе expression значение input:text, мы указываем, что именно мы хотим отыскать на странице. Передавая же во втором аргументе context значение document.forms[0], мы указываем, где именно в контексте какого элемента необходимо осуществить поиск. В заключение мы установим для отобранных элементов CSS-свойство background-color. В результате выполнения


Часть V. jQuery

268

этого кода, первый и пятый элементы input только в первой форме будут иметь бирюзовый цвет фона. jQuery(html)

Эта функция создает элементы DOM "на лету", используя переданную в качестве аргумента строку, содержащую HTML-код. Единственный параметр — html. Это строка, содержащая HTML-код для создания элементов DOM. Функции можно передавать строку HTML-кода как написанную "руками", так и созданную автоматически, например шаблонными движками или плагинами. Строку HTML-кода можно передавать в функцию и через AJAX. Пример представлен в листинге 23.3.

Листинг 23.3. Использование функции jQuery(html) (вариант 1) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование функции jQuery(html)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ $("<div><p>Hello, world!</p></div>").appendTo("body"); }); --> </script> </body> </html>

В листинге 23.3 приведен код, который создает элемент div и внутри него параграф со знакомым выражением "Hello, world!". Затем созданные элементы добавляются в элемент body. И опять все начинается с функции $(). Только здесь ей передается не селектор, а строка HTML-кода.


Глава 23. Функции ядра jQuery

269

Необходимо отметить некоторые ограничения, существующие при создании элементов input (листинг 23.4). Листинг 23.4. Использование функции jQuery(html) (вариант 2) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование функции jQuery(html)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ // Неправильно: // $("<input/>").attr("type", "checkbox").appendTo("body"); // Правильно: $("<input type='checkbox'/>").appendTo("body"); }); --> </script> </body> </html>

В листинге 23.4 приведены примеры правильного и неправильного (строка закомментирована) кода при создании элементов input. При создании элемента input необходимо указать его тип, в противном случае, попытка установить тип элемента input после его создания приведет к возникновению ошибки. Существует ограничение на передачу в качестве аргумента строки, которая может содержать слэши, например путь к файлу изображения. В этом случае необходимо использовать предваряющий "обратный" слэш. jQuery(elements)

Эта функция "обертывает" функциональность jQuery вокруг одного или многих элементов DOM. Принимает в качестве аргумента elements элемент или


Часть V. jQuery

270

массив элементов. В качестве аргумента могут быть переданы также и XMLдокументы. Рассмотрим пример (листинг 23.5). Листинг 23.5. Использование функции jQuery(elements) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование функции jQuery(elements)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ $(document.body).css("background-color", "#000"); }); --> </script> </body> </html>

Здесь мы устанавливаем значение #000 для CSS-свойства background-color элемента body, т. е., попросту говоря, устанавливаем черный цвет фона страницы. jQuery(callback)

Это — просто короткая форма записи для $(document).ready(). Позволяет связать выполнение функции с моментом окончания загрузки объектной модели документа (DOM).

Доступ к объекту jQuery Библиотека предлагает несколько методов для доступа к элементам объекта jQuery. Мы разберем только наиболее часто используемые из них.


Глава 23. Функции ядра jQuery

271

each(callback)

Принимает в качестве аргумента функцию, которая будет выполняться в контексте каждого элемента объекта jQuery. В выполняющуюся функцию в качестве единственного аргумента передается позиция элемента. Все станет гораздо понятнее, если внимательно изучить пример, приведенный в листинге 23.6. В реальном программировании этот код, конечно, не стоит использовать, но в качестве примера, демонстрирующего возможности использования метода each(callback), он вполне подходит. Листинг 23.6. Использование функции each(callback) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование функции each(callback)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style> body { border:1px dotted #000; } div { border:1px solid #F00; text-align:center; cursor:pointer; font-weight:bold; width:300px; margin-bottom:10px; } </style> </head> <body> <div>Элемент №1</div> <div>Элемент №2</div> <div>Элемент №3</div> <script type="text/javascript"> <!-$(document).ready(function(){


Часть V. jQuery

272 $(document.body).click(function () { $("div").each(function (i) { if(i==0) { // Если элемент первый в наборе // Если CSS-свойство color не имеет значения red if(this.style.color != "red") { // Установим ему это значение this.style.color = "red"; // Если CSS-свойство color имеет значения red } else { // Вернем color значение по умолчанию this.style.color = ""; } } else if(i==1) { // если элемент второй в наборе // Если CSS-свойство color не имеет значения blue if(this.style.color != "blue") { // Установим ему это значение this.style.color = "blue"; // Если CSS-свойство color имеет значения blue } else { // Вернем color значение по умолчанию this.style.color = ""; } } else { // Если элемент третий в наборе // Если CSS-свойство color не имеет значения green if(this.style.color != "green") { // Установим ему это значение this.style.color = "green"; // Если CSS-свойство color имеет значения green } else { // Вернем color значение по умолчанию this.style.color = ""; } } }); }); }); --> </script> </body> </html>


Глава 23. Функции ядра jQuery

273

Итак, мы имеем элемент body, который содержит три элемента div. Элементы div в свою очередь содержат текст. Шрифт текста имеет по умолчанию черный цвет. При щелчке мышью в любом месте элемента body будет создан набор элементов, содержащий все div-элементы на текущей странице, и для каждого из них последовательно будет выполнена функция, которую мы передали методу each в аргументе callback. Для первого элемента набора будет установлен красный цвет шрифта, для второго — синий, для третьего — зеленый. length

Собственно, length — это свойство объекта jQuery. Возвращаемое число — количество элементов в объекте. Пример приведен в листинге 23.7. Листинг 23.7. Использование свойства length <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование свойства length</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <p class="result"></p> <span>Элемент SPAN</span> <div>Элемент DIV</div> <p>Элемент P</p> <div>Элемент DIV</div> <span>Элемент SPAN</span> <div>Элемент DIV</div> <script type="text/javascript"> <!-$(document).ready(function(){ var n = $("div").length; $("p.result").text('Найдено элементов div - ' + n + ' шт.'); });


274

Часть V. jQuery

--> </script> </body> </html>

В этом примере на странице имеется несколько элементов div, span и p. Наша задача — подсчитать количество только элементов div. Довольно просто — отбираем все элементы div в объект jQuery и обращаемся к свойству length. Результат записываем в переменную n, а затем вставляем значение этой переменной как текст в элемент p, который имеет класс result.


Глава 24

Селекторы jQuery Как правило, первое, с чего начинается большинство кодов jQuery, — это выбор элемента или элементов, с которыми и будут происходить дальнейшие манипуляции. Неграмотный подход к выбору элементов может потрепать много нервов разработчику. А ключом к грамотному выбору является четкое понимание селекторов jQuery. Ничего особенно сложного здесь нет, ведь библиотека следует CSS-синтаксису и даже дополняет его своими методами выбора элементов. Несколько реже приходится использовать что-то похожее на регулярные выражения. Подбирая и используя совместно различные типы селекторов, можно выбирать элементы и наборы элементов практически с "хирургической" точностью. Поскольку эта тема достаточно важна, мы на простых примерах рассмотрим большинство селекторов.

Базовые селекторы

Базовые селекторы — наиболее употребляемые на практике, понадобятся

в большинстве случаев. #id

Этот селектор выбирает единственный элемент, который имеет соответствующее значение атрибута id (листинг 24.1). Листинг 24.1. Использование селектора #id <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора #id</title>


Часть V. jQuery

276

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <div>Элемент DIV без id</div>

<div id="myDiv">Элемент DIV с id="myDiv"</div>

<div id="otherDiv">Элемент DIV с id="otherDiv"</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#myDiv").css("border","1px solid red"); }); --> </script> </body> </html>

В этом примере на странице присутствуют три элемента div. Один из них вообще не имеет id, два других имеют идентификаторы. Нам необходимо отыскать элемент со значением идентификатора myDiv, что мы с успехом и делаем. А для наглядности мы устанавливаем для найденного элемента красную рамку толщиной в 1 пиксел. Необходимо особо обратить внимание на выбор элементов, идентификаторы которых имеют специфические символы, такие как точка или квадратные скобки. Такая ситуация может возникнуть, например, при использовании каких-либо frameforks, генерирующих значение идентификаторов с помощью специальных символов. Пример из листинга 24.2 иллюстрирует правильный и неправильный подходы. Листинг 24.2. Использование селектора #id со специфическими символами <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора #id со специфическими символами</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head>


Глава 24. Селекторы jQuery

277

<body> <div id="eid.25">div с идентификатором eid.25</div>

<div id="eid.50">div с идентификатором eid.50</div>

<div id="eid[25]">div с идентификатором eid[25]</div> <div id="eid[50]">div с идентификатором eid[50]</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#eid.25").css("background-color","#ccc"); $("#eid\\.50").css("background-color","#ccc"); $("#eid[25]").css("background-color","#ccc");

// это не работает! // а это работает!

// это не работает!

$("#eid\\[50\\]").css("background-color","#ccc"); // а это работает! }); --> </script> </body> </html>

Если вы воспроизведете этот пример, то сможете убедиться, что серый цвет фона будет установлен только для второго и четвертого элементов div. element

Этот селектор выбирает элементы указанного в нем типа (листинг 24.3). Листинг 24.3. Использование селектора element <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора element</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <span>Элемент SPAN</span>

<div id="myDiv">Элемент DIV</div>

<p class="someclass">Элемент P</p> <div>Элемент DIV</div>


Часть V. jQuery

278 <em>Элементе EM</em> <script type="text/javascript"> <!-$(document).ready(function(){ $("div").css("border","1px solid red"); }); --> </script> </body> </html>

В данном примере на странице имеются два элемента div, элементы span, p и em. Используя селектор element, мы выбираем все элементы div и, чтобы убедиться в том, что все было сделано правильно, устанавливаем для них красную рамку в 1 пиксел толщиной. Мы могли бы выбрать любой другой элемент на странице, указав его тип в селекторе. .class

Селектор класса в соответствии со своим названием выбирает все элементы, имеющие соответствующий класс. Пример представлен в листинге 24.4. Листинг 24.4. Использование селектора .class <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора .class</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <div class="myDiv">Элемент DIV с классом myDiv</div>

<div class="otherDiv">Элемент DIV с классом otherDiv</div> <div class="myDiv">Элемент DIV с классом myDiv</div> <script type="text/javascript"> <!-$(document).ready(function(){ $(".myDiv").css("border","1px solid red");


Глава 24. Селекторы jQuery

279

}); --> </script> </body> </html>

Здесь имеем на странице три элемента div. С помощью селектора .class выберем только те из них, которые имеют класс myDiv, и для них установим рамку красного цвета.

П РИМЕЧАНИЕ

Запись в селекторе значения div.myDiv была бы более грамотной, такой код отработал бы быстрее. А вот в случае с селектором #id, напротив, правильнее было бы написать #myDiv, а вовсе не div#myDiv. *

Селектор "звездочка" выбирает все имеющиеся на странице элементы, включая элементы body и head. Пример представлен в листинге 24.5.

Листинг 24.5. Использование селектора * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора *</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <div class="myDiv">Элемент DIV с классом myDiv</div> <div class="otherDiv">Элемент DIV с классом otherDiv</div> <div>Элемент DIV</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("*").css("border","1px solid green"); });


Часть V. jQuery

280 --> </script> </body> </html>

В данном примере мы отыскали все элементы на странице и установили им рамку... А вот и не угадали — зеленого цвета.

Иерархические селекторы ancestor descendant

Этот селектор выбирает все элементы, определенные как потомок (descendant) по отношению к элементу, определенному как предок (ansector). Звучит несколько запутанно, но на примере из листинга 24.6 все станет очень понятно. Листинг 24.6. Использование селектора ansector descendant <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора ansector descendant</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> body { font-size:14px; } form { border:2px green solid; padding:2px; margin:0; background:#efe; } div { color:red; }


Глава 24. Селекторы jQuery

281

fieldset { margin:1px; padding:3px; } </style> </head> <body> <form> <div>Форма заключена в зеленую рамку</div> <label>Ребенок:</label>

<input type="text" name="name" /> <fieldset> <label>Внук:</label> <input type="text" name="newsletter" /> </fieldset> </form>

Сестринский элемент по отношению к форме: <input type="text" name="none" /> <script type="text/javascript"> <!-$(document).ready(function(){ $("form input").css("border", "2px dotted brown"); }); --> </script> </body> </html>

Рассмотрим внимательнее код, приведенный в листинге 24.6. Нас интересует элемент form, а точнее — элементы input внутри него. Первый элемент input является потомком элемента form, а вот второй input — это уже "внук" элемента form, и его предком является элемент fieldset. Третий input вообще за пределами элемента form — это сестринский элемент по отношению к форме. Теперь задача — необходимо отыскать все элементы input внутри form. Вот тут на помощь и придет селектор ansector descendant. Посмотрите выделенную часть кода в листинге 24.6 — в селекторе мы указываем form input, выбирая, таким образом, только те элементы input, которые находятся внутри form. Установим для них коричневую рамку в 2 пиксела, чтобы убедиться, что элемент input, находящийся вне формы, такую рамку не получил.


Часть V. jQuery

282 parent > child

Этот селектор выбирает все элементы, определенные как прямой потомок (child) по отношению к элементу, определенному как родитель (parent). Не менее запутанно, чем в предыдущем примере, но также довольно просто, если разобраться на примере из листинга 24.7. Листинг 24.7. Использование селектора parent

> child

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора parent > child</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> body { font-size:14px; } form { border:2px green solid; padding:2px; margin:0; background:#efe; } div { color:red; } fieldset { margin:1px; padding:3px; } </style> </head> <body> <form> <div>Форма заключена в зеленую рамку</div> <label>Ребенок:</label>

<input type="text" name="name" />


Глава 24. Селекторы jQuery

283

<fieldset> <label>Внук:</label> <input type="text" name="newsletter" /> </fieldset> </form>

Сестринский элемент по отношению к форме: <input type="text" name="none" /> <script type="text/javascript"> <!-$(document).ready(function(){ $("form > input").css("border", "2px dotted brown"); }); --> </script> </body> </html>

Если вы очень внимательно посмотрите на код, приведенный в листинге 24.7, то найдете только одно отличие — эта строка в коде выделена. В селекторе, вместо form input мы записали form > input, применив, таким образом, селектор parent > child. Разметка при этом осталась точно такой же, как и в предыдущем примере. Это сделано специально, чтобы более наглядно показать разницу между селекторами. В примере из листинга 24.7 коричневую рамку получит только элемент input, который является прямым потомком элемента form. Элемент input, находящийся внутри fieldset, и тем более input, который находится вне элемента form, таких рамок не получат. prev + next

Этот селектор выбирает все элементы, определенные как следующие (next) за элементом, который определен как предыдущий (prev). В общем и целом ясно, но, тем не менее, изучить пример из листинга 24.8 не помешает. Листинг 24.8. Использование селектора prev

+ next

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора prev + next</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />


284

Часть V. jQuery

<script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> body { font-size:14px; } form { border:2px green solid; padding:2px; margin:0; background:#efe; } div { color:red; } fieldset { margin:1px; padding:3px; } </style> </head> <body> <form> <div>Форма заключена в зеленую рамку</div> <label>Ребенок:</label> <input type="text" name="name" /> <fieldset> <input type="text" name="newsletter" /> </fieldset> </form> <label>Сестринский элемент по отношению к форме:</label><input type="text" name="none" /> <script type="text/javascript"> <!-$(document).ready(function(){ $("label + input").css("border", "2px dotted brown") .attr("value","Label"); }); --> </script> </body> </html>


Глава 24. Селекторы jQuery

285

В коде из листинга 24.8, по сравнению с предыдущим примером, изменилось не многое. Обратите внимание на элементы label в HTML-разметке. Изменился и JavaScript-код. Теперь мы используем селектор label + input для того, чтобы отыскать все элементы input, которые следуют сразу за элементом label. Когда эти элементы найдены, мы как обычно установим для них коричневую рамочку и заодно добавим в атрибут value найденных элементов input значение "Label". prev ~ siblings

Этот селектор выбирает все элементы, определенные в siblings и являющиеся сестринскими по отношению к элементу, определенному как prev, но следующие после него. Тут уж точно все совсем непонятно, но на помощь придет очередной пример из листинга 24.9. Листинг 24.9. Использование селектора prev

~ siblings

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора prev ~ siblings</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div, span { display:block; width:120px; height:120px; margin:5px; padding:5px; background:#bfa; float:left; font-size:14px; } div#small { width:80px; height:50px; padding:5px;


286

Часть V. jQuery

font-size:12px; background:#fab; } </style> </head> <body> <div>div (не выбран, поскольку следует перед #prev)</div> <div id="prev">div#prev - выбор относительно этого элемента</div> <div>сестринский div</div> <div>сестринский div <div id="small">Этот div не сестринский</div></div> <span>span - сестринский элемент, но не div</span> <div>сестринский div</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#prev ~ div").css("border", "3px double blue"); }); --> </script> </body> </html>

В листинге 24.9 найдите элемент div, имеющий идентификатор prev. Относительно него мы постараемся отыскать все сестринские элементы div, следующие после него, записав в селекторе #prev ~ div. А найденным таким образом элементам установим двойную рамку синего цвета.

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

Этот селектор "отфильтрует" набор элементов, оставив в нем только самый первый. Пример приведен в листинге 24.10.


Глава 24. Селекторы jQuery

287

Листинг 24.10. Использование селектора :first <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :first</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> td { color:#00f; font-weight:bold; } </style> </head> <body> <table> <tr><td>Строка 1</td></tr> <tr><td>Строка 2</td></tr> <tr><td>Строка 3</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("tr:first").css("font-style", "italic"); }); --> </script> </body> </html>

Данный код выбирает только первую строку из таблицы и устанавливает для нее наклонное начертание шрифта. :last

Единственное отличие от предыдущего селектора в том, что селектор :last "отфильтрует" набор элементов, оставив в нем только самый последний (листинг 24.11).


Часть V. jQuery

288

Листинг 24.11. Использование селектора :last <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :last</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> td { color:#00f; font-weight:bold; } </style> </head> <body> <table> <tr><td>Строка 1</td></tr> <tr><td>Строка 2</td></tr> <tr><td>Строка 3</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("tr:last").css("font-style", "italic"); }); --> </script> </body> </html>

Точно так же, как и в предыдущем примере, в листинге 24.11 мы имеем таблицу, состоящую из трех строк. Только теперь мы выбираем последнюю строку и устанавливаем для нее наклонное начертание шрифта. :not(selector)

Этот селектор создаст набор, в который будут включены абсолютно все элементы кроме указаных в selector. Логически он несколько выпадает


Глава 24. Селекторы jQuery

289

из общего ряда рассматриваемых селекторов, но зато с легкостью позволяет "инвертировать" набор элементов.

П РИМЕЧАНИЕ

Селектор, а вернее, селектор-фильтр :not, можно применять только к селекторам-фильтрам, которые можно опознать по символу двоеточия (:) или квадратной скобки ([). Другие селекторы внутри селектора-фильтра :not использовать нельзя. Рассмотрим пример из листинга 24.12.

Листинг 24.12. Использование селектора :not(selector) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :not(selector)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <div> <input type="checkbox" name="a" /> <span>Вера</span> </div> <div> <input type="checkbox" name="b" /> <span>Надежда</span> </div> <div> <input type="checkbox" name="c" checked="checked" /> <span>Любовь</span> </div> <script type="text/javascript"> <!-$(document).ready(function(){ $("input:not(:checked) + span").css("background-color", "yellow"); $("input").attr("disabled", "disabled"); });


Часть V. jQuery

290 --> </script> </body> </html>

В данном примере мы отыскиваем все невыбранные элементы input (в нашем случае это checkbox-элементы) и устанавливаем желтый цвет фона для расположеных сразу за ними сестринскими элементами span. П РИМЕЧАНИЕ Следующий за выделенной строкой код просто запрещает выбор элементов поскольку в простом примере из листинга 24.12 мы не устанавливаем никаких обработчиков событий, связанных с ними. checkbox,

:even

Этот селектор оставит в наборе только четные элементы, включая и нулевой. То есть в наборе останутся элементы с индексами 0, 2, 4, ... и т. д. Пример представлен в листинге 24.13. Листинг 24.13. Использование селектора :even <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :even</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> table { background-color:#eee; border:1px solid #00f; } </style> </head> <body> <table> <tr><td>Строка с индексом #0</td></tr> <tr><td>Строка с индексом #1</td></tr>

<tr><td>Строка с индексом #2</td></tr>


Глава 24. Селекторы jQuery

291

<tr><td>Строка с индексом #3</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("tr:even").css("background-color", "#bbf"); }); --> </script> </body> </html>

Здесь мы имеем таблицу, состоящую из четырех строк. Для нее установлен серый цвет фона. Воспользовавшись селектором :even, мы выбираем все четные строки этой таблицы и устанавливаем их CSS-свойству backgroundcolor значение #bbf. :odd

Этот селектор оставит в наборе только нечетные элементы. То есть в наборе останутся элементы с индексами 1, 3, 5,... и т. д. Пример приедставлен в листинге 24.14.

Листинг 24.14. Использование селектора :odd <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :odd</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> table { background-color:#eee; border:1px solid #00f; } </style> </head> <body>


Часть V. jQuery

292 <table> <tr><td>Строка с индексом #0</td></tr> <tr><td>Строка с индексом #1</td></tr>

<tr><td>Строка с индексом #2</td></tr> <tr><td>Строка с индексом #3</td></tr>

</table> <script type="text/javascript"> <!-$(document).ready(function(){ $("tr:odd").css("background-color", "#bbf"); }); --> </script> </body> </html>

Пример из листинга 24.14 практически полностью повторяет предыдущий пример, за одним исключением — мы делаем все то же самое, только для нечетных строк таблицы. :eq(index)

Этот селектор выбирает единственный элемент с указанным индексом (листинг 24.15). Листинг 24.15. Использование селектора :eq(index) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :eq(index)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> table { background-color:#eee; border:1px solid #00f; } </style>


Глава 24. Селекторы jQuery

293

</head> <body> <table> <tr><td>TD #0</td><td>TD #1</td><td>TD #2</td></tr> <tr><td>TD #3</td><td>TD #4</td><td>TD #5</td></tr> <tr><td>TD #6</td><td>TD #7</td><td>TD #8</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("td:eq(2)").css("color", "red"); }); --> </script> </body> </html>

Разберем код. У нас есть таблица, состоящая из трех строк и трех столбцов, т. е. имеется девять элементов td с индексами от 0 до 8. Мы находим элемент td с индексом 2 и устанавливаем для содержимого этой ячейки таблицы красный цвет шрифта. Это будет третья ячейка в первой строке. :gt(index)

Этот селектор выбирает все элементы с индексами большими, чем указанный. Пример представлен в листинге 24.16.

Листинг 24.16. Использование селектора :gt(index) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :gt(index)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> table { background-color:#eee; border:1px solid #00f;


Часть V. jQuery

294 } </style> </head> <body> <table> <tr><td>TD #0</td><td>TD #1</td><td>TD #2</td></tr> <tr><td>TD #3</td><td>TD #4</td><td>TD #5</td></tr> <tr><td>TD #6</td><td>TD #7</td><td>TD #8</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("td:gt(4)").css("text-decoration", "line-through"); }); --> </script> </body> </html>

В примере из листинга 24.16 таблица осталась такой же, но наша задача изменилась. Нам необходимо выбрать все элементы td с индексами более 4 и с помощью CSS-свойств "зачеркнуть" текст, находящийся в этих ячейках таблицы. Что мы с успехом можем проделать, использовав селектор :gt(index). :lt(index)

Этот селектор выбирает все элементы с индексами меньшими, чем указанный. Пример представлен в листинге 24.17. Листинг 24.17. Использование селектора :lt(index) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :lt(index)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> table {


Глава 24. Селекторы jQuery

295

background-color:#eee; border:1px solid #00f; } </style> </head> <body> <table> <tr><td>TD #0</td><td>TD #1</td><td>TD #2</td></tr> <tr><td>TD #3</td><td>TD #4</td><td>TD #5</td></tr> <tr><td>TD #6</td><td>TD #7</td><td>TD #8</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("td:lt(5)").css("text-decoration", "line-through"); }); --> </script> </body> </html>

В листинге 24.17 HTML-код остался прежним, но теперь мы легко и просто выбираем уже элементы td с индексами меньше 5, используя селектор :lt(index), и так же "зачеркиваем" находящийся в них текст. :header

Этот селектор выбирает все элементы, которые являются заголовками. Например, элементы h1, h2, h3 и т. д. Пример приведен в листинге 24.18. Листинг 24.18. Использование селектора :header <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :header</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css">


Часть V. jQuery

296 body { font-size: 10px; font-family: Arial; } h1, h2 { margin: 3px 0; } </style> </head> <body> <h1>Заголовок - элемент h1</h1>

<p>Какое-то содержимое в элементе p</p> <h2>Заголовок - элемент h2</h2>

<p>Какое-то содержимое в элементе p</p> <script type="text/javascript"> <!-$(document).ready(function(){ $(":header").css({ background:'#ccc', color:'#00f' }); }); --> </script> </body> </html>

В данном коде имеются два элемента p и элементы h1 и h2. Воспользовавшись селектором :header, мы можем выбрать элементы h1 и h2 и установить для них серый цвет фона и синий цвет шрифта. :animated

Этот селектор выбирает все элементы, которые в данный момент являются анимированными. Пример представлен в листинге 24.19. Листинг 24.19. Использование селектора :animated <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :animated</title>


Глава 24. Селекторы jQuery

297

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { background-color:yellow; border:1px solid #AAA; width:80px; height:80px; margin:5px; float:left; } div.colored { background-color:green; } </style> </head> <body> <button id="run">Выполнить</button> <div></div> <div id="mover"></div> <div></div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#run").click(function(){ $("div:animated").toggleClass("colored"); }); function animateIt() { $("#mover").slideToggle("slow", animateIt); } animateIt(); }); --> </script> </body> </html>

Разберем подробнее пример, приведенный в листинге 24.19. На странице имеются три элемента div, один из которых с идентификатором mover — именно он будет играть роль анимированного элемента, который нам необ-


Часть V. jQuery

298

ходимо будет отыскать. Есть также кнопка, по щелчку на которой мы будем отыскивать анимированный элемент и переключать для него стилевой класс. Теперь обратим внимание на JavaScript-код. Функция animateIt() связывает элемент div с идентификатором mover с методом slideToggle() (один из методов, реализующий эффекты в jQuery), который в свою очередь вновь вызывает функцию animateIt(), заставляя целевой элемент быть постоянно в процессе анимации. Но нас больше интересует другая часть кода, которая будет выполнена по щелчку на кнопке. Указав в селекторе div:animated, мы, таким образом, отыскиваем нужный нам элемент и по каждому следующему нажатию кнопки добавляем или удаляем стилевой класс .colored, что приводит к изменению цвета этого элемента с желтого на зеленый и обратно.

Фильтры содержимого Иногда бывает необходимо выбрать элементы, имеющие внутри себя какоелибо содержимое. Это может быть текст или какой-нибудь HTML-элемент. С другой стороны, может понадобиться отыскать элементы, не имеющие вообще никакого содержимого. :contains(text)

Этот селектор выберет все элементы, которые содержат указанный текст. В аргументе text задают строку текста, которую необходимо обнаружить. Поиск производится с учетом регистра. Пример представлен в листинге 24.20.

Листинг 24.20. Использование селектора :contains(text) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :contains(text)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <div>John Resig</div> <div>George Martin</div> <div>Malcom John Sinclair</div> <div>J. Ohn</div>


Глава 24. Селекторы jQuery

299

<div>b byJohns a</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div:contains('John')").css("text-decoration", "underline"); }); --> </script> </body> </html>

В примере из листинга 24.20 имеются пять элементов div, каждый из которых содержит некоторый текст. Указав в селекторе div:contains('John'), мы отыскиваем только те элементы div, внутри которых встречается искомый текст, и с помощью CSS-свойств подчеркиваем весь текст внутри элемента. Таким образом, в данном примере будет подчеркнут текст в первом, третьем и пятом элементах div. :empty

Этот селектор выберет все элементы, которые не имеют потомков. Если элемент имеет внутри себя текстовый узел, он тоже расценивается как потомок. Пример представлен в листинге 24.21. Листинг 24.21. Использование селектора :empty <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :empty</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> td { text-align:center; border:1px solid #00f; } </style> </head>


Часть V. jQuery

300 <body> <table> <tr><td>TD #0</td><td></td></tr> <tr><td>TD #2</td><td></td></tr> <tr><td></td><td>TD #5</td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("td:empty").text("Была пустая!") .css("background", "rgb(255,102,0)"); }); --> </script> </body> </html>

Приведенный код реализует таблицу, состоящую из трех строк и двух столбцов. Некоторые ячейки этой таблицы пусты. Мы используем селектор td:empty, чтобы отыскать эти ячейки, вставить в них текст "Была пустая!" и установить для этих ячеек оранжевый цвет фона с помощью CSS-свойств. :has(selector)

Этот селектор выберет все элементы, которые содержат внутри себя как минимум один элемент, указанный в аргументе selector. Пример приведен в листинге 24.22. Листинг 24.22. Использование селектора :has(selector) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :has(selector)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> .test { border: 3px inset red;


Глава 24. Селекторы jQuery

301

} </style> </head> <body> <div><p>Это текст в параграфе</p></div> <div>Этот просто текст</div>

<script type="text/javascript"> <!-$(document).ready(function(){ $("div:has(p)").addClass("test"); }); --> </script> </body> </html>

Код из листинга 24.22 добавляет ко всем элементам div, внутри которых имеется хотя бы один элемент p, класс .test, т. е. красную рамку. :parent

Этот селектор выберет все элементы, являющиеся родительскими, т. е. имеющие внутри себя элементы потомки. Потомками будут считаться также и текстовые узлы. Пример представлен в листинге 24.23. Листинг 24.23. Использование селектора :parent <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :parent</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> td { width:40px; background:green; border:1px solid #0ff; }


Часть V. jQuery

302 </style> </head> <body> <table> <tr><td>Значение_1</td><td></td></tr> <tr><td>Значение_2</td><td></td></tr> </table> <script type="text/javascript"> <!-$(document).ready(function(){ $("td:parent").fadeTo(1500, 0.3); }); --> </script> </body> </html>

В листинге 24.23 с помощью селектора :parent мы отыскиваем все элементы td, имеющие элементы-потомки, и применяем для них эффект затухания, используя метод fadeTo() (один из методов, реализующий эффекты в jQuery).

Фильтры видимых и невидимых элементов :hidden

Этот селектор выберет все элементы, которые являются скрытыми, или элементы input, имеющие тип hidden. Пример приведен в листинге 24.24. Листинг 24.24. Использование селектора :hidden <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :hidden</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div {


Глава 24. Селекторы jQuery width:70px; height:40px; background:#e7f; margin:5px; float:left; } span { display:block; clear:left; color:#f00; } .starthidden { display:none; } </style> <script type="text/javascript"> <!-$(document).ready(function(){ // В некоторых браузерах селектор :hidden выберет также // элементы head, title, script и т. д.

// Чтобы обойти это, селектор нужно применить в контексте body.

$("span:first").text("Всего найдено " +

$(":hidden", document.body).length + " скрытых элементов.");

$("div:hidden").show(3000); $("span:last").text("из них " + $("input:hidden").length + " элементов input type=hidden.");

}); --> </script> </head> <body> <span></span> <div></div> <div style="display:none;">Был скрыт!</div> <div></div> <div class="starthidden">Был скрыт!</div> <div></div> <form> <input type="hidden" />

303


Часть V. jQuery

304 <input type="hidden" /> <input type="hidden" /> </form> <span></span> </body> </html>

Данный HTML-код реализует пять элементов div, два из которых скрыты с помощью CSS-правил, форму, имеющую три элемента input с типом hidden. Два элемента span служат лишь для того, чтобы вставить в них тексты сообщений. П РИМЕЧАНИЕ

В некоторых браузерах селектор :hidden выберет также элементы head, title, script и т. д. Чтобы обойти это, селектор нужно применить в контексте body.

Итак, воспользовавшись свойством length объекта jQuery, мы для начала подсчитаем все скрытые элементы, имеющиеся в body, и вставим полученный результат в первый элемент span на странице. В последний элемент span на странице мы вставим результат подсчета только элементов input, которые имеют тип hidden. Кроме этого, мы попробуем в деле эффект show() (один из методов, реализующий эффекты в jQuery) для того, чтобы отобразить скрытые элементы div в течение трех секунд после готовности DOM к работе. :visible

Этот селектор выберет все элементы, которые являются видимыми. Пример приведен в листинге 24.25. Листинг 24.25. Использование селектора :visible <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :visible</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div {


Глава 24. Селекторы jQuery

305

width:50px; height:40px; margin:5px; border:3px outset #00f; float:left; } .starthidden { display:none; } </style> <script type="text/javascript"> <!-$(document).ready(function(){ $("div:visible").click(function () { $(this).css("background", "yellow"); }); $("button").click(function () { $("div:hidden").show("fast"); }); }); --> </script> </head> <body> <button>Показать скрытые</button> <div></div> <div class="starthidden"></div> <div></div> <div></div> <div style="display:none;"></div> </body> </html>

Итак, на странице имеются пять элементов div, два из которых скрыты с помощью CSS-правил. Элемент button выступает в качестве управляющего элемента. Щелчок мышью на этом элементе отобразит скрытые ранее элементы div с помощью метода show() (один из методов, реализующий эффекты в jQuery). Для того чтобы убедиться, что селектор :visible работает, мы напишем еще немного кода. Указав в селекторе div:visible, мы создадим


Часть V. jQuery

306

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

Фильтры атрибутов При работе с фильтрами атрибутов библиотека jQuery использует синтаксис, очень похожий на синтаксис регулярных выражений. [attribute]

Этот селектор выберет все элементы, которые имеют указанный атрибут. Пример представлен в листинге 24.26.

Листинг 24.26. Использование селектора [attribute] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора [attribute]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } </style> </head>


Глава 24. Селекторы jQuery

307

<body> <div>Элемент div без атрибута id</div>

<div id="test">Элемент div с атрибутом id</div>

<div id="attribute">Элемент div с атрибутом id</div>

<div>Элемент div без атрибута id</div>

<div id="selector">Элемент div с атрибутом id</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[id]").click(function(){ alert('id = ' + $(this).attr("id")); }); }); --> </script> </body> </html>

Разберемся, что же мы написали в листинге 24.26. Есть пять элементов div, из которых только три имеют атрибут id — именно они будут нас интересовать. В селекторе мы указали div[id], создав, таким образом, набор только из тех элементов div, которые имеют указанный атрибут. Затем назначили обработчик события click() для этого набора — по щелчоку на элементе div, имеющему атрибут id, мы будем выводить в alert() значение этого атрибута. Если сделать щелчок на элементе div, не имеющем атрибута id, ничего не произойдет, поскольку эти элементы не были включены в набор. [attribute=value]

Этот селектор выберет все элементы, которые имеют соответствующее значение указанного атрибута.

Листинг 24.27. Использование селектора [attribute=value] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора [attribute=value]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />


Часть V. jQuery

308

<script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } .test { background-color:#f93; } .testing { background-color:#9f3; } </style> </head> <body> <div>Элемент div без атрибута class</div> <div class="test">Элемент div с атрибутом class=test</div> <div class="test">Элемент div с атрибутом class=test</div> <div class="testing">Элемент div с атрибутом class=testing</div> <div class="test">Элемент div с атрибутом class=test</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[class='test']").click(function() { $(this).css("background","#fff"); }); }); --> </script> </body> </html>


Глава 24. Селекторы jQuery

309

В примере, приведенном в листинге 24.27, на странице имеются пять элементов div, из которых три имеют атрибут class со значением test, один элемент div вообще не имеет атрибута class, и еще один элемент div имеет атрибут class со значением testing. Нашей задачей будет отыскать только те элементы div, которые имеют атрибут class="test". Сделать это очень легко, записав в селекторе div[class='test']. А чтобы убедиться в том, что все сделано правильно, назначим и обработчик события click(), в котором будем устанавливать белый цвет фона элементу div в том случае, если он имеет указанный класс и, соответственно, включен в набор. [attribute!=value]

Этот селектор выберет все элементы, которые удовлетворяют двум правилам: или элемент вообще не имеет указанного атрибута, или такой атрибут есть, но его значение не соответствует значению, указанному в селекторе. Пример представлен в листинге 24.28. Листинг 24.28. Использование селектора [attribute!=value] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора [attribute!=value]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } .test { background-color:#f93; }


Часть V. jQuery

310 .testing { background-color:#9f3; } </style> </head> <body> <div>Элемент div без атрибута class</div>

<div class="test">Элемент div с атрибутом class=test</div>

<div class="test">Элемент div с атрибутом class=test</div>

<div class="testing">Элемент div с атрибутом class=testing</div> <div class="test">Элемент div с атрибутом class=test</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[class!='test']").click(function() { $(this).css("background","#fff"); }); }); --> </script> </body> </html>

Код в листинге 24.28 полностью повторяет код из предыдущего примера. Разница в JavaScript-коде, а точнее в селекторе. Теперь мы записали div[class!='test'], и, соответственно, в набор будут отобраны два из пяти элементов div. Первый — вообще не имеющий атрибута class, второй — с атрибутом class='testing'. Именно для них будет теперь устанавливаться белый цвет фона при щелчке. [attribute^=value]

Этот селектор выберет все элементы, соответствующий атрибут которых начинается с указанного значения. Рассмотрим пример из листинга 24.29. Листинг 24.29. Использование селектора [attribute^=value] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru">


Глава 24. Селекторы jQuery <head> <title>Использование селектора [attribute^=value]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } .test { background-color:#f93; } .testing { background-color:#9f3; } </style> </head> <body> <div>Элемент div без атрибута class</div> <div class="test">Элемент div с атрибутом class=test</div> <div class="test">Элемент div с атрибутом class=test</div> <div class="testing">Элемент div с атрибутом class=testing</div> <div class="test">Элемент div с атрибутом class=test</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[class^='test']").click(function() { $(this).css("background","#fff"); }); }); --> </script> </body> </html>

311


Часть V. jQuery

312

HTML-код в листинге 24.29 оставляем точно таким же, как и в предыдущих примерах. Но теперь мы указываем в селекторе div[class^='test'], чтобы выбрать все элементы div, значение атрибута class которых начинается с test, и только для них будем устанавливать белый цвет фона, если будет совершен щелчок по соответствующему элементу. Какие элементы div получат в итоге белый цвет фона, догадались? Верно — все, кроме первого. [attribute$=value]

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

Листинг 24.30. Использование селектора [attribute$=value] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора [attribute$=value]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } .test { background-color:#f93; } .testing { background-color:#9f3; } </style> </head>


Глава 24. Селекторы jQuery

313

<body> <div>Элемент div без атрибута class</div>

<div class="test">Элемент div с атрибутом class=test</div>

<div class="test">Элемент div с атрибутом class=test</div>

<div class="testing">Элемент div с атрибутом class=testing</div> <div class="test">Элемент div с атрибутом class=test</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[class$='est']").click(function() { $(this).css("background","#fff"); }); }); --> </script> </body> </html>

И вновь оставим без изменений HTML-код, но поменяем правило для выбора элементов в селекторе, записав div[class$='est'], чтобы выбрать все элементы, атрибут class которых заканчивается последовательностью знаков est. Теперь по щелчку на элементах div белый цвет фона будет установлен на втором, третьем и пятом элементах. [attribute*=value]

Этот селектор выберет все элементы, соответствующий атрибут которых содержит указанное значение. Пример приведен в листинге 24.31. Листинг 24.31. Использование селектора [attribute*=value] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора [attribute*=value]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div {


Часть V. jQuery

314 width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } em { font-weight:bold; color:#fc9; } </style> </head> <body> <div>Элемент div без атрибута class</div>

<div class="bigtest">Элемент div с атрибутом class=big<em>test</em></div> <div class="testsmall">Элемент div с атрибутом class=<em>test</em>small</div> <div class="easy-testing">Элемент div с атрибутом class=easy<em>test</em>ing</div> <div class="test">Элемент div с атрибутом class=<em>test</em></div>

<div class="other">Элемент div с атрибутом class=other</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[class*='test']").click(function() { $(this).css("background","#fff"); }); }); --> </script> </body> </html>

Код в листинге 24.31 немного изменен по сравнению с предыдущими примерами. Мы добавили еще один элемент div и немного поменяли имена классов для них, чтобы иметь возможность показать все вероятные варианты использования


Глава 24. Селекторы jQuery

315

селектора. Итак, если в селекторе записать div[class*='test'], то будут выбраны все элементы div, значение атрибута class которых содержит последовательность знаков test. Причем совершенно неважно, в каком месте встретится эта последовательность: в начале, в конце или в середине названия класса. В данном примере белый цвет фона при щелчке на элементе div будет установлен для элементов со второго по пятый включительно. [attributeFilter1] [attributeFilter2] ... [attributeFilterN]

Этот селектор выберет элемент или элементы, которые удовлетворяют всем имеющимся фильтрам атрибутов. Пример представлен в листинге 24.32.

Листинг 24.32. Использование селектора

[attributeFilter1][attributeFilter2]...[attributeFilterN] <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора [attributeFilter1][attributeFilter2]...[attributeFilterN]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#39f; float:left; cursor:pointer; } </style> </head> <body> <div id="first">Элемент div id=first</div> <div id="second" class="test">Элемент div id=second class=bigtest</div> <div class="test">Элемент div class=test</div> <div class="easy-testing">Элемент div class=easy-testing</div>


316

Часть V. jQuery

<div id="fifth" class="test">Элемент div id=fifth class=test</div> <div class="other">Элемент div class=other</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div[id^='fi'][class='test']").click(function() { $(this).css("background","#fff"); }); }); --> </script> </body> </html>

В данном примере имеется шесть элементов div. Пусть нам необходимо отыскать только такие элементы, атрибут id которых начинается с последовательности знаков 'fi', а такому правилу соответствуют два элемента — первый и пятый, но при этом элемент должен иметь еще и атрибут class со значением test, а такому общему правилу будет соответствовать уже только один элемент div — пятый. И лишь он, при щелчке по нему мышью, получит белый цвет фона.

Фильтры потомков

Эти селекторы-фильтры позволяют отыскивать элементы на основе отношений "родитель — потомок". :nth-child(index/even/odd/equation)

Этот селектор выбирает элементы-потомки по индексу (index), четные (even) или нечетные (odd) и даже по формуле, определяющей шаг, с которым надо отбирать элементы, например первый, четвертый, седьмой и т. д. (equation) для каждого элемента-родителя. Здесь ключевые слова — "для каждого элемента-родителя". Понять логику работы этого селектора проще на примере, приведенном в листинге 24.33. Листинг 24.33. Использование селектора :nth-child(index/even/odd/equation) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head>


Глава 24. Селекторы jQuery

317

<title>Использование селектора :nth-child(index/even/odd/equation)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> button { display:block; font-size:12px; width:100px; } div { float:left; margin:10px; font-size:10px; border:1px solid #000; } span { color:#00f; font-size:18px; } #inner { color:#f00; } td { width:50px; font-size:14px; text-align:center; } </style> </head> <body> <div> <button>:nth-child(even)</button> <button>:nth-child(odd)</button> <button>:nth-child(3n)</button> <button>:nth-child(2)</button> </div> <div> <button>:nth-child(3n+1)</button> <button>:nth-child(3n+2)</button> <button>:even</button> <button>:odd</button>


Часть V. jQuery

318 </div> <div> <table> <tr><td>John</td></tr> <tr><td>Karl</td></tr> <tr><td>Brandon</td></tr> <tr><td>Benjamin</td></tr> </table> </div> <div> <table> <tr><td>Sam</td></tr> </table> </div> <div> <table> <tr><td>Glen</td></tr> <tr><td>Tane</td></tr> <tr><td>Ralph</td></tr> <tr><td>David</td></tr> <tr><td>Mike</td></tr> <tr><td>Dan</td></tr> </table> </div> <span>tr<span id="inner"></span></span> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function () { var str = $(this).text(); $("tr").css("background", "#fff"); $("tr" + str).css("background", "#f00"); $("#inner").text(str); }); }); --> </script> </body> </html>


Глава 24. Селекторы jQuery

319

Разберем пример довольно подробно. Сначала обратим внимание на кнопки — элементы button содержат текст, который является каким-либо из вариантов использования селектора. Следом идут три "подопытных" таблицы. Все они состоят только из одного столбца, но имеют разное количество строк. Есть еще элемент span с идентификатором inner — он тоже нам пригодится. Перейдем к рассмотрению JavaScript-кода. Он представляет собой обработчик события click() для элементов button. Что же будет сделано при щелчке на какой-либо кнопке? Сначала в переменную str будет записан текст, который содержится в выбранном элементе button. Следующая строка кода выберет все элементы tr на странице и установит для них белый цвет фона. Самое главное в следующей строке — здесь селектор формируется из названия элемента tr и к нему добавляется тот текст, который ранее был сохранен в переменной str, а поскольку текст мы получаем из элементов button, то имеем возможность наблюдать работу селектора в разных вариантах, просто щелкая по соответствующим кнопкам. Выбранные в соответствии с селектором строки таблиц будут иметь красный цвет фона. А какой именно вариант селектора мы используем, видно в элементе #inner, куда мы тоже вставляем соответствующий текст. Стоит заметить, что кнопки :even и :odd добавлены в пример специально для того, чтобы помочь лучше уяснить разницу между использованием селекторов, например :nth-child(even) и просто :even. При щелчке на кнопке :even будут выбраны все четные элементы tr независимо от принадлежности к какой-либо таблице, т. е. красный цвет фона будет установлен для ячеек: "John", "Brandon", "Sam", "Tane", "David" и "Dan". А вот при щелчке на кнопке :nth-child(even) — для ячеек "Karl", "Benjamin", "Tane", "David" и "Dan", т. е. тоже четные строки, но отсчет будет начинаться каждый раз с новой таблицы. :first-child

Этот селектор выбирает все элементы, являющиеся первыми потомками своего родителя. Рассмотрим пример из листинга 24.34.

Листинг 24.34. Использование селектора :first-child <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :first-child</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />


320

Часть V. jQuery

<script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> span { color:#008; } </style> </head> <body> <div> <span>John,</span> <span>Karl,</span> <span>Brandon</span> </div> <div> <span>Glen,</span> <span>Tane,</span> <span>Ralph</span> </div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div span:first-child").css("text-decoration", "underline"); }); --> </script> </body> </html>

Это очень простенький пример, демонстрирующий работу селектора :first-child. Собственно, даже название селектора вполне "говорящее" — будут выбраны элементы span, которые являются потомками элементов div, причем его первыми потомками. Текст, находящийся в выбранных элементах, будет подчеркнут. Таким образом, "подчеркнутыми" у нас окажутся "John" и "Glen". :last-child

Этот селектор выбирает все элементы, являющиеся последними потомками своего родителя. Пример представлен в листинге 24.35.


Глава 24. Селекторы jQuery

321

Листинг 24.35. Использование селектора :last-child <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :last-child</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> span { color:#008; } </style> </head> <body> <div> <span>John,</span> <span>Karl,</span> <span>Brandon</span> </div> <div> <span>Glen,</span> <span>Tane,</span> <span>Ralph</span> </div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div span:last-child").css("text-decoration", "underline"); }); --> </script> </body> </html>

Здесь мы используем тот же самый код, что и в предыдущем примере, только с помощью селектора :last-child выберем элементы span, которые являются потомками элементов div, но теперь уже его последними потомками.


Часть V. jQuery

322

Точно так же подчеркнем текст, находящийся в выбранных элементах. В этом случае "подчеркнутыми" у нас окажутся "Brandon" и "Ralph". :only-child

Этот селектор выбирает все элементы, являющиеся единственными потомками своего родителя. Пример приведен в листинге 24.36. Листинг 24.36. Использование селектора :only-child <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :only-child</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { width:100px; height:80px; margin:5px; float:left; background:#b9e; } </style> </head> <body> <div> <button>Sibling</button> <button>Sibling</button> </div> <div> <button>Sibling</button> </div> <div> None </div> <div>


Глава 24. Селекторы jQuery

323

<button>Sibling</button> <button>Sibling</button> <button>Sibling</button> </div> <div> <button>Sibling</button> </div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div button:only-child").text("Alone") .css("border", "2px solid #00f"); }); --> </script> </body> </html>

Данный код описывает пять элементов div, четыре из которых имеют разное количество элементов-потомков, а один таких элементов не имеет. С помощью селектора :only-child мы выберем только те элементы, которые являются единственными потомками своего элемента-родителя div, вставим в них текст "Alone" и установим рамку. Нашим условиям будут удовлетворять только элементы button внутри второго и пятого элементов div.

Селекторы в формах

Селекторы-фильтры предназначены для точного отбора необходимых элементов внутри форм. После краткого описания назначения каждого фильтра мы познакомимся с их возможностями на единственном примере: :input — выбирает все элементы input, а также элементы select, textarea и button; :text — выбирает все элементы input, имеющие тип text; :password — выбирает все элементы input с типом password; :radio — выбирает все элементы input с типом radio; :checkbox — выбирает все элементы input с типом checkbox; :submit — выбирает все элементы input с типом submit; :image — выбирает все элементы input с типом image;


Часть V. jQuery

324 :reset

— выбирает все элементы input с типом reset;

— выбирает все элементы input с типом button; :file — выбирает все элементы input с типом file; :hidden — выбирает все элементы input с типом hidden. :button

Работу всех этих селекторов удобнее разобрать на одном примере, приведенном в листинге 24.37.

Листинг 24.37. Использование селекторов форм <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селекторов форм</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> textarea { height:45px; } form { width:300px; margin-top:20px; background-color:#c6c6c6; border:1px dotted #9c9c9c; } label { margin-left:10px; color:#f00; } </style> </head> <body> <select id="choice"> <option>Выбор селектора</option> <option value=":input">:input</option> <option value=":text">:text</option> <option value=":password">:password</option> <option value=":radio">:radio</option>


Глава 24. Селекторы jQuery <option value=":checkbox">:checkbox</option> <option value=":submit">:submit</option> <option value=":image">:image</option> <option value=":reset">:reset</option> <option value=":button">:button</option> <option value=":file">:file</option> <option value=":hidden">:hidden</option> </select> <form> checkbox <input type="checkbox" /><label></label><br /> <input type="file" /><label></label><br /> hidden <input type="hidden" /><label></label><br /> <input type="image" /><label></label><br /> Password <input type="password" /><label></label><br /> radio <input type="radio" /><label></label><br /> <input type="reset" /><label></label><br /> <input type="submit" /><label></label><br /> Text <input type="text" /><label></label><br /> <select> <option>Option1</option><option>Option2</option> </select><label></label><br /> <textarea></textarea><label></label><br /> <button>Button</button><label></label> </form> <div id="messages"></div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#choice").change(function() { $("label").text(""); var op = $("option:selected").attr("value"); $("form > " + op).next().text("найден!"); }); // Предотвращаем отправку формы $("form").submit(function () { return false; }); }); --> </script> </body> </html>

325


Часть V. jQuery

326

Здесь мы имеем элемент select, который позволит выбирать нужный селектор-фильтр, и "подопытную" форму, включающую в себя все возможные типы элементов. Как только в элементе select будет сделан выбор, мы получим значение атрибута value выбранной опции и используем его для того, чтобы найти нужный элемент или элементы в форме. А для того чтобы визуально отследить работу селектора, мы отыскиваем следующий непосредственно за найденным элементом элемент label и вставляем в него текст "найден!".

Фильтры состояния элементов форм Эти селекторы-фильтры помогают выбирать необходимые элементы на основании их состояния. :enabled

Этот селектор выбирает все элементы, являющиеся активными. Пример представлен в листинге 24.38.

Листинг 24.38. Использование селектора :enabled <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :enabled</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <form> <input name="email" disabled="disabled" /> <input name="id" /> </form> <script type="text/javascript"> <!-$(document).ready(function(){ $("input:enabled").val("активный элемент"); });


Глава 24. Селекторы jQuery

327

--> </script> </body> </html>

Итак, имеется форма, содержащая два элемента input, первый из которых сделан неактивным с помощью атрибута disabled. Второй элемент input такого атрибута не имеет и, соответственно, является активным элементом. Используя селектор :enabled, мы отыскиваем его и вставляем в него значение "активный элемент". :disabled

Этот селектор выбирает все неактивные элементы. Пример представлен в листинге 24.39. Листинг 24.39. Использование селектора :disabled <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :disabled</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <form> <input name="email" disabled="disabled" /> <input name="id" /> </form> <script type="text/javascript"> <!-$(document).ready(function(){ $("input:disabled").val("элемент неактивен"); }); --> </script> </body> </html>


Часть V. jQuery

328

Пример в листинге 24.39 повторяет предыдущий пример за тем исключением, что теперь мы используем селектор :disabled, чтобы найти неактивный элемент и подставить в него значение "элемент неактивен". :checked

Этот селектор выбирает все отмеченные элементы или, другими словами, все элементы, которые имеют атрибут checked и значение этого атрибута checked. Типичный пример — элементы checkbox и radio (листинг 24.40). Листинг 24.40. Использование селектора :checked <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :checked</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { color:#f00; } </style> </head> <body> <form> <input type="checkbox" name="news" value="Hourly" checked="checked" /> <input type="checkbox" name="news" value="Daily" /> <input type="checkbox" name="news" value="Weekly" /> <input type="checkbox" name="news" value="Monthly" checked="checked" /> <input type="checkbox" name="news" value="Yearly" /> </form> <div></div> <script type="text/javascript"> <!-$(document).ready(function(){ function countChecked() { var n = $("input:checked").length; $("div").text((n == 1 ? "отмечен - " : "отмечено - ") + n);


Глава 24. Селекторы jQuery

329

} countChecked(); $(":checkbox").click(countChecked); }); --> </script> </body> </html>

Здесь форма содержит пять элементов checkbox, два из которых отмечены по умолчанию. Для того чтобы немножко "оживить" пример, напишем небольшую функцию countChecked, которая будет подсчитывать количество отмеченных элементов checkbox и выводить результат в виде текстового сообщения в элемент div. Вызывать функцию countChecked будем как при загрузке страницы, так и при щелчке на любом из элементов checkbox. Проверьте — это работает. :selected

Этот селектор служит для отбора выбранных элементов выпадающего списка. Типичный пример — выбранная опция в элементе select (листинг 24.41).

Листинг 24.41. Использование селектора :selected <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование селектора :selected</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { color:#f00; } </style> </head> <body> <select name="fruit" multiple="multiple">


Часть V. jQuery

330 <option>Груша</option>

<option>Яблоко</option>

<option>Слива</option> <option>Банан</option>

<option>Ананас</option>

<option>Апельсин</option> </select> <div></div> <script type="text/javascript"> <!-$(document).ready(function(){ $("select").change(function () { var str = ""; $("select option:selected").each(function () { str += $(this).text() + " "; }); $("div").text(str); }); }); --> </script> </body> </html>

В листинге 24.41 описан элемент select с возможностью выбора нескольких опций. С помощью селектора :selected мы отыскиваем все выбранные опции и добавляем их значение в строку, которую впоследствии вставляем в элемент div.


Глава 25

События в jQuery Интернет не был бы Интернетом, если бы страницы, появляющиеся на экране монитора, не умели реагировать на различные события, будь то щелчок мышью на элементе или нажатие клавиши. Так что это действительно очень важный раздел, без знакомства с которым было бы довольно сложно использовать возможности библиотеки jQuery в части работы с AJAX. Если рассмотренные ранее селекторы jQuery помогают выбрать из объектной модели документа (DOM) необходимые элементы, то многочисленные методы jQuery, включенные в раздел событий, помогают связывать с выбранными элементами определенные события и вызывать функции-обработчики этих событий. Наиболее важной функцией, включенной в модуль событий, несомненно, является ready(fn). Знакомство с библиотекой jQuery мы начинали именно с нее, но, учитывая, что повторение никогда не бывает лишним и что эта функция присутствует практически в любом jQuery-коде, рассмотрим ready(fn) еще раз. ready(fn)

С событием ready можно связать функцию, которая должна быть выполнена всякий раз, когда объектная модель документа готова к обходу и манипуляциям с ней. Посмотрим еще раз на простейший пример, приведенный в листинге 25.1. Листинг 25.1. Использование события ready <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head>


Часть V. jQuery

332 <title>Использование события ready(fn)</title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ alert("Hello, world!"); }); --> </script> </body> </html>

В данном примере в момент готовности объектной модели документа к работе мы увидим знакомое сообщение: "Hello, world!". Использование этого метода позволит вызывать необходимую функцию непосредственно в момент готовности объектной модели документа (DOM) к работе, т. е. пользователю не придется ожидать окончания загрузки какихлибо данных с удаленных ресурсов (баннеры, счетчики и т. п.), чтобы приступить к работе с вашим приложением.

Помощники при работе с событиями Мы рассмотрим часто используемые методы библиотеки jQuery, которые помогают при отслеживании и обработке событий. Большинство этих методов являются "парными". Если вызываемому методу передается аргумент, которым обычно является функция-обработчик данного события, то результат соответствует ожиданию — метод действительно свяжет набор элементов с определенным событием. Если же аргумент методу не передается — будет вызвано соответствующее событие для набора элементов, определенного в селекторе. П РИМЕЧАНИЕ

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


Глава 25. События в jQuery

333

blur()

Метод blur() вызывает событие blur для каждого выбранного элемента. Пример приведен в листинге 25.2. Листинг 25.2. Использование метода blur() <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода blur()</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <input type="text" name="One" /> <input type="text" name="Two" /> <script type="text/javascript"> <!-$(document).ready(function(){ $("input").focus(function() { $(this).blur(); }); }); --> </script> </body> </html>

В этом примере пользователь никогда не сможет передать фокус ни одному из элементов input. Посмотрите внимательно на JavaScript-код — как только любой из элементов input получает фокус, мы сразу же вызываем для него событие blur(), по которому этот элемент сразу же теряет его. blur(fn)

Метод blur(fn) связывает функцию fn с событием blur для каждого выбранного элемента. Пример приведен в листинге 25.3.


Часть V. jQuery

334

Листинг 25.3. Использование метода blur(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода blur(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> span { display:none; color:#f00; } </style> </head> <body> <p><input type="text" /> <span>Событие blur!</span></p> <p><input type="password" /> <span>Событие blur!</span></p> <script type="text/javascript"> <!-$(document).ready(function(){ $("input").blur(function () { $(this).next("span").css("display","inline").fadeOut(1500); }); }); --> </script> </body> </html>

Здесь мы связали некоторую функцию с событием blur, которое возникает в элементах input. Эта функция отыскивает элемент span, следующий непосредственно за тем элементом input, который потерял фокус, делает его видимым (сначала элементы span скрыты с помощью CSS), устанавливая CSSсвойству display значение inline, и для большей эффектности "гасит" этот же элемент span в течение полутора секунд.


Глава 25. События в jQuery

335

П РИМЕЧАНИЕ

Событие blur возникает, когда элемент теряет фокус через указатель мыши или через нажатие клавиши <Tab>. change()

Метод change() вызывает событие change для каждого выбранного элемента. change(fn)

Метод change(fn) связывает функцию fn с событием change для каждого выбранного элемента. Пример приведен в листинге 25.4. Листинг 25.4. Использование метода change(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода change(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <input type="text" name="One" /><input type="text" name="Two" /> <script type="text/javascript"> <!-$(document).ready(function(){ $("input").change(function() { var name = $(this).attr("name"); var val = $(this).attr("value"); alert('Значение для ' + name + ' = ' + val); }); }); --> </script> </body> </html>

В данном примере с элементами input связано событие change. При его наступлении выполняется код, который получает значения атрибутов name


Часть V. jQuery

336

и value того элемента input, в котором произошло событие глядности эти данные выводятся в окне alert.

change.

Для на-

П РИМЕЧАНИЕ

Событие change возникает, когда элемент теряет фокус, и его значение было изменено по сравнению с моментом получения фокуса. click()

Метод click() вызывает событие click для каждого выбранного элемента. click(fn)

Метод click(fn) связывает функцию fn с событием click для каждого выбранного элемента. Пример приведен в листинге 25.5.

Листинг 25.5. Использование метода click(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода click(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { background-color:#00f; color:#fff; font-weight:bold; padding:0 10px; cursor:pointer; } </style> </head> <body> <p>Первый параграф</p> <p>Второй параграф</p> <p>Третий параграф</p> <script type="text/javascript">


Глава 25. События в jQuery

337

<!-$(document).ready(function(){ $("p").click(function() { $(this).slideUp(); }); }); --> </script> </body> </html>

Данный пример связывает элементы p с событием click. При возникновении этого события на каком-либо из параграфов, этот параграф будет скрыт. П РИМЕЧАНИЕ

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

Метод dblclick() вызывает событие элемента.

dblclick

для каждого выбранного

dblclick(fn)

Метод dblclick(fn) связывает функцию fn с событием dblclick для каждого выбранного элемента. Пример представлен в листинге 25.6. Листинг 25.6. Использование метода dblclick(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода dblclick(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { background:#00f;


Часть V. jQuery

338 color:#fff; height:100px; width:150px; padding:10px; } div.dbl { background:#0ff; color:#000; } </style> </head> <body> <div>Сделайте двойной щелчок на этом элементе</div> <script type="text/javascript"> <!-$(document).ready(function(){ $("div").dblclick(function () { $(this).toggleClass("dbl"); }); }); --> </script> </body> </html>

Пример, приведенный в листинге 25.6, связывает элементы div с событием dblclick. При каждом возникновении этого события для элемента div переключается CSS-класс с именем dbl. П РИМЕЧАНИЕ

Событие dblclick возникает, когда пользователь совершает двойной щелчок мышью над элементом. focus()

Метод focus() вызывает событие focus для каждого выбранного элемента. focus(fn)

Метод focus(fn) связывает функцию fn с событием focus для каждого выбранного элемента. Пример представлен в листинге 25.7.


Глава 25. События в jQuery

339

Листинг 25.7. Использование метода focus(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода focus(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> span { display:none; } </style> </head> <body> <p><input type="text" name="txt1" /><span>focus</span></p> <p><input type="password" name="psw1" /><span>focus</span></p> <p><input type="text" name="txt2" /><span>focus</span></p> <p><input type="text" name="txt3" /><span>focus</span></p> <script type="text/javascript"> <!-$(document).ready(function(){ $("input").focus(function () { $(this).next("span").css("display","inline").fadeOut(1500); }); $("input[name='txt2']").focus(); }); --> </script> </body> </html>

В листинге 25.7 мы связываем событие focus с элементами input. При возникновении этого события для элемента input выполняется функция, которая отыщет элемент span, следующий непосредственно за элементом input, получившим фокус, сделает этот элемент видимым и вновь скроет в течение полутора секунд. Интересна и последняя строка JavaScript-кода. В ней вызывается событие focus для элемента input, который имеет атрибут name


Часть V. jQuery

340

со значением txt2. Если говорить проще — при загрузке страницы фокус будет передан указанному элементу.

П РИМЕЧАНИЕ

Событие focus возникает, когда элемент принимает фокус через указатель мыши или через нажатие клавиши <Tab>. keydown()

Метод keydown() вызывает событие keydown для каждого выбранного элемента. keydown(fn)

Метод keydown(fn) связывает функцию fn с событием keydown для каждого выбранного элемента.

Листинг 25.8. Использование метода keydown(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода keydown(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <p>Нажмите какую-нибудь клавишу.</p> <script type="text/javascript"> <!-$(document).ready(function(){ $(window).keydown(function(event){ alert('Код нажатой клавиши: ' + event.keyCode); }); }); --> </script> </body> </html>


Глава 25. События в jQuery

341

В листинге 25.8 при возникновении события keydown мы выполним функцию, которая сообщит нам код нажатой клавиши. П РИМЕЧАНИЕ

Событие keydown возникает, когда нажата какая-либо клавиша. keyup()

Метод keyup() вызывает событие keyup для каждого выбранного элемента. keyup(fn)

Метод keyup(fn) связывает функцию fn с событием keyup для каждого выбранного элемента. Пример приведен в листинге 25.9. Листинг 25.9. Использование метода keyup(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода keyup(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <p>Нажмите и затем отпустите клавишу Escape.</p> <script type="text/javascript"> <!-$(document).ready(function(){ $(window).keyup(function(event){ if (event.keyCode == 27) { alert('Escape!'); } }); }); --> </script> </body> </html>


Часть V. jQuery

342

В этом примере при возникновении события keyup мы выполним функцию, которая проверит код нажатой клавиши, и если этот код соответствует клавише <Esc>, мы увидим соответствующее сообщение. Обратите внимание, что событие возникает именно в момент освобождения клавиши. П РИМЕЧАНИЕ Событие keyup возникает, когда отпущена какая-либо клавиша.

keypress()

Метод keypress() вызывает событие элемента.

keypress

для каждого выбранного

keypress(fn)

Метод keypress(fn) связывает функцию fn с событием keypress для каждого выбранного элемента. Пример представлен в листинге 25.10. Листинг 25.10. Использование метода keypress(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода keypress(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> input { margin:10px; } p { color:#00f; margin:10px; font-size:18px; } div { color:#f00; } </style> </head>


Глава 25. События в jQuery

343

<body> <input type="text" /> <p>Добавленный текст: </p> <div></div> <script type="text/javascript"> <!-$(document).ready(function(){ $("input").keypress(function (e) { if (e.which == 32 || (1072 <= e.which && e.which <= 1103)) { var c = String.fromCharCode(e.which); $("p").append($("<span/>")) .children(":last") .append(document.createTextNode(c)); } else if (e.which == 8) { $("p").children(":last").remove(); } $("div").text(e.which); }); }); --> </script> </body> </html>

В HTML-коде из листинга 25.10 присутствуют: текстовое поле, параграф, в конец которого будет добавляться очередной символ, и элемент div, в котором будет отображаться код нажатой клавиши. Из JavaScript-кода видно, что текстовое поле связано с событием keypress, при наступлении которого выполнится функция, обрабатывающая нажатие клавиш в зависимости от их кода. Обратим внимание на условие. В конец элемента p будет добавлен элемент span, в который, в свою очередь, будет вставлен символ, соответствующий нажатой клавише. Но произойдет это только в том случае, если нажатая клавиша является либо пробелом, либо одним из символов русского алфавита (исключая "ё"). Если же будет нажата клавиша <Backspace>, последний элемент, находящийся внутри параграфа, будет удален. В элемент div код нажатой клавиши вставляется в качестве простого текста. П РИМЕЧАНИЕ Событие keypress возникает, когда нажата и затем отпущена какая-либо клавиша. Точнее это можно определить как "щелчок" по клавише или как последовательность событий keydown → keypress → keyup.


Часть V. jQuery

344 mousedown(fn)

Метод mousedown(fn) связывает функцию fn с событием mousedown для каждого выбранного элемента. mouseup(fn)

Метод mouseup(fn) связывает функцию fn с событием mouseup для каждого выбранного элемента. Пример представлен в листинге 25.11. Листинг 25.11. Использование методов mousedown(fn) и mouseup(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование методов mousedown(fn) и mouseup(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <p>Нажмите и отпустите левую/правую кнопку мыши над этим параграфом.</p> <script type="text/javascript"> <!-$(document).ready(function(){ $("p").mouseup(function(){ $(this).append('<span style="color:#f00;"> Mouse up!</span>'); }).mousedown(function(){ $(this).append('<span style="color:#00f;"> Mouse down!</span>'); }); }); --> </script> </body> </html>

В листинге 25.11 приведен простой пример использования методов mousedown(fn) и mouseup(fn). При нажатии кнопки мыши в параграф будет добавлен элемент span, содержащий текст "Mouse down!", а в тот момент, когда кнопка мыши будет отпущена, туда же добавится элемент span с текстом "Mouse up!".


Глава 25. События в jQuery

345

П РИМЕЧАНИЕ

Событие mousedown возникает, когда кнопка мыши нажата над элементом. Событие mouseup возникает, когда кнопка мыши отпущена над элементом. resize(fn)

Метод resize(fn) связывает функцию fn с событием resize для каждого выбранного элемента. Пример приведен в листинге 25.12. Листинг 25.12. Использование метода resize(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода resize(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <p>Попробуйте изменить размеры области просмотра документа.</p> <script type="text/javascript"> <!-$(document).ready(function(){ $(window).resize(function(){ var h = $(this).height(); var w = $(this).width(); $("p").text('высота: ' + h + ' px, ширина: ' + w + 'px'); }); }); --> </script> </body> </html>

Данный пример демонстрирует использование метода resize(fn). При изменении размеров области просмотра документа мы получаем размеры window в пикселах и выводим сообщение в элемент p. П РИМЕЧАНИЕ

Событие resize возникает, когда область просмотра документа изменяет свои размеры.


Часть V. jQuery

346 scroll(fn)

Метод scroll(fn) связывает функцию fn с событием scroll для каждого выбранного элемента. Пример приведен в листинге 25.13. Листинг 25.13. Использование метода scroll(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода scroll(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { color:#00f; } p { color:#0f0; margin:50px 0; } span { color:#f00; display:none; } </style> </head> <body> <div>Попробуйте прокрутить окно просмотра с помощью полосы прокрутки или колесика мыши.</div> <p>Какой-то параграф - <span>Событие scroll!</span></p> <script type="text/javascript"> <!-$(document).ready(function(){ $("p").clone().appendTo(document.body); $("p").clone().appendTo(document.body); $("p").clone().appendTo(document.body); $("p").clone().appendTo(document.body);


Глава 25. События в jQuery

347

$(window).scroll(function () { $("span").css("display", "inline").fadeOut("slow"); }); }); --> </script> </body> </html>

Из данного HTML-кода нас интересует элемент p. Внутри него находится элемент span, скрытый до поры с помощью CSS-правил. Первое, что мы проделаем в JavaScript-коде, — "размножим" этот элемент p, воспользовавшись для этого методом clone(). Вы можете увидеть четыре совершенно одинаковых строки кода. После выполнения первой строки, в DOM будет вставлен еще один параграф, в точности соответствующий уже существующему параграфу. После выполнения второй строки их станет четыре, после третьей — восемь и, наконец, шестнадцать. Но для нас это не главное. Цель всего этого — лишь дать наглядность файлу примера. Нас интересует событие scroll, при наступлении которого мы будем отображать элементы span для каждого параграфа, воздействуя через код JavaScript на CSS-свойства и затем их же не спеша скрывать. Если вы попробуете прокрутить документ с помощью колесика мыши или воспользуетесь полосой прокрутки, то увидете, как появляются и медленно пропадают около каждого параграфа сообщения — "Событие scroll!". П РИМЕЧАНИЕ

Событие scroll возникает, когда прокручивается область просмотра документа. select()

Метод select() вызывает событие select для каждого выбранного элемента. select(fn)

Метод select(fn) связывает функцию fn с событием select для каждого выбранного элемента. Пример приведен в листинге 25.14. П РИМЕЧАНИЕ

Не следует путать событие select с событием change, которое возникает, когда пользователь изменяет выбранную опцию в HTML-элементе select.


348

Часть V. jQuery

Листинг 25.14. Использование метода select(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода select(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { color:#00f; } div { color:#f00; } input { width:250px; } </style> </head> <body> <p>С помощью мыши выделите часть текста в любом из элементов input.</p> <input type="text" value="Немного какого-то текста" /> <input type="text" value=", предназначенного для теста" /> <div></div> <script type="text/javascript"> <!-$(document).ready(function(){ $("input").select(function () { $("div").text("Вы что-то выделили!").show().fadeOut(1000); }); }); --> </script> </body> </html>

В данном примере имеются два элемента input, содержащие какой-то текст. С этими элементами связано событие select, при наступлении которого или,


Глава 25. События в jQuery

349

другими словами, тогда, когда вы попробуете выделить с помощью мыши часть текста или весь текст в любом из элементов input, в элемент div будет вставлено сообщение "Вы что-то выделили!". Это сообщение быстро появится и медленно пропадет.

П РИМЕЧАНИЕ

Событие select возникает, когда пользователь выделяет какой-либо текст в текстовом поле элементов input или textarea. submit()

Метод submit() вызывает событие submit для каждого выбранного элемента. submit(fn)

Метод submit(fn) связывает функцию fn с событием submit для каждого выбранного элемента. Пример приведен в листинге 25.15.

Листинг 25.15. Использование метода submit(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода submit(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { margin:0; color:#00f; } div, p { margin-left:10px; } span { color:#f00; } </style> </head> <body> <p>Введите 'correct' в текстовое поле.</p>


Часть V. jQuery

350 <form action="javascript:alert('Удачно!');"> <div> <input type="text" /> <input type="submit" /> </div> </form> <span></span> <script type="text/javascript"> <!-$(document).ready(function(){ $("form").submit(function() { if ($("input:first").val() == "correct") { $("span").text("Отправлено.").show(); return true; }

$("span").text("Неправильное значение!").show().fadeOut(1000); return false; }); }); --> </script> </body> </html>

Разберемся в примере, который приведен в листинге 25.15. У нас имеется форма, в которой есть только одно поле для ввода текстовой информации и кнопка submit. Имеется на странице и тег span — сюда мы будем выводить текстовые сообщения. Первое, что мы делаем, — с помощью jQuery-селектора выбираем форму, с которой будем в дальнейшем работать. Затем связываем эту форму с событием submit. При возникновении этого события выполняем функцию, которую тут же и придумаем. Например, будем проверять значение, введенное в поле ввода. Если это значение будет "correct", тогда вставляем в элемент span сообщение "Отправлено" и возвращаем true, чтобы разрешить браузеру выполнить действие по умолчанию. Если же введенное значение будет иным, вставим в элемент span сообщение "Неправильное значение!" и вернем false, чтобы предотвратить поведение браузера по умолчанию. Методы show() и fadeOut() мы используем для внесения эффектов при отображении текста.


Глава 25. События в jQuery

351

П РИМЕЧАНИЕ

Событие submit возникает, когда происходит отправка данных из формы. Иными словами, либо совершен щелчок мышью на элементе input с типом submit, либо нажата клавиша <Enter>, когда этот элемент имеет фокус. unload(fn)

Метод unload(fn) связывает функцию fn с событием unload для каждого выбранного элемента. Листинг 25.16. Использование метода unload(fn) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода unload(fn)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <a href="http://jquery.com">jQuery.com</a> <script type="text/javascript"> <!-$(document).ready(function(){ $(window).unload(function () { alert("До скорой встречи!"); }); }); --> </script> </body> </html>

Очень простой пример — при возникновении события unload, например, если вы переходите по ссылке на официальный сайт jQuery (или просто закрываете браузер), то сначала увидите сообщение "До скорой встречи!". П РИМЕЧАНИЕ

Событие unload возникает, когда текущая страница выгружается из браузера.


Глава 26

Манипуляции элементами в jQuery Научившись создавать наборы элементов и связывать с ними определенные события, мы уже кое-что можем делать. Но для того чтобы действительно оживить страницы, необходимо научиться манипулировать элементами объектной модели документа. Библиотека jQuery предлагает весьма богатый арсенал способов, позволяющих манипулировать элементами в DOM. Можно воздействовать на атрибуты элемента, устанавливая им нужные значения или присваивать значения CSS-свойствам элементов, изменяя их отображение. Мы же разберем методы библиотеки jQuery, которые помогают "на лету" создавать необходимые элементы и помещать их в требуемое место объектной модели документа. В листингах предыдущих глав вы уже встречали некоторые из этих методов.

Изменение содержимого элементов

В этом разделе мы познакомимся с несколькими, пожалуй, самыми часто используемыми методами, позволяющими получать или изменять содержимое элементов. И обратите внимание — эти методы, как и многие другие методы jQuery, тоже являются "парными". html()

Получает содержимое (innerHTML) в виде HTML для первого элемента набора. Этот метод не работает с XML-документами, но для XHTMLдокументов вполне подходит. Метод возвращает строку. Пример приведен в листинге 26.1.


Глава 26. Манипуляции элементами в jQuery

353

Листинг 26.1. Использование метода html() <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода html()</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> span { color:#00f; } </style> </head> <body> <ul> <li>Пункт <span>меню</span> 1</li> <li>Пункт <span>меню</span> 2</li> <li>Пункт <span>меню</span> 3</li> </ul> <button>Получить HTML-код</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { alert($("ul").html()); }); }); --> </script> </body> </html>

В листинге 26.1 приведен очень простой пример. По щелчку на кнопке мы сможем получить строку HTML-кода, являющуюся внутренним содержимым элемента ul. Попробуйте самостоятельно дополнить пример, чтобы получить HTML-код только из первого или последнего пункта меню.


Часть V. jQuery

354 html(val)

Устанавливает содержимое в виде HTML для каждого элемента набора. Точно так же, как и в случае использования метода без передачи параметра, не работает c документами XML, но его можно использовать с XHTML. Метод возвращает объект jQuery. Пример приведен в листинге 26.2. Листинг 26.2. Использование метода html(val) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода html(val)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; margin:10px; border:1px dotted #00f; width:100px; height:50px; float:left; } textarea { clear:left; display:block; width:400px; height:100px; margin-left:10px; } button { margin-left:10px; } </style> </head> <body> <div></div>


Глава 26. Манипуляции элементами в jQuery

355

<div></div> <div></div> <textarea></textarea> <button>Вставить HTML-код</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { var val = $("textarea").attr("value"); $("div").html(val); }); }); --> </script> </body> </html>

В данном примере мы видим три элемента div, элемент textarea и кнопку button. Мы хотим сделать так, чтобы, после того как пользователь введет какой-либо HTML-код в текстовое поле и нажмет кнопку, этот HTML-код был вставлен во все элементы div, которые есть на странице. Нет ничего проще! Связываем элемент button с событием click, а в качестве обработчика события выполняем простую функцию, которая сначала запишет в переменную val значение атрибута value элемента textarea, а затем с помощью метода html() вставит этот код во все элементы div. П РИМЕЧАНИЕ Обратите внимание, что в случае вызова метода без передачи ему параметра будет получен HTML только для одного, первого элемента набора. В случае, если параметр передается, т. е. мы добавляем HTML-код в отобранные элементы, этот код будет добавлен в каждый элемент набора. text()

Получает содержимое в виде текста для всех элементов набора. Результатом является строка, которая содержит "объединеный" текст из всех элементов набора. Метод работает с HTML-, XHTML- и XML-документами, однако не может быть использован для элементов input (в этом случае нужно получить значение атрибута value). Пример приведен в листинге 26.3.


Часть V. jQuery

356

Листинг 26.3. Использование метода text() <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода text()</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { padding:10px; margin:10px; border:1px dotted #00f; } span,em { color:#0f0; } button { margin-left:10px; } </style> </head> <body> <p>Текст первого параграфа</p>

<p>Текст <span>второго</span> параграфа</p> <p>Текст <em>третьего</em> параграфа</p> <button>Получить текст</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { alert($("p").text()); }); }); --> </script> </body> </html>


Глава 26. Манипуляции элементами в jQuery

357

Для демонстрации работы метода text() используем пример, приведенный в листинге 26.3. На странице имеются кнопка и три параграфа с текстом. Причем второй и третий параграфы имеют внутри себя еще и HTML-элементы. При щелчке на кнопке мы увидим в сообщении текст всех трех параграфов, без каких бы то ни было элементов HTML-разметки. Попробуйте изменить этот пример так, чтобы получить текст только второго параграфа на странице. text(val)

Устанавливает текстовое содержимое для всех элементов набора. Этот метод очень похож на метод html(val), однако он экранирует HTML, заменяя символы < и > их HTML-сущностями. Точно так же метод text(val) не может быть использован для установки значения элементов input (в этом случае нужно установить значение атрибута value). Метод возвращает объект jQuery. Пример приведен в листинге 26.4. Листинг 26.4. Использование метода text(val) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода text(val)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; margin:10px; border:1px dotted #00f; width:100px; height:50px; float:left; } textarea { clear:left; display:block; width:400px; height:100px; margin-left:10px; }


Часть V. jQuery

358 button { margin-left:10px; } </style> </head> <body> <div></div> <div></div> <div></div> <textarea></textarea> <button>Вставить текст</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { var val = $("textarea").attr("value"); $("div").text(val); }); }); --> </script> </body> </html>

Этот пример очень похож на пример, где мы разбирали работу метода html(val). Точно так же имеется текстовое поле, куда предполагается вводить текст, и кнопка, при нажатии на которую этот текст будет вставлен во все элементы div, присутствующие на странице. Попробуйте ввести в текстовое поле какой-либо HTML-код и нажать кнопку. В отличие от примера из листинга 26.2 вы увидите этот код в виде текста. П РИМЕЧАНИЕ Необходимо помнить, что при изменении содержимого элементов с использованием рассмотренных выше методов html(val) и text(val) старое содержимое будет замещено новым.

Вставка содержимого внутрь элементов

Здесь мы познакомимся с несколькими методами, которые позволяют вставлять содержимое внутрь других элементов, сохраняя при этом уже существующее там содержимое. С их помощью можно вставлять новое содержимое


Глава 26. Манипуляции элементами в jQuery

359

до или после имеющегося содержимого, или например, вставлять целый набор элементов внутрь других элементов, определенных в селекторе. Ничего страшного, если это звучит несколько запутанно — мы разберем эти методы на простейших примерах, и тогда все станет предельно ясно. append(content)

Добавляет содержимое внутрь каждого элемента набора, после существующего содержимого. Передаваемый параметр может представлять собой строку, элемент или объект jQuery. Пример приведен в листинге 26.5.

Листинг 26.5. Использование метода append(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода append(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { padding:10px; border:1px dotted #0f0; } em { color:#00f; font-weight:bold; } </style> </head> <body> <p>Я хочу сказать </p>

<button>Сказать</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("p").append("<em>Привет! </em>"); });


Часть V. jQuery

360 }); --> </script> </body> </html>

В данном примере имеется параграф с текстом "Я хочу сказать". Нашей задачей будет добавить в этот параграф, после имеющегося там текста, элемент em со словом "Привет!". Эту задачу мы с успехом решаем с помощью метода append(), передавая ему в качестве параметра требуемый HTML-код. prepend(content)

Добавляет содержимое внутрь каждого элемента набора, перед существующим содержимым. Передаваемый параметр может представлять собой строку, элемент или объект jQuery. Пример приведен в листинге 26.6. Листинг 26.6. Использование метода prepend(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода prepend(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { padding:10px; border:1px dotted #0f0; } em { color:#00f; font-weight:bold; } </style> </head> <body> <p>говорю я Вам!</p>

<button>Сказать</button>


Глава 26. Манипуляции элементами в jQuery

361

<script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("p").prepend("<em>Привет, </em>"); }); }); --> </script> </body> </html>

Очень похожая на предыдущий пример задача приведена в листинге 26.6, только теперь нам необходимо вставить элемент em со словом "Привет," в параграф перед имеющимся там содержимым. Используем для этого метод prepend(), и, пожалуйста, результат — "Привет, говорю я Вам!". appendTo(content)

Добавляет все элементы набора в другой, указанный набор элементов, после существующего содержимого. Пример приведен в листинге 26.7. Листинг 26.7. Использование метода appendTo(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода appendTo(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; border:1px dotted #0f0; } span { margin-left:10px; color:#fff; font-weight:bold;


Часть V. jQuery

362 background-color:#00f; } </style> </head> <body> <span>Первый элемент span </span> <span>Второй элемент span </span>

<div id="foo">Элементы span будут вставлены сюда --></div>

<button>Вставить</button>

<script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("span").appendTo("#foo"); }); }); --> </script> </body> </html>

Теперь мы имеем два элемента span и элемент div с находящимся внутри него текстом. Пусть нам необходимо все элементы span, имеющиеся на странице, вставить в элемент div после имеющегося там содержимого. Очень просто — выбираем в набор все элементы span и применяем метод appendTo(), передав ему в качестве параметра идентификатор элемента div. В результате этого оба элемента span будут перемещены на свое новое место. prependTo(content)

Добавляет все элементы набора в другой, указанный набор элементов, перед существующим содержимым. Пример приведен в листинге 26.8. Листинг 26.8. Использование метода prependTo(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода prependTo(content)</title>


Глава 26. Манипуляции элементами в jQuery

363

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; border:1px dotted #f00; } span { margin-right:10px; font-weight:bold; background-color:#0ff; } </style> </head> <body> <span>Первый элемент span </span> <span>Второй элемент span </span>

<div id="foo"><-- Сюда будут вставлены элементы span</div>

<button>Вставить</button>

<script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("span").prependTo("#foo"); }); }); --> </script> </body> </html>

Данный пример содержит HTML-разметку, практически аналогичную разметке из предыдущего примера. Разница в поставленной задаче — теперь все элементы span необходимо вставить в параграф перед имеющимся содержимым. Применяем метод prependTo(), и в результате оба элемента span оказываются там, где и требовалось — внутри параграфа, перед его содержимым. При использовании методов, описаных в этом разделе, необходимо учитывать следующее обстоятельство: если добавление производится в единственный элемент, то добавляемый элемент удаляется из своего прежнего место-


Часть V. jQuery

364

положения — выполняется операция перемещения. Если добавление производится в несколько элементов, то добавляемый элемент останется на своем месте — будет выполнена операция копирования.

Вставка содержимого снаружи элементов Немного смешной получился заголовок, но он, тем не менее, довольно верно отражает суть тех методов, которые будут представлены в этом разделе. Эти методы позволяют добавлять элементы сразу до или сразу после необходимого элемента. after(content)

Вставляет содержимое после каждого элемента набора. Передаваемый параметр может представлять собой строку, элемент или объект jQuery. Пример приведен в листинге 26.9.

Листинг 26.9. Использование метода after(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода after(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; border:1px dotted #f00; } span { margin-right:10px; font-weight:bold; background-color:#0ff; } </style> </head> <body> <ul>


Глава 26. Манипуляции элементами в jQuery

365

<li><a href="26.6.html">Пример 26.6</a></li> <li><a href="26.7.html">Пример 26.7</a></li> <li><a href="26.8.html">Пример 26.8</a></li> <li><a href="http://jquery.com/">ссылка на jQuery</a></li> </ul> <div>Некоторое содержимое, где встречаются различные <a href="http://www.sape.ru/">ссылки</a> на самые разные интересные <a href="http://www.yandex.ru/">сайты</a>, которые можно <a href="http://www.town-explorer.ru/">посмотреть</a>.</div> <button>Применить</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("a[href^=http]").after("<img src='img-26.9.gif' alt='link' />") .attr("target","_blank"); }); }); --> </script> </body> </html>

В листинге 26.9 приведен пример, который может быть применен и на практике. В HTML-разметке есть ненумерованный список и элемент div, внутри которых имеется некоторое содержимое, в том числе и гиперссылки. Гиперссылки ведут как на внутренние страницы ресурса, так и на внешние ресурсы. Нам нужно добавить небольшую картинку сразу же после гиперссылок, указывающих на внешние ресурсы, и сделать так, чтобы эти гиперссылки открывались в новом окне. Сначала мы выбираем только те гиперссылки на странице, которые имеют атрибут href, начинающийся с последовательности символов "http", а затем применяем к этому набору метод after(), передав ему в качестве параметра HTML-код, описывающий необходимую картинку. Наконец, с помощью метода attr() устанавливаем значение _blank атрибута target для всех выбранных ссылок. before(content)

Вставляет содержимое перед каждым элементом набора. Передаваемый параметр может представлять собой строку, элемент или объект jQuery. Пример приведен в листинге 26.10.


Часть V. jQuery

366

Листинг 26.10. Использование метода before(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода before(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; border:1px dotted #f00; } p { padding:10px; border:1px dotted #00f; } span { margin-right:10px; font-weight:bold; } </style> </head> <body> <p>Какой-то текст в элементе p</p> <div>Какой-то текст в элементе div</div> <p>Какой-то текст в элементе p</p> <div>Какой-то текст в элементе div</div> <p>Какой-то текст в элементе p</p> <button>Применить</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("p").before("<span>Элемент span вставлен перед параграфом.</span>"); }); });


Глава 26. Манипуляции элементами в jQuery

367

--> </script> </body> </html>

В листинге 26.10 есть несколько элементов p и div с некоторым текстом. Необходимо перед всеми элементами p вставить элемент span с текстом. Решим задачу с помощью метода before(). Сначала отберем в набор все элементы p, а затем применим метод before(), передав ему необходимый HTML-код. insertAfter(content)

Вставляет все элементы набора после другого, указанного в параметре метода, набора элементов. Пример приведен в листинге 26.11. Листинг 26.11. Использование метода insertAfter(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода insertAfter(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; border:1px dotted #f00; } p { padding:10px; border:1px dotted #00f; } </style> </head> <body> <p>Это элемент p</p>

<div id="foo">Это элемент div с идентификатором #foo</div> <div>Элемент div</div>


Часть V. jQuery

368 <button>Применить</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("p").insertAfter("#foo"); }); }); --> </script> </body> </html>

В данной HTML-разметке присутствуют элемент p и два элемента div. Необходимо вставить элемент p перед элементом div, имеющим идентификатор #foo. Выбираем в набор элементы p и применяем метод insertAfter(), указывая в качестве параметра идентификатор элемента, перед которым необходимо осуществить вставку. insertBefore(content)

Вставляет все элементы набора перед другим, указанным в параметре метода, набора элементов. Пример приведен в листинге 26.12. Листинг 26.12. Использование метода insertBefore(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода insertBefore(content)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> div { padding:10px; border:1px dotted #f00; } p { padding:10px;


Глава 26. Манипуляции элементами в jQuery

369

border:1px dotted #00f; } </style> </head> <body> <div>Элемент div</div> <div id="foo">Это элемент div с идентификатором #foo</div> <p>Это элемент p</p> <button>Применить</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("p").insertBefore("#foo"); }); }); --> </script> </body> </html>

HTML-разметка, показанная в листинге 26.12, очень похожа на разметку из предыдущего примера. Поменялся только порядок следования элементов. Ну и задача, конечно. Теперь элемент p необходимо вставить перед элементом с идентификатором #foo. Точно так же отбираем в набор элементы p, но применяем к набору уже метод insertBefore(), указывая в параметре идентификатор #foo.

Обертывание элементов В этом разделе коротко поговорим о некоторых из методов, которые помогут при работе с еще одним типом манипулирования объектной моделью документа — обертывание элементов или групп элементов какой-либо разметкой. Эти операции могут быть весьма полезны при добавлении дополнительных структур в документ без нарушения его оригинальной семантики. wrap(html)

Обертывает каждый элемент набора указаной HTML-разметкой.


Часть V. jQuery

370 wrapAll(html)

Обертывает все элементы набора в одну, указанную HTML-разметкой. wrapInner(html)

Обертывает все внутреннее содержимое каждого элемента набора указанной HTML-разметкой. Пример приведен в листинге 26.13. Листинг 26.13. Использование методов wrap(html), wrapAll(html) и wrapInner(html) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование методов wrap(html), wrapAll(html) и wrapInner(html)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> ul { border:1px dotted #f00; padding:20px; } li { border:1px dotted #00f; } </style> </head> <body> <div> <a href="http://www.google.com">Google</a> <a href="http://www.msn.com">MSN</a> <a href="http://www.yandex.ru">Yandex</a> <a href="http://www.rambler.ru">Rambler</a> </div> <p><button>Применить</button></p> <script type="text/javascript"> <!-$(document).ready(function(){


Глава 26. Манипуляции элементами в jQuery

371

$("button").click(function() { $("a").wrapInner("<b></b>").wrap("<li></li>"); $("li").wrapAll("<ul></ul>"); }); }); --> </script> </body> </html>

В листинге 26.13 элемент div содержит четыре гиперссылки. Не лучшее решение для визуального представления. Для отображения такой информации уместнее было бы использовать ненумерованный список. Попробуем исправить отображение этой информации с помощью jQuery. Первое, что мы сделаем, — отберем в набор все элементы a. Затем обернем их внутреннее содержимое в элементы b с помощью метода wrapInner(). Следующий шаг — оборачиваем все элементы a в элементы li с помощью метода wrap(). Теперь осталось только обернуть все элементы li в элемент ul. Для этого воспользуемся методом wrapAll(), предварительно создав набор элементов, состоящий только из элементов li. Попробуйте щелкнуть по кнопке, и вы увидите, как HTML-разметка преобразится прямо на ваших глазах.

Замещение, удаление, копирование элементов Это операции, без которых трудно представить интернет-страницу, живо реагирующую на какие-либо действия пользователей. replaceWith(content)

Замещает все элементы набора указанной HTML-разметкой или элементами DOM. Возвращает jQuery элемент, который только что был замещен, т. е. удален из DOM. Пример представлен в листинге 26.14. Листинг 26.14. Использование метода replaceWith(content) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru">


Часть V. jQuery

372 <head> <title>Использование метода replaceWith(content)</title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> button { display:block; margin:3px; color:#f00; width:200px; } div { color:#f00; border:2px solid #00f; width:200px; margin:3px; text-align:center; } </style> </head> <body> <button>First</button> <button>Second</button> <button>Third</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $(this).replaceWith("<div>" + $(this).text() + "</div>"); }); }); --> </script> </body> </html>

В HTML-коде из листинга 26.14 имеются три элемента button. Нам требуется при нажатии соответствующей кнопки замещать эту кнопку элементом div, но сохранять при этом в элементе div тот текст, который был на кнопке.


Глава 26. Манипуляции элементами в jQuery

373

Приступаем к решению. Сначала выбираем в набор элементы типа button и связываем с ними событие click. В качестве обработчика события будет выступать функция, которая с помощью метода replaceWith() заместит кнопку указанным в параметре метода HTML-кодом. this в текущем контексте будет указывать на тот элемент button, который был нажат. empty()

Удаляет все узлы-потомки из каждого элемента набора. Пример приведен в листинге 26.15. Листинг 26.15. Использование метода empty() <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода empty()</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { background:#00f; color:#fff; padding:5px; } a { color:#fff; } </style> </head> <body> <p>Привет, <span>человек</span>! <a href="javascript:;">Как дела?</a></p> <button>Вызвать метод empty() для параграфа</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("p").empty(); });


Часть V. jQuery

374 }); --> </script> </body> </html>

Здесь мы попробуем удалить все содержимое из элементов p. Для этого мы смастерили пример, где внутри элемента p имеется обычный текст, текст в элементе span и текст в гиперссылке. Метод empty() будем вызывать по щелчку мышью на кнопке. Один щелчок — и внутри параграфа ничего нет. remove([expr])

Удаляет все элементы набора из объектной модели документа. В необязательном параметре expr можно передать выражение, позволяющее отфильтровать элементы исходного набора, которые должны быть удалены. Пример представлен в листинге 26.16.

Листинг 26.16. Использование метода remove([expr]) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода remove([expr])</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { background:#00f; color:#fff; padding:5px; } </style> </head> <body> <p>Hello,</p> how are <p>you?</p> <button>Вызвать метод remove() для параграфов</button> <script type="text/javascript"> <!--


Глава 26. Манипуляции элементами в jQuery

375

$(document).ready(function(){ $("button").click(function() { $("p").remove(); }); }); --> </script> </body> </html>

В листинге 26.16 приведен пример, проще которого придумать невозможно — щелчок мышью по кнопке приведет к тому, что из объектной модели документа будут удалены все элементы p. Другими словами — у вас останется только "how are". П РИМЕЧАНИЕ Обратите внимание, что метод не удаляет эти элементы из объекта jQuery, позволяя использовать этот набор в дальнейшем. clone()

Клонирует выбранные элементы DOM. Метод clone() может пригодиться при перемещении копий элементов в другое место объектной модели документа. Пример представлен в листинге 26.17. Листинг 26.17. Использование метода clone() <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода clone()</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { background:#00f; color:#fff; padding:5px; }


Часть V. jQuery

376 </style> </head> <body> <em>Hello, </em><p>how are you?</p> <button>Клонировать!</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("em").clone().prependTo("p"); }); }); --> </script> </body> </html>

В данном примере мы копируем элементы em по щелчку мыши на кнопке Клонировать! и помещаем их перед содержимым, имеющимся в элементе p. clone(true)

Клонирует выбранные элементы DOM и все обработчики событий, связанные с ними. Метод clone(true) может пригодиться при перемещении копий элементов и связанных с ними обработчиков событий в другое место объектной модели документа. Пример приведен в листинге 26.18. Листинг 26.18. Использование метода clone(true) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование метода clone(true)</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <button>Клонировать!</button> <script type="text/javascript">


Глава 26. Манипуляции элементами в jQuery

377

<!-$(document).ready(function(){ $("button").click(function() { $(this).clone(true).insertAfter(this); }); }); --> </script> </body> </html>

Здесь щелчок мышью по кнопке Клонировать! приведет к тому, что будет создана точно такая же кнопка и помещена сразу после кнопки, на которой был совершен щелчок. Обработчик события click также будет скопирован для новой кнопки.


Глава 27

AJAX-запросы в jQuery Наконец-то мы, вооруженные минимальным необходимым набором знаний по использованию библиотеки jQuery, добрались до самого интересного места. Мы довольно подробно познакомимся с тем, как jQuery помогает осуществлять взаимодействие с механизмами AJAX.

Загрузка содержимого

Наверное, чаще всего технология AJAX применяется для получения какоголибо содержимого и добавления этого содержимого в требуемое место объектной модели документа. Давайте сначала попробуем реализовать это на примере, используя традиционный подход JavaScript. Код, который вам пришлось бы написать, приведен в листинге 27.1. Листинг 27.1. Пример выполнения AJAX-запроса традиционным способом <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript"> // Сохраняем ссылку на экземпляр объекта XMLHttpRequest var xmlHttp = createXHR(); // Функция, которая создает XMLHttpRequest function createXHR() { var xmlHttp;


Глава 27. AJAX-запросы в jQuery

379

try { // Для браузеров, следующих стандартам W3C xmlHttp = new XMLHttpRequest(); } catch(e) { // Для IE6 и ниже var XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"); // Перебираем все возможные варианты for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++) { try { // Пытаемся создать XMLHttpRequest xmlHttp = new ActiveXObject(XmlHttpVersions[i]); } catch (e) {} } } // Возвращаем объект или выводим сообщение об ошибке if (!xmlHttp) alert("Ошибка создания объекта XMLHttpRequest"); else return xmlHttp; } // Функция, которая читает файл с сервера function start() { if (xmlHttp) { // Пытаемся соединиться с сервером try { // Читаем файл с сервера xmlHttp.open("GET", "27.2.html", true); xmlHttp.onreadystatechange = responseHandler; xmlHttp.send(null); } catch (e) { // Или выводим сообщение об ошибке

alert("Невозможно соединиться с сервером:\n" + e.toString()); }


Часть V. jQuery

380 } } // Функция, которая обрабатывает ответ от сервера function responseHandler() { // Получаем ссылку на элемент <div> myDiv = document.getElementById("example"); // Продолжаем, только если код состояния 4 if (xmlHttp.readyState == 4) { // Продолжаем, только если код ответа сервера 200 if (xmlHttp.status == 200) { try { // Читаем сообщение с сервера response = xmlHttp.responseText; // Выводим сообщение myDiv.innerHTML = response; } catch(e) { // Или выводим сообщение об ошибке

alert("Ошибка чтения ответа: " + e.toString()); } } else { // Если не удалось получить данные с сервера

alert("Проблема при получении данных с сервера:\n" + xmlHttp.statusText); } } } </script> <style type="text/css"> span { color:#00f; } </style> </head> <body> <div id="example"></div> <button onclick="start()">Start</button> </body> </html>


Глава 27. AJAX-запросы в jQuery

381

А в листинге 27.2 приведен код файла, к которому мы отправляем AJAXзапрос. Посмотрите HTML-разметку — имеется заголовок, ненумерованный список и параграф. Листинг 27.2. Файл, к которому отправляется AJAX-запрос <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Файл, к которому отправляется ajax-запрос.</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <h1>Сервер приветствует Вас!</h1> <ul id="menu"> <li>Пункт меню № 1</li> <li>Пункт меню № 2</li> <li>Пункт меню № 3</li> <li>Пункт меню № 4</li> <li>Пункт меню № 5</li> </ul> <p>Какой-то текст, находящийся внутри элемента p.</p> </body> </html>

Открыв в браузере пример из листинга 27.1, вы увидите только кнопку Start. Однако при нажатии на эту кнопку будет выполнен AJAX-запрос к файлу, код которого приведен в листинге 27.2, и вы увидите, как содержимое этого файла окажется загруженным в элемент div с идентификатором #example. Очень изящно и никаких перезагрузок! А теперь, на примере из листинга 27.3, посмотрим, как сделать абсолютно то же самое с помощью метода load() библиотеки jQuery. Листинг 27.3. Пример выполнения AJAX-запроса load() <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head>


382

Часть V. jQuery

<title>Пример выполнения AJAX-запроса load().</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("#example").load("27.2.html"); }); }); --> </script> <style type="text/css"> span { color:#00f; } </style> </head> <body> <div id="example"></div> <button>Start</button> </body> </html>

Точно так же, открыв в браузере пример из листинга 27.3, вы увидите только кнопку Start. И точно так же при щелчке на ней будет загружено содержимое файла, код которого приведен в листинге 27.2. Но обратите внимание на код из листинга 27.3 — того же самого результата мы добились одной строкой кода! Все остальные заботы берет на себя библиотека jQuery. Вы уже решили, какой подход будете использовать в своих приложениях? Вместе с теми, кто решил воспользоваться помощью библиотеки jQuery, продолжим знакомство с методом load(). Надо сразу заметить — это самый простой метод, который помогает во взаимодействии с AJAX. load(url, [data], [callback])

Данный метод загружает HTML из файла и вставляет его в элемент объектной модели документа (DOM), замещая существующее ранее содержимое. Метод возвращает объект jQuery. Параметры метода: url — строка, содержащая адрес ресурса в Интернете, к которому будет отправлен запрос;


Глава 27. AJAX-запросы в jQuery

383

(необязательный аргумент) — данные в виде пар "ключ/значение", которые могут быть переданы серверу; callback (необязательный аргумент) — функция, которая может быть вызвана при окончании запроса. data

П РИМЕЧАНИЕ

Стоит обратить внимание, что для вызова функции, определенной в аргументе callback, не требуется обязательное успешное выполнение запроса. По умолчанию, без аргумента data выполняется как GET-запрос, однако, если будут переданы данные, выполнится как POST-запрос. В примере из листинга 27.3 мы с помощью метода load() загружали все содержимое HTML-страницы, но что делать, если необходимо загрузить ее не полностью, а всего лишь какую-то, но вполне конкретную часть? И эту проблему тоже можно решить. Начиная с версии jQuery 1.2, в аргументе url можно определить селектор jQuery. Указывать его необходимо через пробел.

Листинг 27.4. Пример выполнения AJAX-запроса load <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Пример выполнения AJAX-запроса load</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $("#example").load("27.2.html #menu"); }); }); --> </script> <style type="text/css"> span { color:#00f; } </style>


384

Часть V. jQuery

</head> <body> <div id="example"></div> <button>Start</button> </body> </html>

При выполнении запроса load(), пример которого приведен в листинге 27.4, в элемент с идентификатором #example будет загружен только ненумерованный список ul. Попробуйте изменить код примера таким образом, чтобы загрузить только заголовок h1 или параграф p. Еще один пример приведен в листинге 27.5. Здесь мы попробуем передать в запросе все возможные аргументы. Листинг 27.5. Пример выполнения AJAX-запроса load с аргументами <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title> Пример выполнения AJAX-запроса load с аргументами</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <div id="feeds"></div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#feeds").load( "feeds.php", { limit: 25 }, function(){ alert("Последние 25 записей загружены"); }); }); --> </script> </body> </html>


Глава 27. AJAX-запросы в jQuery

385

В примере из листинга 27.5 мы выполняем запрос к файлу feeds.php и в качестве параметров запроса передаем пару "ключ/значение" limit: 25. Этим мы определяем и тип выполняемого запроса как POST. В итоге сервер получит от нашего клиентского приложения данные в виде $_POST['limit'] = 25. Разумеется, что на основании полученных данных сценарий на сервере должен вернуть 25 последних записей. В результате выполнения запроса (необязательно успешного!) будет вызвана функция, которая сообщит нам, что "Последние 25 записей загружены". Обратите внимание, что мы не приводим листинга файла feeds.php — его просто не существует. Но тем не менее, если вы попробуете выполнить пример из листинга 27.5, то все равно увидите сообщение об успешной загрузке данных. Чтобы иметь возможность отслеживать успешное или ошибочное выполнение AJAX-запросов, надо использовать другие методы, которые предоставляет библиотека jQuery.

Реализация GET-запросов В отличие от метода load(), эти способы выполнения запросов не предназначены для манипулирования набором элементов, но предоставляют более полный контроль над выполнением запроса. Метод load() просто загрузит некоторые данные в указанное место, а все методы, с которыми мы познакомимся в дальнейшем, позволяют на основании данных, полученных в ответе от сервера, решить, что же делать дальше. $.get(url, [data], [callback], [type])

Метод загружает страницу, используя GET-запрос. Метод возвращает объект XMLHttpRequest. Параметры метода: url — строка, содержащая адрес ресурса в Интернете, к которому будет отправлен запрос; data (необязательный аргумент) — данные в виде пар "ключ/значение", которые могут быть переданы серверу; callback (необязательный аргумент) — функция, которая будет вызвана при успешном выполнении запроса (и только если получен код ответа successful);

(необязательный аргумент) — определяет тип данных, которые будут возвращены в callback-функцию. Возможные значения этого аргумента: "xml", "html", "script", "json", "jsonp" или "text". Это самый простой способ передачи GET-запроса серверу. Он допускает возможность использования одной функции, которая может быть вызвана при type


Часть V. jQuery

386

окончании запроса. Но в отличие от метода load(), функция будет вызвана лишь при успешном выполнении запроса.

П РИМЕЧАНИЕ Обратите внимание, что успешным выполнением запроса будет считаться получение кода ответа successful. При необходимости отслеживать как ошибочное, так и успешное выполнение AJAX-запроса следует использовать метод $.ajax(options). Давайте рассмотрим пример использования запроса $.get с передачей всех возможных параметров (листинг 27.6).

Листинг 27.6. Пример выполнения AJAX-запроса $.get <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Пример выполнения AJAX-запроса $.get</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ $.get("27.7.php", { name: "John", time: "2pm" }, function(data){ alert("Загружены данные: " + data); }); }); --> </script> </body> </html>

В примере из листинга 27.6 серверному сценарию 27.7.php отправляется GETзапрос с параметрами name=John и time=2pm. Исходный код файла 27.7.php приведен в листинге 27.7.


Глава 27. AJAX-запросы в jQuery

387

Листинг 27.7. Исходный код файла, обслуживающего выполнение AJAXзапроса $.get <?php header('Content-Type: text/html; charset=utf-8'); if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { if($_GET) { print $_GET['time'] . ' - ' . $_GET['name']; } } ?>

Какие-либо действия будут выполнены только в случае существования объекта XMLHttpRequest. Если также существует глобальный массив $_GET, то с помощью оператора print мы просто выведем переменные $_GET['time'] и $_GET['name']. При успешном выполнении запроса данные, полученные от сервера, будут переданы в функцию alert и мы увидим сообщение: "Загружены данные: 2pm — John". $.getJSON(url, [data], [callback])

Метод загружает данные в формате JSON, используя GET-запрос. Метод возвращает объект XMLHttpRequest. Параметры матода: url — строка, содержащая адрес ресурса в Интернете, к которому будет отправлен запрос. data (необязательный аргумент) — данные в виде пар "ключ/значение", которые могут быть переданы серверу. callback (необязательный аргумент) — функция, которая будет вызвана, когда данные будут успешно загружены. Очень похоже на обычный GET-запрос. Использование оправдано в том случае, если заранее известно, что сервер вернет данные в формате JSON. Тогда ответ сервера будет интерпретирован как данные в формате JSON, и эти данные будут переданы в callback-функцию, где работать с ними будет просто в удовольствие. Посмотрите это на примере (листинг 27.8). Листинг 27.8. Пример выполнения AJAX-запроса $.getJSON <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru">


Часть V. jQuery

388 <head> <title>Пример выполнения AJAX-запроса $.getJSON</title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ $.getJSON("27.9.js", function(json){ alert(json.users[2].name + ": " + json.users[2].phone); }); }); --> </script> </body> </html>

В примере из листинга 27.8 мы используем GET-запрос для загрузки данных в формате JSON из файла 27.9.js и получаем значения свойств name и phone из этих данных. Пример представления данных в формате JSON приведен в листинге 27.9. Листинг 27.9. Пример представления данных в формате JSON { "users":[ { "name":"John", "phone":"555-66-77" }, { "name":"Mary", "phone":"566-77-77" }, { "name":"Helen", "phone":"577-88-77" },


Глава 27. AJAX-запросы в jQuery

389

{ "name":"Bob", "phone":"588-66-99" }] }

После выполнения примера из листинга 27.8 мы увидим следующий результат — "Helen: 577-88-77". Попробуйте изменить пример из листинга 27.8 таким образом, чтобы получить результат — "John: 555-66-77". Начиная с версии jQuery 1.2, имеется возможность загружать данные в формате JSON, расположенные на другом домене, используя JSON with Padding, или просто JSONP (подразумевается, что сервер предоставляет такую возможность). Пример представлен в листинге 27.10. Листинг 27.10. Загрузка данных с использованием JSONP <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Загрузка данных с использованием JSONP</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style> img { height:100px; float: left; margin:4px; border:4px solid #999; } </style> </head> <body> <div id="images"></div> <script type="text/javascript"> <!-$(document).ready(function(){ $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags= cat&tagmode=any&format=json&jsoncallback=?",


Часть V. jQuery

390 function(data){ $.each(data.items, function(i,item){ var img = $("<img/>").attr("src", item.media.m) .attr("title",item.title) .appendTo("#images"); if (i == 3) return false; }); }); }); --> </script> </body> </html>

Сервер flickr.com предоставляет API, используя который можно получать нужные данные в формате JSON. В листинге 27.10 приведен пример загрузки миниатюр четырех последних добавленных фотографий с тегом "cat". Обратите внимание на один из параметров в строке запроса — jsoncallback=?. Таким образом, имя callback-функции будет генерироваться автоматически при каждом обращении к серверу. П РИМЕЧАНИЕ Имя callback-функции можно указать и явно, используя $.ajax(options) и передав его в параметре jsonp.

для этого метод

$.getScript(url, [callback])

Метод загружает и выполняет JavaScript-файлы, используя GET-запрос. Метод возвращает объект XMLHttpRequest. Параметры метода: url — строка, содержащая адрес ресурса в Интернете, к которому будет отправлен запрос; callback (необязательный аргумент) — функция, которая будет вызвана, когда данные будут успешно загружены. Также один из вариантов GET-запроса. Испытаем его на простом примере (листинг 27.11). Листинг 27.11. Пример выполнения AJAX-запроса $.getScript <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru">


Глава 27. AJAX-запросы в jQuery

391

<head> <title>Пример выполнения ajax-запроса $.getScript</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ $.getScript("27.12.js"); }); --> </script> </body> </html>

В листинге 27.11 мы отправляем AJAX-запрос $.getScript к файлу 27.12.js, очень простой исходный код которого приведен в листинге 27.12. В результате выполнения примера из листинга 27.11 мы увидим в окне сообщения строку "Script loaded and executed". Листинг 27.12. Исходный код файла 27.12.js alert('Script loaded and executed');

Начиная с версии jQuery 1.2, можно загружать и выполнять JavaScript-файлы с другого домена. Попробуем проделать это (листинг 27.13). Листинг 27.13. Пример выполнения AJAX-запроса $.getScript <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Пример выполнения AJAX-запроса $.getScript</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style> .block { background-color: blue;


392

Часть V. jQuery

width: 150px; height: 150px; margin: 10px; } button { margin: 10px; } </style> </head> <body> <div class="block"></div> <button id="go">Run</button> <script type="text/javascript"> <!-$(document).ready(function(){ $.getScript("http://dev.jquery.com/view/trunk/plugins/color/jquery.color. js", function(){ $("#go").click(function(){ $(".block").animate({ backgroundColor: 'red' }, 1500) .animate({ backgroundColor: 'blue' }, 1500); }); }); }); --> </script> </body> </html>

Исходный код, приведенный в листинге 27.13, загружает с домена dev.jquery.com последнюю версию официального плагина к jQuery — jQuery Color Animation. Как только данные будут загружены, мы можем использовать функциональность плагина на нашей странице. В данном случае мы изменяем CSS-свойство background-color элементов, имеющих класс .block, с синего на красный и обратно при щелчке мышью на кнопке с идентификатором #go.

Реализация POST-запросов

Запросы типа POST, в отличие от GET-запросов, должны использоваться в случае, если необходимо вызвать какие-либо изменения на сервере (добавление, удаление, редактирование). Довольно логично, если учесть, что браузеры


Глава 27. AJAX-запросы в jQuery

393

принимают решение о кэшировании данных на основании типа запроса. И GET-запросы, в отличие от POST-запросов, выглядят более реальными кандидатами на попадание в кэш. $.post(url, [data], [callback], [type])

Метод загружает страницу, используя POST-запрос. Метод возвращает объект XMLHttpRequest. Параметры метода: url — строка, содержащая адрес ресурса в Интернете, к которому будет отправлен запрос; data (необязательный аргумент) — данные в виде пар "ключ/значение", которые могут быть переданы серверу; callback (необязательный аргумент) — функция, которая будет вызвана при успешном выполнении запроса (и только если получен код ответа successful);

(необязательный аргумент) — определяет тип данных, которые будут возвращены в callback-функцию. Возможные значения этого аргумента: "xml", "html", "script", "json", "jsonp" или "text". Это самый простой способ передачи POST-запроса серверу. Он допускает использование одной функции, которая может быть вызвана при окончании запроса. type

П РИМЕЧАНИЕ Обратите внимание, что успешным выполнением запроса будет считаться получение кода ответа successful. При необходимости отслеживать как ошибочное, так и успешное выполнение AJAX-запроса следует использовать метод $.ajax(options). Давайте рассмотрим пример использования запроса $.post с передачей всех возможных параметров (листинг 27.14).

Листинг 27.14. Пример выполнения AJAX-запроса $.post <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Пример выполнения AJAX-запроса $.post</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script>


Часть V. jQuery

394 </head> <body> <script type="text/javascript"> <!-$(document).ready(function(){ $.post("27.15.php", { name: "John", time: "2pm" }, function(data){ alert("Загружены данные: " + data); }); }); --> </script> </body> </html>

В примере из листинга 27.14 серверному сценарию 27.15.php отправляется POST-запрос с параметрами name=John и time=2pm. Исходный код файла 27.15.php приведен в листинге 27.15. Листинг 27.15. Исходный код файла, обслуживающего выполнение AJAXзапроса $.post <?php header('Content-Type: text/html; charset=utf-8'); if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { if($_POST) { print $_POST['time'] . ' - ' . $_POST['name']; } } ?>

Какие-либо действия будут выполнены только в случае существования объекта XMLHttpRequest. Если также существует глобальный массив $_POST, то с помощью оператора print мы просто выведем переменные $_POST['time'] и $_POST['name']. При успешном выполнении запроса данные, полученные от сервера, будут переданы в функцию alert, и мы увидим сообщение: "Загружены данные: 2pm — John".


Глава 27. AJAX-запросы в jQuery

395

Полный контроль над AJAX-запросами

Методы $.get, $.post и т. п. являются высокоуровневыми абстракциями, более простыми в понимании и использовании, но они не в состоянии предоставить такую функциональность, такие возможности "тонкой настройки" запроса, которую предоставляет метод $.ajax(options). В конечном счете, все методы библиотеки jQuery, связанные с использованием технологии AJAX, используют именно этот метод. $.ajax(options)

Метод загружает страницу, используя HTTP-запрос. Метод принимает один аргумент options — объект, состоящий из пар "ключ/значение", которые использует, чтобы инициализировать и обработать AJAX-запрос. Все опции необязательные. Значения по умолчанию могут быть установлены для любой опции в $.ajaxSetup(). Чтобы лучше понять возможности $.ajax(options), разберем пример (листинг 27.16), в котором постараемся показать использование наиболее часто применяемых опций, а уже затем познакомимся со всеми возможными опциями. Листинг 27.16. Пример выполнения запроса с использованием $.ajax <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Пример выполнения запроса с использованием $.ajax</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> #result { width:400px; height:100px; margin:5px; padding:3px; border:1px solid #00f; background-color:#eee; } p {


Часть V. jQuery

396 font-weight:bold; width:400px; } </style> </head> <body> <div id="result">

Здесь мы сможем наблюдать ход выполнения запроса и результат его выполнения </div> <p>

В POST-запросе серверу будут отправлены данные: name=John и time=2pm </p> <button>Отправить ajax-запрос</button> <script type="text/javascript"> <!-$(document).ready(function(){ $("button").click(function() { $.ajax({ url: "27.17.php", type: "POST", data: "name=Jonh&time=2pm", dataType: "text", timeout: 3000, beforeSend: function (){ $("#result").html("<img src='img-27.16.gif' /> Loading..."); }, success: function (answer){ $("#result").text(answer); }, error: function (XMLHttpRequest, textStatus, errorThrown){ $("#result").text(textStatus); } }); }); }); --> </script> </body> </html>


Глава 27. AJAX-запросы в jQuery

397

Пример из листинга 27.16 выполняет AJAX-запрос к файлу 27.17.php, исходный код которого приведен в листинге 27.17. Листинг 27.17 . Исходный код файла, обслуживающего выполнение запроса $.ajax <?php header('Content-Type: text/html; charset=utf-8'); if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { if($_POST) { print 'Сервер получил данные: name = ' . $_POST['name'] . ', и time = ' . $_POST['time'];

} } ?>

Исходный код файла, обслуживающего выполнение запроса $.ajax, довольно простой и уже не раз встречался нам на протяжении текущей главы. А вот код из листинга 27.16 стоит разобрать подробнее. В HTML-разметке мы предусмотрели элемент button, при щелчке на котором будет выполняться AJAX-запрос. В элементе div с идентификатором #result мы будем отображать ход выполнения AJAX-запроса и результат его выполнения. Но давайте уже познакомимся с теми опциями, с помощью которых мы осуществляем "настройку" запроса. В опции url указываем адрес ресурса, к которому будет отправлен запрос. Указывая в опции type значение POST, определяем таким образом тип запроса. В опции data указаны данные для передачи на сервер. Опция dataType определит тип данных, которые, как ожидается, будут получены в ответе сервера. Передавая в этой опции значение text, мы предполагаем, что данные, полученные от сервера, будут представлять собой обычный текст. Опция timeout установит предельное время ожидания ответа от сервера в миллисекундах. В опции beforeSend мы определяем функцию, которая будет выполнена перед отправкой запроса. С помощью этой функции мы вставим в элемент div с идентификатором #result картинку и текст "Loading...". В опции success укажем функцию, которая будет вызвана в случае, если код статуса в ответе сервера сообщит об успешном выполнении запроса. Эта функция вставит текст ответа сервера в элемент div с идентификатором #result. И наконец, в опции error определим функцию, которую вызовем в случае, если код статуса в ответе сервера сообщит об ошибке. Эта функция вставит в элемент div#result собственно текстовое сообщение об ошибке.


398

Часть V. jQuery

При успешном выполнении запроса в элемент div с идентификатором #result будет выведено сообщение "Сервер получил данные: name = Jonh и time = 2pm". Попробуйте теперь удалить с сервера файл 27.17.php и вновь выполнить запрос — вы получите сообщение об ошибке. Далее приведен полный список всех доступных опций с их кратким описанием. async — по умолчанию все AJAX-запросы передаются в асинхронном режиме. Опция принимает значения true или false. По умолчанию установлено true. Если необходимо выполнить синхронный запрос, ее нужно установить в false. При этом следует помнить, что пока синхронный запрос не завершен, блокируется любая активность браузера. beforeSend — функция, которая может быть вызвана с целью изменения объекта XMLHttpRequest перед его отправкой на сервер. Можно использовать для проверки условий, при которых должна состояться передача объекта на сервер и т. п. В этой функции можно вернуть false, чтобы отменить отправку запроса на сервер. cache — опция добавлена в jQuery, начиная с версий 1.2. Принимает логические значения true или false. Если опция установлена в false, то браузер не будет кэшировать запрашиваемую страницу. По умолчанию установлено значение true, но если для опции dataType установлено значение script, то для опции cache по умолчанию будет использовано значение false. complete — функция, которая может быть вызвана после завершения AJAX-запроса (и после того как будут выполнены функции, определенные в опциях success и error). Функция принимает два аргумента: объект XMLHttpRequest и строку, описывающую тип успешно выполненного запроса. Пример использования опции приведен в листинге 27.18. Листинг 27.18. Использование опции complete function (XMLHttpRequest, textStatus) { this; // Опции этого AJAX-запроса }

— строка, определяющая используемый content-type при передаче данных на сервер. По умолчанию установлен вариант application/x-www-form-urlencoded, как наиболее подходящий в большинстве случаев. contentType


Глава 27. AJAX-запросы в jQuery

399

— объект или строка с данными, предназначенными для передачи на сервер, преобразуются в строку запроса, если уже не являются таковой. Для запросов типа GET добавляются в URL. Объект должен представлять собой пары "ключ/значение". Если значение будет являться массивом, jQuery упорядочит множество значений с одним и тем же ключом. Например: {foo:["bar1", "bar2"]} превратится в '&foo=bar1&foo=bar2'. data

— функция, которая будет использоваться для обработки данных, возвращенных в объекте XMLHttpRequest. Это функция предварительной фильтрации, которая должна возвратить предварительно обработанные данные. Функция принимает два аргумента: данные, возвращенные с сервера в аргументе data, и значение опции dataType в аргументе type. dataFilter

Пример использования опции приведен в листинге 27.19.

Листинг 27.19. Использование опции dataFilter function (data, type) { // Выполняем некоторый код

// и возвращаем предварительно обработанные данные

return data; }

— строка, определяющая тип данных, которые ожидаются в ответе сервера. Если опция не определена, то jQuery передаст в success либо responseXML, либо responseText. Выбор будет произведен на основании MIME-типа ответа. Далее приведены доступные значения: dataType

• xml — возвращается XML-документ, который может быть обработан через jQuery; • html — возвращается HTML как простой текст. Включенные теги script расцениваются как вставленные в объектную модель документа (DOM); • script — ответ оценивается как JavaScript и возвращается как простой текст; • json — ответ оценивается как данные в формате JSON и возвращается как объект JavaScript; • jsonp — ответ оценивается как данные в формате JSON, загружаемые с удаленного домена с использованием JSONP; • text — строка, представляющая собой простой текст.


400

Часть V. jQuery

— функция, которая может быть вызвана, если запрос потерпел неудачу. Функция принимает три аргумента: объект XMLHttpRequest, строку, описывающую тип ошибки, которая произошла, и дополнительный объект исключения, если он имеется. Пример использования опции приведен в листинге 27.20. error

Листинг 27.20. Использование опции error function (XMLHttpRequest, textStatus, errorThrown) { // Чтобы получить необходимую информацию, обычно используется

// либо только textStatus, либо только errorThrown

this; // Опции этого AJAX-запроса }

— опция определяет, вызывать или нет глобальные обработчики событий для этого AJAX-запроса. По умолчанию установлено true. Необходимо установить опцию в false, чтобы предотвратить вызов глобальных обработчиков (например ajaxStart или ajaxStop) из этого запроса. Эту опцию можно использовать для управления различными AJAXсобытиями. ifModified — опция принимает значения true или false. По умолчанию установлено false. Если установить значение true, то это позволит запросу быть успешным только в том случае, если ответ сервера изменился со времени последнего запроса к нему. Реализовано проверкой заголовка global

Last-Modified.

— строка, которая переопределяет имя callback-функции при выполнении запроса с использованием jsonp. Это значение будет использоваться вместо callback в той части строки запроса, где передается callback=? (в GET-запросе) или в данных (в POST-запросе). То есть использование варианта {jsonp: onJsonPLoad} приведет к передаче на сервер данных onJsonPLoad=?. password — строка, где можно указать пароль, который будет использоваться в ответ на запрос HTTP-аутентификации. processData — опция может принимать значения true или false. По умолчанию установлено значение true. Технически, данные передаются как объект, который будет обработан и преобразован в строку запроса, соответствующую content-type application/x-www-form-urlencoded. Если же необходимо передать DOM-документ или другие данные, не подлежащие обработке, необходимо установить значение этой опции в false. jsonp


Глава 27. AJAX-запросы в jQuery

401

— строка, определяющая кодировку данных. Может применяться только с запросами типа GET и только, если опция DataType имеет значения jsonp или script. scriptCharset

— функция, которая будет вызвана, когда запрос будет успешно завершен. Функция принимает два аргумента: данные, возвращенные с сервера и отформатированные согласно значению, установленному в опции dataType, и строка, описывающая статус ответа сервера. success

Пример использования опции приведен в листинге 27.21.

Листинг 27.21. Использование опции success function (data, textStatus) { // Данные могут быть документом XML, объектом JSON, HTML, простым // текстом и т. д.

this; // Опции для этого AJAX-запроса

}

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

— строка, определяющая тип запроса "POST" или "GET". Необходимо отметить, что другие типы запросов, такие как "PUT" и "DELETE", также могут быть использованы здесь, но они не поддерживаются всеми браузерами. type

— строка, содержащая адрес ресурса в Интернете, к которому будет отправлен запрос. Значение по умолчанию — текущая страница. url

— строка, где можно указать имя пользователя, которое будет использоваться в ответ на запрос HTTP-аутентификации. username

$.ajaxSetup(options)

В рассмотренном ранее методе $.ajax(options) мы определяли "тонкие настройки" для конкретного AJAX-запроса. Библиотека jQuery предоставляет возможность определить настройки по умолчанию для всех AJAX-запросов, используемых на странице с помощью метода $.ajaxSetup(options). Метод принимает один аргумент — объект, состоящий из пар "ключ/значение", которые используются, чтобы инициализировать и обрабатывать AJAXзапросы. Список доступных опций точно такой же, как и в методе $.ajax(options). В настройках же какого-то конкретного AJAX-запроса любую из опций по умолчанию можно переопределить.


Глава 28

События AJAX в jQuery AJAX-запросы

вызывают различные события, которые можно отслеживать

и связывать с конкретными элементами

DOM.

Далее приведен полный спи-

сок событий и порядок, в котором они передаются. Имеются два типа событий

— локальные и глобальные.

ajaxStart — глобальное событие. Это событие распространяется, если AJAX-запрос начат и нет других AJAX-запросов, выполняющихся в это же время. Вызывается до создания объекта XMLHttpRequest. beforeSend — локальное событие. Это событие, которое вызывается до того, как начнет выполняться AJAX-запрос, позволяет изменить объект XMLHttpRequest (например, при необходимости можно установить дополнительные заголовки). ajaxSend — глобальное событие. Это глобальное событие также вызывается до начала выполнения AJAX-запроса, но после создания объекта XMLHttpRequest. success — локальное событие. Это событие вызывается только, если AJAX-запрос был успешно выполнен (нет ошибок с сервера, нет ошибок в данных). ajaxSuccess — глобальное событие. Вызывается только в случае успешного выполнения AJAX-запроса, т. е. в случае получения ответа от сервера, содержащего код статуса, свидетельствующего об успешном выполнении. error — локальное событие. Это событие вызывается только в случае возникновения ошибок AJAX-запроса (никогда не могут возникнуть оба события вместе — успешное и ошибочное выполнение AJAX-запроса). ajaxError — глобальное событие. Вызывается только в случае получения ответа от сервера, содержащего код статуса, свидетельствующего об ошибке выполнения запроса.


Глава 28. События AJAX в jQuery

403

— локальное событие. Это событие вызывается независимо от того, был ли AJAX-запрос успешно выполнен или нет. Вы будете всегда получать это событие, даже для AJAX-запросов, которые выполнялись синхронно. ajaxComplete — глобальное событие. Это событие ведет себя точно так же, как локальное событие complete, и будет вызываться каждый раз, когда AJAX-запрос завершается. ajaxStop — глобальное событие. Вызывается, если больше нет выполняющихся AJAX-запросов. Локальные события можно отслеживать внутри конкретного AJAX-запроса, например так, как показано в листинге 28.1. complete

Листинг 28.1. Пример отслеживания локальных событий $.AJAX({ beforeSend: function(){ // Обработка события beforeSend }, complete: function(){ // Обработка события complete } // ... });

Глобальные события передаются всем элементам в объектной модели документа (DOM), вызываются любые обработчики, которые могут прослушивать эти события. Прослушивать эти события можно, например так, как показано в листинге 28.2.

Листинг 28.2. Пример отслеживания глобальных событий $("#loading").bind("ajaxSend", function(){ $(this).show(); }).bind("ajaxComplete", function(){ $(this).hide(); });

В листинге 28.3 приведен пример, который очень наглядно демонстрирует возникновение и порядок следования локальных и глобальных событий как для успешных, так и ошибочных AJAX-запросов.


404

Часть V. jQuery

Листинг 28.3. Глобальные и локальные AJAX-события <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Глобальные и локальные AJAX-события</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> #result { width:300px; height:150px; margin:5px; padding:3px; border:1px solid #00f; background-color:#eee; float:left; overflow:auto; } p { margin:0 0 2px 0; font-size:.8em; } .global { color:#00f; } .local { color:#f0f; } button { display:block; width:150px; margin:5px; } </style> </head> <body> <div id="result"></div> <div>


Глава 28. События AJAX в jQuery <button id="well">Успешный AJAX-запрос</button> <button id="bad">Ошибочный AJAX-запрос</button> </div> <script type="text/javascript"> <!-$(document).ready(function(){ $("#result") .ajaxStart(function() { $(this).append('<p class="global">' + new Date() + ' : ajaxStart</p>'); }) .ajaxSend(function() { $(this).append('<p class="global">' + new Date() + ' : ajaxSend</p>'); }) .ajaxSuccess(function() { $(this).append('<p class="global">' + new Date() + ' : ajaxSuccess</p>'); }) .ajaxError(function() { $(this).append('<p class="global">' + new Date() + ' : ajaxError</p>'); }) .ajaxComplete(function() { $(this).append('<p class="global">' + new Date() + ' : ajaxComplete</p>'); }) .ajaxStop(function() { $(this).append('<p class="global">' + new Date() + ' : ajaxStop</p>'); }); $("#well").click(function() { $("#result").empty(); $.AJAX({ url: "27.17.php", timeout: 3000, beforeSend: function() { $("#result").append('<p class="local">' + new Date() + ' : beforeSend</p>'); },

405


406 success: function() { $("#result").append('<p ' : }, complete: function() { $("#result").append('<p ' : } }); }); $("#bad").click(function() { $("#result").empty(); $.AJAX({ url: "nonexistent.php", timeout: 3000, beforeSend: function() { $("#result").append('<p ' : }, error: function() { $("#result").append('<p ' : }, complete: function() { $("#result").append('<p ' : } }); }); }); --> </script> </body> </html>

Часть V. jQuery class="local">' + new Date() + success</p>');

class="local">' + new Date() + complete</p>');

class="local">' + new Date() + beforeSend</p>');

class="local">' + new Date() + error</p>');

class="local">' + new Date() + complete</p>');

Несмотря на то, что по размерам листинг получился весьма внушительным, код довольно прост. В HTML-разметке определен элемент div c идентификатором #result — сюда мы будем выводить результаты, и два элемента button, с помощью которых будут отправляться AJAX-запросы. Один — успешный, а второй — ошибочный.


Глава 28. События AJAX в jQuery

407

Мы свяжем все глобальные события с элементом #result и при их возникновении будем добавлять в него дату и время события и его название. Функции — обработчики локальных событий мы определим в соответствующих опциях $.ajax(options). Эти функции тоже будут добавлять в элемент #result дату и время возникновения события и его название. Глобальные и локальные события будем записывать разным цветом шрифта. Таким образом, мы сможем наглядно проследить порядок следования событий. А теперь немного подробнее рассмотрим аргументы callback-функций, которые вызываются при наступлении того или иного глобального события. ajaxStart(callback)

В качестве единственного аргумента передается функция, которая будет вызываться каждый раз, когда начинается AJAX-запрос и на этот момент не существует никаких других выполняющихся AJAX-запросов. Callback-функция не принимает никаких аргументов. Пример представлен в листинге 28.4. Листинг 28.4. Аргументы callback-функции события ajaxStart function () { this; // Прослушиваемые элементы DOM } ajaxSend(callback)

В качестве единственного аргумента передается функция, которая будет вызвана до того, как AJAX-запрос будет отправлен. Объект события, объект XMLHttpRequest и установки, используемые для этого запроса, передаются как аргументы в callback-функцию, как и показано в листинге 28.5. Листинг 28.5. Аргументы callback-функции события ajaxSend function (event, XMLHttpRequest, ajaxOptions) { this; // Прослушиваемые элементы DOM } ajaxSuccess(callback)

В качестве единственного аргумента передается функция, которая будет вызываться всякий раз, когда AJAX-запрос завершен успешно. Объект события, объект XMLHttpRequest и установки, используемые для этого запроса, передаются как аргументы в callback-функцию, как и показано в листинге 28.6.


408

Часть V. jQuery

Листинг 28.6. Аргументы callback-функции события ajaxSuccess function (event, XMLHttpRequest, ajaxOptions) { this; // Прослушиваемые элементы DOM } ajaxError(callback)

В качестве единственного аргумента передается функция, которая будет вызываться всякий раз, когда AJAX-запрос будет неудачен. Объект события, объект XMLHttpRequest и установки, используемые для этого запроса, передаются как аргументы в callback-функцию, как и показано в листинге 28.7. Четвертый аргумент, передаваемый в callback-функцию, объект исключения, передается только в том случае, если исключение возникло во время выполнения запроса. Листинг 28.7. Аргументы callback-функции события ajaxError function (event, XMLHttpRequest, ajaxOptions, thrownError) { // thrownError передается, если ошибка была перехвачена this; // Прослушиваемые элементы DOM } ajaxComplete(callback)

В качестве единственного аргумента передается функция, которая будет вызываться всякий раз, когда AJAX-запрос будет завершен. Объект события, объект XMLHttpRequest и установки, используемые для этого запроса, передаются как аргументы в callback-функцию, как и показано в листинге 28.8. Листинг 28.8. Аргументы callback-функции события ajaxComplete function (event, XMLHttpRequest, ajaxOptions) { this; // Прослушиваемые элементы DOM } ajaxStop(callback)

В качестве единственного аргумента передается функция, которая будет вызываться всякий раз, когда все AJAX-запросы завершены. Callback-функция не принимает никаких аргументов. Пример — в листинге 28.9.


Глава 28. События AJAX в jQuery

409

Листинг 28.9. Аргументы callback-функции события ajaxStop function () { this; // Прослушиваемые элементы DOM }

П РИМЕЧАНИЕ

Надо хорошо понимать разницу между очень похожими парами событий. Первая пара — это ajaxStart и ajaxSend. Событие ajaxStart происходит, когда начинается AJAX-запрос, но на этот момент других AJAX-запросов, находящихся в процессе выполнения, не существует. А событие ajaxSend происходит перед выполнением каждого AJAX-запроса. Похожая ситуация и со второй парой. Это — ajaxComplete и ajaxStop. Событие ajaxComplete происходит всякий раз, когда завершается какой-либо AJAX-запрос. А событие ajaxStop — когда завершены все AJAX-запросы.


Глава 29

Расширения для jQuery Библиотека jQuery предлагает большое количество разнообразных методов, с помощью которых можно реализовать практически любую функциональность, но тем не менее она обладает и возможностью подключения дополнительных модулей, которые могут потребоваться разработчикам. На момент написания книги таких расширений насчитывалось уже более тысячи. Мы познакомимся с двумя из наиболее полезных и популярных расширений.

Плагин jQuery Form Вероятно, наиболее часто AJAX-запросы используются для отправки данных из формы на сервер. Ядро библиотеки jQuery располагает несколькими методами, которые существенно облегчают работу с формами, но этих методов не так много, а поэтому мы предлагаем познакомиться с одним из наиболее популярных официальных расширений для библиотеки jQuery — jQuery Form Plugin.

Плагин расширяет функциональность библиотеки в работе с получением значений из элементов формы, с очисткой и установкой значений по умолчанию в элементах формы и собственно с отправкой формы с использованием механизмов AJAX. Плагин предлагает большое количество разнообразных методов, но мы разберем лишь наиболее интересующий нас случай — отправка формы с помощью AJAX (листинг 29.1). Листинг 29.1. Использование расширения jQuery Form Plugin <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head>


Глава 29. Расширения для jQuery <title>Использование расширения jQuery Form Plugin</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <script type="text/javascript" src="js/jquery.form.js"></script> <style type="text/css"> * { margin:0; padding:0; } html, body { background-color:#FFF; font-family: "Trebuchet MS", Arial, sans-serif; font-size: 10pt; } form { margin-left:25px; } input, textarea, select { display:block; width:250px; float:left; margin-left:10px; margin-bottom:10px; font-family: "Trebuchet MS", Arial, sans-serif; font-size: 10pt; } optgroup, option { font-family: "Trebuchet MS", Arial, sans-serif; font-size: 10pt; } label { display:block; text-align:right; float:left; width:105px; padding-right:5px; } br { clear:left;

411


Часть V. jQuery

412 } .cb, .rb { width:1em; } .ms, .ta { height:100px; } #reset, #submit1 { width:87px; margin-left:20px; margin-top:10px; } #submit2 { width:87px; height:40px; margin-left:20px; } #output { background-color:#F0B80D; padding:5px; } </style> </head> <body>

<div id="output">AJAX-ответ от сервера заменит этот текст.</div> <form id="myForm" action="29.2.php" method="post"> <input type="hidden" name="Hidden" value="hidden Value" /><br /> <label for="Name">Имя:</label>

<input name="Name" type="text" value="Мое имя" /><br />

<label for="Password">Пароль:</label>

<input name="Password" type="password" /><br /> <label for="File">Файл:</label> <input name="File" type="file" /><br /> <label for="Multiple">Мультиселект:</label> <select class="ms" name="Multiple" multiple="multiple"> <optgroup label="Группа 1">

<option value="one" selected="selected">Первый элемент</option> <option value="two">Второй элемент</option>

<option value="three">Третий элемент</option>


Глава 29. Расширения для jQuery

413

</optgroup> <optgroup label="Группа 2">

<option value="four">Четвертый элемент</option>

<option value="five">Пятый элемент</option> <option value="six">Шестой элемент</option>

<option value="seven">Седьмой элемент</option> </optgroup> </select><br /> <label for="Single">Элемент select:</label> <select name="Single"> <option value="one" selected="selected">Первый элемент</option>

<option value="two">Второй элемент</option>

<option value="three">Третий элемент</option> </select><br /> <label for="Single2">Элемент select 2:</label> <select name="Single2"> <optgroup label="Группа 1">

<option value="A" selected="selected">Буква A</option> <option value="B">Буква B</option> <option value="C">Буква C</option>

</optgroup> <optgroup label="Группа 2">

<option value="D">Буква D</option> <option value="E">Буква E</option> <option value="F">Буква F</option> <option value="G">Буква G</option>

</optgroup> </select><br /> <label for="Check">Флажки:</label> <input class="cb" type="checkbox" name="Check" value="1" /> <input class="cb" type="checkbox" name="Check" value="2" /> <input class="cb" type="checkbox" name="Check" value="3" /><br /> <label for="Radio">Переключатели:</label> <input class="rb" type="radio" name="Radio" value="1" /> <input class="rb" type="radio" name="Radio" value="2" /> <input class="rb" type="radio" name="Radio" value="3" /><br /> <label for="Text">Просто текст:</label>

<textarea class="ta" name="Text" rows="2" cols="20">Это элемент textarea</textarea><br />


Часть V. jQuery

414

<input id="reset" type="reset" name="resetButton" value="Reset" /> <input id="submit1" type="submit" name="submitButton" value="Submit1" /> <input id="submit2" type="image" name="submitButton" value="Submit2" src="img-29.1.gif" /> </form> <script type="text/javascript"> $(document).ready(function(){ // Готовим объект с настройками var options = { // Элемент, который будет обновлен по ответу сервера target: "#output", beforeSubmit: showRequest, // функция, вызываемая перед передачей success: showResponse, timeout: 3000

// функция, вызываемая при получении ответа

// тайм-аут

}; // Связываем событие submit с нужной формой $('#myForm').submit(function() { // Вызываем метод и передаем ему настройки $(this).ajaxSubmit(options); // !!! Важно !!!

// Всегда возвращаем false, чтобы предупредить стандартные

// действия браузера (переход на страницу 29.2.php) return false; }); // Вызов функции перед передачей данных function showRequest(formData, jqForm, options) { // formData - массив, содержащий данные формы;

// jqForm - объект jQuery, содержащий элементы формы;

// options - объект с используемыми настройками. alert('Вызвана функция showRequest');

// Здесь можно вернуть false, чтобы запретить отправку формы. // Например, если данные не прошли предварительную проверку,

// любое отличное от false значение разрешит отправку формы. return true; } // Вызов функции при получении ответа сервера function showResponse(responseText, statusText) alert('Статус ответа сервера: ' + statusText);

{


Глава 29. Расширения для jQuery

415

} }); </script> </body> </html>

Мы не будем подробно разбирать CSS- и HTML-разметку примера, приведенного в листинге 29.1, а сосредоточимся на изучении применения самого плагина jQuery Form. Для того чтобы иметь возможность использовать его функциональность, плагин необходимо подключить к странице, точно так же как мы подключали саму библиотеку jQuery. Скачать последнюю версию плагина можно на сайте разработчика http://malsup.com/jquery/form/#download. После этого необходимо подготовить объект с настройками запроса — очень похоже на объект options метода $.ajax(options). Опции timeout, success и beforeSend имеют то же самое значение, что и при использовании метода $.ajax. Незнакома нам только опция target — в ней можно указать элемент DOM, содержимое которого необходимо заменить ответом, полученным от сервера. В опциях beforeSend и success определим очень простые функции, которые будут служить исключительно в демонстрационных целях, с тем, чтобы показать саму возможность вызова обработчика событий как перед отправкой запроса, так и при получении ответа от сервера. П РИМЕЧАНИЕ

В опциях можно указать также и URL ресурса, к которому будет отправлен запрос, и метод передачи данных, но на практике лучше определить это в HTMLразметке самой формы, чтобы она оставалась в рабочем состоянии даже при отключенной поддержке JavaScript в браузере пользователя.

Следующее, что необходимо сделать, — связать нужную форму с событием submit, выбрав ее с помощью jQuery-селектора. В функции-обработчике этого события мы будем вызывать метод ajaxSubmit выбранной формы и передавать ему объект options, содержащий необходимые настройки. Обратите внимание, что здесь мы всегда возвращаем false, чтобы предупредить стандартные действия браузера, т. е. в нашем случае, переход на страницу 29.2.php. Этот файл обслуживает запрос от нашей формы и его код приведен в листинге 29.2. Листинг 29.2. Исходный код файла, обслуживающего выполнение запроса <?php header('Content-Type: text/html; charset=utf-8'); if($_POST) {


Часть V. jQuery

416 print_r($_POST); } if($_FILES) { print_r($_FILES); } ?>

Все, что делает код, приведенный в листинге 29.2, — выводит содержимое глобальных массивов $_POST и $_FILES, если, конечно, они есть. Именно эти данные мы увидим в элементе с идентификатором #target, после выполнения запроса. Обратите внимание, что в этом коде мы не проводим проверку существования объекта XMLHttpRequest, поскольку предполагаем наличие файла, загружаемого на сервер, а объект XMLHttpRequest не поддерживает такую возможность. С реализацией загрузки файла на сервер нам помогает плагин jQuery Form, который выполняет запрос, используя скрытый фрейм и устанавливая тип содержимого запроса в значение multipart/form-data.

Плагин Live Query

Создавая с помощью jQuery все более сложные приложения, мы рано или поздно сталкиваемся с необходимостью вставлять в объектную модель документа довольно сложные конструкции, которые также нужно связать с какимилибо событиями и их обработчиками. Чтобы лучше понять суть проблемы, рассмотрим очень простой пример, приведенный в листинге 29.3. Листинг 29.3. Обработка событий для загружаемых элементов <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Обработка событий для загружаемых элементов.</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <style type="text/css"> p { background-color:#F0B80D; padding:0 5px; font-weight:bold;


Глава 29. Расширения для jQuery

417

cursor:pointer; } </style> </head> <body> <button>Отправить AJAX-запрос</button> <p>Параграф 1</p>

<p>Параграф 2</p> <script type="text/javascript"> <!-$(document).ready(function(){ // Обрабатываем щелчок на параграфах $("p").click(function() { alert($(this).text()); }); // AJAX-запрос $("button").click(function() { $.ajax({ url: "29.4.php", success: function(answ){ $("body").append(answ); } }); }); }); --> </script> </body> </html>

Давайте разберемся, что делает код, приведенный в листинге 29.3. HTMLразметка предельно проста: два элемента p с текстом и элемент button. Рассмотрим JavaScript-код. Элементы p связаны с событием click, функцияобработчик этого события выведет в alert текст параграфа, по которому был совершен щелчок. Щелчок по элементу button вызовет отправку AJAXзапроса к URL 29.4.php, и при получении ответа его содержимое будет вставлено в элемент body после существующего содержимого. Обратимся к листингу 29.4, в котором приведен код серверного сценария, и посмотрим, что же мы получаем в качестве ответа сервера.


Часть V. jQuery

418

Листинг 29.4. Исходный код файла, обслуживающего выполнение запроса <?php header('Content-Type: text/html; charset=utf-8'); if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { print '<p>Добавленный параграф</p>'; } ?>

Код из листинга 29.4 всего лишь возвращает простую HTML-разметку — элемент p с текстом. Именно этот элемент будет вставлен в DOM, если вы выполните пример из листинга 29.3. Испытайте этот пример и убедитесь, что при щелчке на параграфах, которые существовали на странице с самого начала, все происходит как и предполагалось — мы видим сообщение с текстом самого параграфа. А вот при щелчке на добавленных параграфах ничего не происходит. Почему? Просто потому, что они были добавлены в DOM уже после того, как был выбран набор элементов p, с которым и было связано событие click. А добавленные параграфы в этот набор попасть не могли, потому что на тот момент еще не существовали. Если вы уже осознали проблему, давайте найдем и пути ее решения. Можно, конечно, загружать обработчики событий вместе с добавляемыми элементами, но это решение для простых приложений, и, кроме того, оно вызовет другие проблемы, которые тоже придется как-то решать. Поэтому не стоит изобретать велосипед, раз существует великолепное расширение для библиотеки jQuery, название которого и вынесено в заголовок — Live Query. Расширение Live Query помогает подключать обработчики событий к элементам, которые появляются в объектной модели документа, и отключать обработчики событий от элементов, которые из нее пропадают. Иными словами, Live Query предоставляет методы, которые дают нам полный контроль над управлением событиями и их обработкой в изменяющейся объектной модели документа. Давайте попробуем немного изменить листинг 29.3 и воспользоваться функциональностью Live Query для решения проблемы, приведенной в указанном листинге. Первое, что мы сделаем, — подключим плагин к нашей странице, как показано в листинге 29.5. Скачать последнюю версию плагина можно на официальном сайте jQuery, на странице плагина http://plugins.jquery.com/project/livequery.


Глава 29. Расширения для jQuery

Листинг 29.5. Использование расширения Live Query <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="ru" xml:lang="ru"> <head> <title>Использование расширения Live Query</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="js/jquery-1.2.6.js"></script> <script type="text/javascript" src="js/jquery.livequery.js"></script> <style type="text/css"> p { background-color:#F0B80D; padding:0 5px; font-weight:bold; cursor:pointer; } </style> </head> <body> <button id="ajax">Отправить AJAX-запрос</button> <button id="delete">Удалить обработчики</button> <p>Параграф 1</p> <p>Параграф 2</p> <script type="text/javascript"> <!-$(document).ready(function(){ // Обрабатываем щелчок на параграфах с помощью Live Query $("p").livequery("click", function(event) { alert($(this).text()); }); // AJAX-запрос $("#ajax").click(function() { $.ajax({ url: "29.4.php", success: function(answ){ $("body").append(answ); } }); });

419


Часть V. jQuery

420 // Удаляем обработчики события $("#delete").click(function() { $("p").expire(); }); }); --> </script> </body> </html>

По сравнению с листингом 29.3, исходный код в листинге 29.5 изменился не слишком сильно. Мы подключили файл jquery.livequery.js, добавили в HTML-разметку в демонстрационных целях еще один элемент button. Самые интересные изменения в JavaScript-коде. Теперь мы обрабатываем щелчок по элементам p, используя метод livequery плагина Live Query, передавая ему в качестве первого параметра название события, которое необходимо отслеживать, и во втором параметре определяем функцию — обработчик этого события. По щелчку на кнопке с идентификатором #ajax будем отправлять AJAX-запрос к файлу 29.4.php (исходный код этого файла приведен в листинге 29.4). Щелчок по кнопке с идентификатором #delete приведет к удалению всех обработчиков событий. Попробуйте испытать пример из листинга 29.5 и убедитесь, что обработка события click теперь выполняется не только для тех элементов p, которые существовали в разметке с самого начала, но и для тех, которые были загружены в DOM в результате выполнения AJAX-запроса.

Резюме

Мы познакомились с некоторыми возможностями библиотеки jQuery, в частности довольно подробно узнали о функциях ядра и селекторах, научились работать с событиями и манипулировать элементами DOM с помощью методов, предоставляемых библиотекой. Самое пристальное внимание было уделено взаимодействию jQuery с механизмами AJAX. И наконец мы узнали, как можно использовать дополнительные модули, расширяющие функциональность библиотеки, познакомившись с наиболее популярными и востребованными в практическом программировании плагинами — jQuery Form и Live Query. Хочется отметить, что функциональность библиотеки jQuery не ограничивается возможностями, рассмотренными в рамках этой книги, но полученные сведения помогут легко продолжить самостоятельное изучение других, не менее полезных возможностей.


Приложения


Приложение 1

Установка Web-сервера Apache, модуля PHP 5 и сервера MySQL в Windows Следующее описание установки справедливо для работы в операционной системе Windows XP Professional или Windows Server 2003. При установке программ вы должны работать от имени пользователя, имеющего достаточные привилегии, чтобы писать файлы в системный каталог Windows и каталог Program Files системного диска.

Установка сервера Apache Для установки Web-сервера Apache необходимо скачать дистрибутив с сайта apache.org. Нам потребуется бинарный пакет (Win32 Binary) в файле с расширением msi (MS Installer). Приведенные далее инструкции касаются установки сервера Apache 2.2. После запуска на исполнение пакета с дистрибутивом вам потребуется ответить на ряд вопросов (рис. П1.1). Если вы работаете дома, то, возможно, у вас нет ни сети, ни, следовательно, домена, в который может входить ваш компьютер. Тогда в первом поле следует указать значение localdomain. Если же ваш компьютер работает в сети, то узнайте у администратора сети название домена и укажите его здесь. Впрочем, если вы работаете в домене, то инсталлятор может и сам вписать имя домена в поле ввода. Имя сервера — второе, что в окне на рис. П1.1 вам надо указать. Таким именем может быть либо полное доменное имя компьютера, либо его IP-адрес, либо специальное имя localhost. В третьем поле следует указать электронный адрес администратора сервера — в зависимости от ситуации можете ввести подлинный или вымышленный (последний вполне годится для домашнего сервера, который кроме вас никто не увидит).


Приложения

424

Рис. П1.1. Запрос информации о сервере

Рис. П1.2. Запрос имени каталога

Наконец, надо разрешить запускать Apache автоматически при запуске системы как службу Windows и открыть 80-й порт для работы с сервером. Затем придется ответить на вопрос о том, в какой каталог следует установить Apache (рис. П1.2). Инсталлятор предложит каталог Program Files на систем-


Приложение 1. Установка Web-сервера Apache, модуля PHP 5…

425

ном диске. Это не очень удобно, но всякие изменения в этом пункте приводят к неоправданным для новичка сложностям. После прохождения всех окон вы увидите, как установится пакет, а затем и запустится. Об успешности запуска надо судить по черному DOS-окну, в котором могут появиться ошибки. Не появились — отлично, черное окно закроется, что означает успешный запуск сервера. Откройте окно браузера и отправьте HTTP-запрос вашему серверу, указав адрес: http://localhost. Вы должны увидеть титульную страницу с "апачевским" перышком.

Директивы конфигурации Apache Каталог conf установленного нами сервера содержит конфигурационный файл httpd.conf. Apache читает этот файл при запуске. Строки конфигурационного файла, начинающиеся символами #, — это комментарии, которые Apache не читает. Файл httpd.conf содержит директивы, определяющие режим работы Apache. Настройка Apache в основном состоит в том, чтобы дать директивам файла httpd.conf нужные значения. Например: DirectoryIndex index.html index.php

Эта директива позволяет задать название документа, возвращаемого по запросу, который не содержит в строке URL названия документа. Например, в адресе http://www.shop.com/ отсутствует название документа, поэтому будет возвращен документ, указанный в директиве DirectoryIndex. Поскольку в директиве указано имя файла index.html, сервер передаст клиенту документ index.html из каталога DocumentRoot на сервере. В директиве DirectoryIndex можно задать несколько имен файлов. Если первый документ, указанный в строке, не найден в каталоге, то сервер ищет следующий и, в случае успеха, передает его клиенту. Эта настройка также предоставляет важный уровень защиты информации. По умолчанию, если клиент указывает адрес URL каталога, то сервер Apache передает в ответ список файлов, имеющихся в этом каталоге. Создав в каталоге индексный файл, вы лишите пользователей возможности получить список всех файлов в этом каталоге. Для разработчика наличие индексного файла неудобно, поэтому удалите из каталога htdocs все индексные файлы. Обратившись после этого к своему серверу, вы больше не увидите страницы с "апачевским" перышком, только информацию об Apache и пустой каталог.


426

Приложения

Надо также найти директиву, отвечающую за выбор кодировки документа по умолчанию. Эта директива позволит генерировать в ответе сервера указание браузеру переключиться в указанную кодировку. В Apache такой директивой является AddDefaultCharset. Установите ее значение: AddDefaultCharset utf-8. Подробное описание Apache на английском языке поставляется вместе с сервером и доступно по адресу http://localhost/manual, и это лучшее, что можно прочитать об Apache.

Установка модуля PHP Дистрибутивы PHP 5 следует копировать с сайта www.php.net. Для установки PHP 5 под Windows потребуется инсталлятор с расширением msi, если же после установки модуля выяснится, что забыли установить какие-либо библиотеки, которые в PHP обычно называют расширениями (extensions), то подключить их можно, достав из архива с расширением zip. Далее описывается установка модуля версии 5.2.4. От цифр, которые идут после первой пятерки, зависит конкретный порядок установки, который несколько различен, скажем, для версий PHP 5.0.5 и 5.2.4. На компакт-диске, приложенном к этой книге, находится модуль PHP именно версии 5.2.4. Коллекция модулей PECL, предлагаемая на сервере php.net, не является необходимой для начала работы с PHP. Начинать установку следует с запуска инсталлятора с расширением msi. Если системный диск вашего компьютера — C, то инсталлятор предложит установить модуль в каталог C:\Program Files\PHP\ — соглашайтесь. Затем вас спросят, с каким Web-сервером вы собираетесь использовать PHP. Есть два способа подключения PHP: как модуля Web-сервера и как CGIприложения. В качестве модуля PHP становится частью Web-сервера и при запуске последнего тоже запускается и постоянно готов к работе. При работе в качестве CGI-приложения выполнение PHP как отдельной программы происходит при каждом запросе Web-страницы. PHP 5 работает быстрее, если его подключить как модуль. Нужно только учесть, что при таком способе подключения все изменения, вносимые в файл php.ini, вступят в силу только после перезапуска Apache. Выберите вариант, предусматривающий работу PHP в качестве модуля сервера Apache 2.2. Затем инсталлятор захочет узнать, в каком каталоге находится конфигурационный файл сервера Apache. Дело в том, что модуль PHP постарается внести


Приложение 1. Установка Web-сервера Apache, модуля PHP 5…

427

в этот файл информацию о своем местоположении и необходимости загружать модуль PHP при запуске сервера Apache. В следующем диалоговом окне потребуется выбрать библиотеки, которые вы собираетесь использовать в дальнейшей работе (рис. П1.3). Выбирайте перечисленные ниже, они будут использованы в этой книге. Необходимый для дальнейшей работы набор расширений включает в себя:

gd2.dll — графическая библиотека; mbstring.dll — расширение для работы с многобайтными кодировками (multi-byte string); mysqli.dll — расширение для работы с сервером MySQL 5.

Рис. П1.3. Выбор расширений PHP для установки Не устанавливайте лишних библиотек: они занимают память, а некоторые могут привести к нежелательным проблемам при работе модуля PHP. В результате установки модуля PHP в конфигурационный файл сервера Apache будут внесены следующие строки (ищите в самом конце файла): AddType application/x-httpd-php .php #BEGIN PHP INSTALLER EDITS - REMOVE ONLY ON UNINSTALL PHPIniDir "C:/Program Files/PHP/" LoadModule php5_module "C:/Program Files/PHP/php5apache2_2.dll" #END PHP INSTALLER EDITS - REMOVE ONLY ON UNINSTALL


428

Приложения

Строки, начинающиеся с символа решетки #, являются комментариями, а вот остальные обязательно должны присутствовать в файле httpd.conf. Директива AddType связывает расширение файла запрашиваемого клиентом ресурса и MIME-тип, известный серверу Apache. Директива PHPIniDir указывает местонахождение конфигурационного файла php.ini. Директива LoadModule заставляет Apache подгружать указанный модуль. После установки модуля PHP обязательно проверьте наличие этих строк и при необходимости допишите отсутствующие директивы. Указание это необходимо, т. к. инсталляционные программы разных версий различным образом добавляют директивы в httpd.conf. Конфигурационный файл этого модуля — php.ini. В этом файле, который модуль PHP читает при запуске, большинство строк начинаются с символа точки с запятой или решетки — это комментарии для нас, модуль PHP их не обрабатывает. Остальные строки содержат директивы для PHP. Найдите директиву display_errors и удостоверьтесь, что ее значение установлено в On. На сервере хостера ошибки не должны демонстрироваться посетителю, но при разработке сценариев без просмотра ошибок не обойтись. Сохраните изменения и перезапустите Apache. Если ошибок нет, то черное DOS-окно откроется и закроется без всяких слов.

Установка сервера MySQL 5 Для создания баз данных и работы с ними нам потребуется установить сервер MySQL и утилиты, которые поставляются вместе с сервером. Дистрибутив сервера можно взять с сайта dev.mysl.com. Это должен быть zip-архив из раздела Windows Downloads (Windows (x86)). Далее в этой главе описывается установка, настройка и работа с MySQL 5. Разархивируйте пакет и запустите на выполнение файл с расширением exe, затем выберите типичный способ установки. Далее сконфигурируйте сервер с помощью мастера (wizard), выбрав вначале стандартный вариант конфигурации. Выберите установку сервера MySQL как сервиса Windows, тогда MySQL будет запускаться при старте системы автоматически. Выберите имя для сервиса, например, MySQL 5. Под этим именем вы в дальнейшем найдете его в списке сервисов в окне, вызываемом командой Администрирование |

Службы.

Укажите, что необходимо включить путь до каталога bin в переменную PATH (рис. П1.4). Переменная PATH содержит имена всех каталогов, в которых сис-


Приложение 1. Установка Web-сервера Apache, модуля PHP 5…

429

тема ищет программу, запускаемую пользователем. Если окажется, что программа находится в каталоге, не указанном в переменной PATH, то Windows не сможет ее найти и сообщит о невозможности запуска приложения.

Рис. П1.4. Конфигурирование сервера MySQL В каталоге bin сервера MySQL содержатся те файлы, которые потребуется запускать при старте сервера, а также утилиты, которые позволят нам связываться с сервером напрямую из командной строки. На следующем этапе задайте пароль для суперпользователя root. Суперпользователь root — это владелец системы, он может выполнять любые действия по управлению сервером и администрированию баз данных. Требования к качеству пароля здесь нежесткие, можете выбрать любое словарное слово, записав его латиницей. Не разрешайте удаленный доступ к серверу для суперпользователя. Разрешение удаленного доступа к серверу существенно ослабит его безопасность и может быть разрешено, только если доступ осуществляется с шифрацией передаваемых данных. Создайте учетную запись для анонимного пользователя. Анонимный пользователь заходит в систему и при этом получает ограниченные права, в частности, он не имеет доступа к системным базам данных. Завершите установку и удостоверьтесь, что сервис MySQL запущен (рис. П1.5). Найдите среди установленных на вашем компьютере программ группу MySQL и запустите клиентскую программу доступа к серверу MySQL Command Line Client из командной строки. Если установка завершилась


Приложения

430

благополучно, то сервер у вас уже запущен, клиентская утилита mysql установит соединение с сервером и предложит ввести пароль. Введите пароль суперпользователя. Таким образом, вы соединитесь с сервером MySQL под именем суперпользователя root, т. е. будете иметь все возможные права для работы. Для завершения сеанса работы с сервером MySQL достаточно дать команду quit в окне клиентского приложения, и его окно закроется.

Рис. П1.5. Запуск сервера MySQL

Прежде чем начать работу с MySQL, надо настроить его для работы с русским языком. Подробное описание этого вы найдете в главе 8. И последнее. В версии PHP 5.2.4 при установке в Windows обнаружилась проблема, про которую русскоязычные гуру говорят, что причина ее науке неизвестна. Проблема же состоит в том, что даже после установки всех необходимых для работы с MySQL расширений PHP они не подключаются до тех пор, пока не скопируешь библиотеку libmysql.dll в каталог Windows\System32. Но к настоящему моменту в вашем распоряжении имеются две таких библиотеки — в каталоге, куда установлен PHP, и в каталоге, где находится MySQL. Так вот копировать нужно ту, что находится в каталоге PHP.


Приложение 2

Отладка JavaScript. Использование Firebug Отладка программ, написанных на JavaScript, всегда была непростым занятием. Часто самым надежным отладочным средством является вызов метода alert, позволяющего в нужный момент вывести в модальном окне значения переменных. Вообще-то для отладки приложений в Internet Explorer существует IE Developer Toolbar, но в действительности российский Интернет говорит только про применение плагина для Firefox, который называется Firebug. Firebug — это расширение для браузера Firefox, являющееся консолью, отладчиком и DOM-инспектором JavaScript, DHTML, CSS, XMLHttpRequest. Firebug показывает в консоли вызвавшую ошибку функцию, стек вызовов функций, вызвавших эту ошибку. Вы можете скачать Firebug с одного из следующих сайтов: http://getfirebug.com/; http://firebug.ru/; https://addons.mozilla.org/en-US/firefox/addon/1843.

Там же есть некоторая документация по этому плагину. Для установки на странице Firebug нажмите кнопку Добавить в Firefox. В верхней части окна может появиться предупреждение о том, что загрузка заблокирована — нажмите Настроить и разрешите загрузку с этого сайта. После установки перезапустите браузер. Firebug будет включаться по нажатию клавиши <F12>. Здесь мы не будем подробно пересказывать документацию по Firebug, а только продемонстрируем его применение для отладки сценариев JavaScript и, особенно, для отслеживания этапов прохождения асинхронного запроса. Справа в строке состояния Firefox вы можете заметить небольшой зеленый значок. Это способ, которым Firebug говорит, что все в порядке. Если значок превращается в красный крест "x", то что-то произошло. Нажмите на "x",


Приложения

432

чтобы открыть консоль ошибок Firebug, где будут отображены все ошибки, которые произошли на странице.

Выполнение и отладка кода JavaScript В Firebug можно запускать код JavaScript прямо из командной строки. Перейдите в Firebug на вкладку Script и напечатайте слово "window" в строке New watch expression... в правой части Firebug, затем нажмите клавишу <Enter>. Теперь можно писать код на JavaScript, запускать его на выполнение можно щелчком по кнопке Run. Посмотрите на рис. П2.1, чтобы оценить удобство такого способа.

Рис. П2.1. Запуск кода JavaScript с помощью командной строки Firebug В JavaScript есть два вида объектов и функций: части обычного DOM и созданные при помощи кода JavaScript. Firebug знает различия и показывает созданные вами объекты и функции наверху списка. Полюбуйтесь на мониторинг объектов с помощью Firebug на рис. П2.2. Есть много различных типов объектов, и Firebug делает все возможное, чтобы визуально подчеркнуть различия и дать побольше информации. Когда это удобно, объекты включают в себя краткое описание содержимого, так что происходящее видно без необходимости делать щелчок. Объекты кодируются цветом, так что HTML-элементы, числа, строки, функции, массивы, объекты и null легко различить.


Приложение 2. Отладка JavaScript. Использование Firebug

Рис. П2.2. DOM-объекты в Firebug

Рис. П2.3. Создание точки останова

433


Приложения

434

Firebug включает в себя отладчик JavaScript, который позволяет приостанавливать выполнение в любой момент и анализировать все, что угодно. Чтобы установить точку останова, щелкните на номере любой строки, и там появится красный кружок, обозначающий, что точка установлена (рис. П2.3). Щелкните на красном кружке еще раз, чтобы убрать точку останова. Пока выполнение приостановлено, вы можете просмотреть значения переменных и состояние объектов.

Просмотр HTTP-заголовков и AJAX-запросов Прохождение AJAX-запросов в Firebug отслеживается как нельзя лучше. Достаточно взглянуть на рис. П2.4 и П2.5.

Рис. П2.4. Заголовки ответа сервера


Приложение 2. Отладка JavaScript. Использование Firebug

Рис. П2.5. Текст ответа сервера

435


Приложение 3

Описание компакт-диска На компакт-диске, прилагаемом к книге, находятся примеры, разобранные в частях I—IV. Номера подкаталогов с примерами — это номера частей, внутри подкаталога примеры пронумерованы так же, как в тексте книги. Таблица П3.1. Содержание компакт-диска Каталоги part 1 part 2 part 3 Soft

Описание

Примеры к части I Примеры к части II Примеры к части III Серверы Apache и MySQL, модуль PHP, редактор NotePad++,

Mozilla Firefox


Литература 1. Richards R. Pro PHP XML and Web Services. — Apress, 2006.

Аткинсон Л. MySQL. Библиотека профессионала. — М.: Вильямс, 2002. 3. Бибо Б., Кац И. jQuery. Подробное руководство по продвинутому JavaScript. — СПб.: Символ-плюс, 2008. 4. Веллинг Л., Томсон Л. Разработка Web-приложений с помощью PHP и MySQL. — М.: Вильямс, 2007. 5. Гутманс Э., Баккен С., Ретанс Д. PHP 5. Профессиональное программирование. — СПб.: Символ-плюс, 2006. 6. Дари К., Бринзаре Б., Черчез-Тоза Ф., Бусика М. AJAX и PHP. Разработка динамических Web-приложений. — СПб.: Символ-плюс, 2006. 7. Дейтел Х., Дейтел П. Как программировать на XML. — М.: Бином, 2001. 8. Закас Н., Мак-Пик Дж., Фосетт Дж. Ajax для профессионалов. — СПб.: Символ-плюс, 2008. 9. Кей М. XSLT. Справочник программиста. — СПб.: Символ-плюс, 2002. 10. Крейн Д., Бибо Б., Локи Т. AJAX. Библиотеки Prototype и Scriptaculous в действии Prototype. — М.: Вильямс, 2008. 11. Кузнецов М., Симдянов И. MySQL на примерах. — СПб.: БХВПетербург, 2007. 12. Кузнецов М., Симдянов И., Голышев С. PHP 5. Практика разработки Web-сайтов. — СПб.: БХВ-Петербург, 2006. 13. Маклафлин Б. Изучаем Ajax. — СПб.: Питер, 2008. 14. Флэнаган Д. JavaScript. Подробное руководство. — СПб.: Символ-плюс, 2.

2008.


Предметный указатель D, E Document Object Model (DOM) 61 ExtJS, библиотека 167 F Firebug 431 FireBug 172 J jQuery Form Plugin 410 jQuery, библиотека 261 JSON 58 L, T Live Query 416 try/catch/throw 32 X XML 51 атрибуты 53 декларация 53 имена элементов 52 кодировка документа 53 комментарии 53 правильнооформленный 52 пространство имен 55 процессуальная инструкция 55 раздел CDATA 57 сущности 57 элементы 52

А Автозагрузка класса 32 Атрибут 15

Б База данных, создание 240 Библиотека: effects.js 150 ExtJS 167 jQuery 261 libxml 75 libxslt 75 Prototype 131 script.aculo.us 149

В, Д Виджет 236 Дата и время 217 Деструктор объекта 17 Доступ к элементам класса 24

З, И Замыкание 47 Запрос 378 GET 385 POST 392 Инкапсуляция 14, 24

К Класс 15 Ajax.Autocompleter 157 Ajax.Base 141 Ajax.InPlaceEditor 161 Ajax.Request 142

Ajax.Response 143 Ajax.Updater 144 Array 137 BoxComponent 183 Component 181 Container 184 DomQuery 187 Element 135 Exception 31 Panel 184 ToolTip 236 Viewport 184 наследник 41 финальный 22 частные элементы 43 Клонирование объектов 19 Ключевое слово: final 22 instanceof 30 private 25 protected 25 public 15, 25 Кнопка 209 Кодировка 81 Кодовая страница 81 Компонент, псевдоним 182 Константа класса 29 Конструктор: класса 16 объекта 37 Копирование объектов 19

Л Локализация: MySQL 89 в PHP 86


Предметный указатель

440

М Метод 14 абстрактный 28 класса, статический 27 Н Набор символов 81 Наследование 15, 20 О Обработка ошибок 45 Объект 14, 17

Attr 68 Document 65 DOMImplementation 68 Element 66, 69 Ext.Element 171 NamedNodeMap 67 Node 64 NodeList 67 Text 68 XMLHttpRequest 93 35 35

базовый браузера

клон 35 клонирование 19 копирование 19 пользовательский 35 создание 36 ленивое 181 Оператор: new 17 try...catch 45

П, Р Панель 196 Парсер 51 Перегрузка 20 Полиморфизм 14, 42 Приложение Web 11 Пространство имен 44 Прототип 39 Псевдоним 182 Расширения для jQuery 410 С Свойство класса15 статическое 27

Селектор: атрибутов 306 базовый 275 в форме 323 видимых и невидимых элементов 302 иерархический 280 основной 286 потомков 316 содержимого 298 состояния элемента формы 326 Синглетон 34, 46 Событие 177, 331 AJAX 402

Т Таблица, редактируемая 240 Ф Форма 209, 210 Функция JavaScript 38


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.