Геннадий Самков
Санкт-Петербург
«БХВ-Петербург»
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
Введение .................................................................................................................. 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
Терминология
В 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 и необязательно. Правда, бросить ее из-за скуки вам
не удастся!
1
Флэнаган Д. JavaScript. Подробное руководство. // Пер. с англ. — СПб.: Символ-плюс, 2008.
2
Веллинг Л., Томсон Л. Разработка Web-приложений с помощью PHP и MySQL. — М.: Виль-
ямс, 2007.
Введение 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 Введение
Источники информации
Как известно, в книге нельзя охватить все вопросы, и читателю нужно иметь
возможность получить дополнительные сведения, например, из Интернета.
Вот адреса, которыми вы можете воспользоваться:
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 (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).
Браузер
Событие JavaScript Запрос HTTP Сервер
Интерфейс Сценарий Обработка запроса
пользователя JavaScript
Объектно-ориентированное
программирование
в серверных приложениях
Принципы объектно-ориентированного
программирования
В реальной жизни мы повседневно имеем дело с объектами. Любые предме-
ты, окружающие нас, можно представлять как объекты, характеризующиеся
некоторыми свойствами. Кроме того, объекты могут совершать действия или
над ними можно производить какие-либо действия.
Понятие объекта в программировании предоставляет программисту возмож-
ность оперировать данными как объектами реальной жизни. Объект пред-
ставляет собой совокупность свойств и операций, выполняемых объектом
или над объектом. Операции принято называть методами.
Объектно-ориентированное программирование базируется на трех основных
принципах: инкапсуляции, наследовании и полиморфизме.
Инкапсуляция подразумевает обращение с объектом как с черным ящиком:
при работе с объектом не видно его внутреннего устройства. Объект дает
возможность составить о нем представление по ограниченному числу дос-
тупных свойств и методов. Инкапсуляция как раз и означает, что объект
представляет собой совокупность данных и операций с ними. Инкапсуля-
ция — это механизм защиты свойств и операций объекта, ограждающий их
от неправильного использования. Данные внутри объекта могут быть дос-
тупны только для других частей этого объекта, но закрыты от программ, на-
ходящихся вне этого объекта.
Полиморфизм позволяет использовать одно и то же имя для решения разных
задач. Иначе говоря, одно и то же имя метода для разных объектов может
означать разные действия.
Глава 2. Объектно-ориентированное программирование в серверных приложениях 15
Ключевое слово public указывает, что после него идет объявление элемента
класса (т. е. свойства или метода). Кроме того, это ключевое слово определяет
16 Часть I. Технологии, составляющие AJAX
механизм доступа к элементу, о чем пойдет речь далее в этой главе. Дадим
свойству значение:
$blood="теплая";
Конструктор класса
При создании объекта — экземпляра класса вызывается функция, которая
инициализирует все требуемые переменные, выполнит все действия, нуж-
ные для полного определения объекта. Эта функция называется конструк-
тором.
В PHP 5 конструктор — это метод, имеющий зарезервированное имя
__construct, и может быть определен так:
function __construct($name)
{
$this->name = $name;
$this ->blood='теплая';
echo "Запущен конструктор класса mammal <br>";
}
Создание объекта
Объект — экземпляр класса, который можно создать с помощью оператора
new, после которого указывается имя класса и параметры, передаваемые кон-
структору:
$cat = new mammal("кошка");
При создании объекта $cat для него устанавливаются значения свойств. По-
лучить доступ к ним можно так:
echo $cat->name;
Деструктор объекта
Объект можно уничтожить в ходе выполнения сценария, вызвав функцию
unset() и передав ей в качестве параметра имя объекта. Но в любом случае
при завершении работы сценария память, занимаемая объектом, высвобож-
18 Часть I. Технологии, составляющие AJAX
При выполнении этого примера обратите внимание на то, что деструктор вы-
полняется именно при вызове функции unset(). Если же вы удалите из сце-
нария строку с этой функцией, то деструктор будет вызван в самом конце
работы после выполнения всех остальных операторов. Деструктор — это
подходящее место для действий, которые наводят порядок после выполнения
различных работ: закрывают соединения с серверами управления базами
данных, очищают память для предотвращения утечек памяти и т. п.
Глава 2. Объектно-ориентированное программирование в серверных приложениях 19
Вот теперь все работает так, как задумывалось. Последние примеры призва-
ны пояснить отличительные черты работы с объектами в PHP 5: при созда-
нии копии объекта с помощью оператора присваивания ($whale=$cat) созда-
ется ссылка на объект $cat, а не копия всех свойств и методов объекта $cat.
Наследование
Можно определить класс beast, являющийся наследником ранее определен-
ного класса mammal:
class beast extends mammal
parent::__construct($name);
echo "Запущен конструктор класса beast <br>";
}
Финальные классы
Теперь мы хотим создать класс cat — потомок класса beast. Класс cat явля-
ется также потомком класса mammal. Мы хотим указать, что у этого класса
cat никаких наследников быть не может. Для этого используется ключевое
слово final:
final class cat extends beast
А так?
echo $Murka->eat;
Нет, нельзя:
Fatal error: Cannot access private property mammal::$eat
Константа класса
В PHP 5 введен такой элемент класса, как константа (листинг 2.12).
Листинг 2.12. Константа класса
<?php
class baza
{
30 Часть I. Технологии, составляющие AJAX
const DBNAME = "taxi";
}
echo baza::DBNAME;
?>
Обработка ошибок
Посмотрим теперь, как в объектном коде реализована обработка ошибок. Мы
видели, что при написании процедурного кода не рекомендуется выводить
сообщения о возможных ошибках, генерируемые самим модулем PHP, на
экран. Лучше предусмотреть возможность ошибки и создать свой код ее об-
работки. Например, может возникнуть проблема с открытием файла, так
Глава 2. Объектно-ориентированное программирование в серверных приложениях 31
Автозагрузка класса
Итак, есть у нас большой проект, при реализации которого мы решили ис-
пользовать объектный подход. Определили классы и записали каждый класс
в отдельный файл — так мы сможем соблюсти принцип модульности про-
граммного обеспечения и подключать объявления классов к любому скрипту,
используя, например, инструкцию include. Теперь каждый из описанных
ранее классов хранится в файлах mammal.php, beast.php и cat.php. Для их
подключения в начале скрипта пишем:
include("mammal.php");
Эта функция принимает один параметр — имя класса. Действия, которые бу-
дут выполняться при вызове функции автозагрузки, придется определить
(листинг 2.16).
Синглетон
Синглетон — это шаблон проектирования, который следует применить в том
случае, когда в приложении необходим только один экземпляр какого-либо
класса. Посмотрите пример в листинге 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 позволяет решать задачи объектно-
ориентированного программирования, т. е. реализовывать инкапсуляцию,
36 Часть I. Технологии, составляющие AJAX
Создание объекта
с помощью оператора new
В JavaScript определен тип данных Object, который и применяется при соз-
дании объектов. Простейший способ создания объекта состоит в вызове опе-
ратора new:
obj = new Object;
obj.x = 1;
obj.y = 2;
Создание объектов
с помощью объектных литералов
Другой путь состоит в использовании объектных литералов, как в листин-
ге 3.1.
Конструктор объекта
Для создания однотипных объектов требуется сначала заполучить объект,
который бы обладал нужными чертами, но который можно было бы исполь-
зовать для того, чтобы по его подобию конструировать другие объекты.
В JavaScript для этой цели используется функция конструктора объекта.
Конструктор — это функция языка, отличающаяся тем, что вызывается она
с помощью оператора new. Ей передается в виде значения ключевого слова
this ссылка на создаваемый пустой объект. Действие конструктора состоит
в наполнении созданного пустого объекта свойствами и методами.
38 Часть I. Технологии, составляющие AJAX
или:
var a = function(x) { return 1 + x; }
Как видите, вызов метода addFName для объекта girl не вызвал ошибки и дал
необходимый результат: свойство f_name был добавлено в объект. В резуль-
тате добавления метода addFName в прототип все экземпляры объектов, соз-
данные на основе этого прототипа, получат новый метод.
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>
Создание класса-наследника
Идея состоит в том, чтобы использовать цепочку прототипов для организа-
ции наследования методов от базового класса. Посмотрите, как это делается
в листинге 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;
42 Часть I. Технологии, составляющие AJAX
}
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; }
a = new A;
b = new B;
a.DoIt();
b.DoIt();
Глава 3. Объектно-ориентированное программирование в JavaScript 43
Пространства имен
В 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
Обработка ошибок
Оператор try...catch используется в тех фрагментах сценария, где может
возникнуть исключение, для его обработки. Он имеет вид:
try {
оператор1
}
catch (исключение) {
оператор2
}
Синглетоны
В некоторых случаях необходимо иметь единственный экземпляр объекта.
Вы уже встречались с такой ситуацией в главе 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>
В этом способе создания объекта есть одна весьма примечательная черта: функ-
ция выполняется в момент создания экземпляра синглетона, что невозможно при
создании объекта с помощью объектного литерала. В последнем случае надо
сначала создать объект, а уж потом вызывать методы созданного объекта.
Замыкания
Фундаментальная статья на эту тему принадлежит Ричарду Корнфорду (Ri-
chard Cornford) — см. http://www.jibbering.com/faq/faq_notes/closures.html.
По-русски на настоящий момент лучшая статья В. Агафонкина на
http://habrahabr.ru/blogs/webdev/38642/.
Как известно, в JavaScript областью видимости локальных переменных (объ-
являемых словом var) является тело функции, внутри которой они определе-
ны. Посмотрите, что происходит, если внутри одной функции определена
другая (листинг 3.12).
48 Часть I. Технологии, составляющие AJAX
return function() {
return ++ numberOfCalls;
}
})();
XML и JSON
Язык XML
XML (EXtensible Markup Language) — расширяемый язык разметки был соз-
дан для описания данных, т. е. каждый тег языка XML предназначен для
того, чтобы объяснить, какой смысл имеет текст, стоящий между открываю-
щим и закрывающим тегами. XML-документ — это просто текст с тегами.
В отличие от языка HTML, в XML теги не определены, при создании XML-
документов автор создает собственные теги.
Синтаксис XML.
Правильно оформленный XML
Программы, читающие и анализирующие XML-документы, называют парсе-
рами (от англ. parse — обрабатывать). Современные браузеры, такие как
Internet Explorer (начиная с версии 6) или Mozilla Firefox, включают в себя
парсеры XML-документов, отвечающие стандартам W3C.
52 Часть I. Технологии, составляющие AJAX
XML-декларация
В начале документа бывает полезно указать, каков конкретный тип этого до-
кумента. XML предоставляет в наше распоряжение специальную декларацию
для того, чтобы пометить документы как документы XML.
XML-декларация всегда начинается с символов <?xml и заканчивается сим-
волами ?>. Декларация должна располагаться в самом начале файла, т. е.
первым символом файла должна быть угловая скобка и никаких концов
строки и пробелов. Декларация сама по себе не является частью XML-
документа. Она не является XML-элементом и может не иметь закрыва-
ющего тега.
Типичная декларация выглядит так:
<?xml version='1.0' ?>
Атрибуты
Элементы XML могут содержать атрибуты в начальном теге. Атрибуты —
это пары "имя = значение", поставленные в соответствие одному из элемен-
тов. Атрибуты применяются для предоставления дополнительной информа-
ции об элементах. Они должны находиться при открывающем теге.
Атрибуты всегда должны иметь значение, даже если оно — всего лишь пус-
тая строка, и эти значения должны заключаться в кавычки. Допускаются как
двойные, так и одинарные кавычки.
Пример:
<file type="gif">computer.gif</file>
Комментарии
Комментарии позволяют вставлять в XML-документ текст, который на самом
деле не является частью документа, а предназначен для тех, кто будет читать
сам исходный XML. Комментарии не могут располагаться внутри тега.
54 Часть I. Технологии, составляющие AJAX
Процессуальная инструкция
Процессуальная инструкция (т. е. инструкция по обработке вкладываемого
в нее текста) имеет следующую общую форму записи:
<?Имя_приложения инструкция ?>
Инструкция передает информацию указанному в ее начале приложению.
Пространства имен XML
Поскольку имена элементов в XML не определены, возможны конфликты,
когда два различных документа используют одно и то же имя для описания
двух различных типов элементов.
В этом XML-документе информация заключена в таблицу (table):
<table>
<tr>
<td>sun</td>
<td>rain</td>
</tr>
</table>
Кроме применения самих префиксов h в этих примерах к тегу table был до-
бавлен атрибут xmlns. Это сделано для того, чтобы дать префиксу имя, свя-
занное с пространством имен. Префикс h в приведенном примере играет та-
кую же роль, как фамилия для ребенка: "Он из семьи Петровых" — могут
сказать про ребенка, определив тем самым его принадлежность к некоторому
множеству (семье).
Особые символы
Некоторые особые XML-символы не могут быть корректно обработаны
и должны заменяться ссылками на сущности.
Глава 4. XML и JSON 57
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>
58 Часть I. Технологии, составляющие AJAX
В этом примере все, что расположено внутри секции 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);
?>
Если вам пока трудно оценить выгоды применения JSON, то представьте се-
бе, что вы пишете сетевую игру, герои которой действуют на клиентских
машинах. На клиентской машине герои фигурируют в виде объектов Java-
Script, их действиями управляет пользователь. Клиентская программа от-
правляет запросы на сервер для того, чтобы сделать эти действия известными
другим участникам. Действия героев приводят к тому, что их свойства меня-
ются, и, возможно, они приобретают новые возможности, описываемые
функциями. На сервере герой представлен в виде объекта PHP. Обработка
данных в случае использования JSON существенно упростится по сравнению
с XML-вариантом.
Глава 5
Root
Nodelist
Root Element <book>
Nodelist NamedNodemap
Объект Node
Базовым объектом DOM является объект Node, методы которого наследуют
прочие объекты — элементные узлы, атрибуты и др. Объекты-наследники
имеют также характерные только им свойства и методы.
В DOM для объекта Node, представляющего узлы документа, определены
свойства и методы, которые могут быть использованы для обработки доку-
ментов. Вот некоторые из них:
nodeName — имя узла;
nodeValue — значение узла;
nodeType — тип узла в виде числа;
textContent — текстовое содержимое узла и его наследников;
parentNode — родитель данного узла;
childNodes — список детей данного узла;
attributes — список атрибутов данного узла;
firstChild — первый дочерний узел данного узла;
lastChild — последний дочерний узел данного узла;
nextSibling — следующий узел, дочерний элемент того же родительско-
го элемента;
previousSibling — предыдущий дочерний элемент, обладающий тем же
родительским элементом, что и данный узел.
Свойство childNodes возвращает упорядоченный список узлов (т. е. объект
класса NodeList). Нумерация узлов в списке начинается с 0. Свойство масси-
ва length показывает количество узлов в списке. Список узлов перестраива-
ется в случае удаления или добавления узла автоматически.
Свойство attributes элементного узла возвращает список атрибутных узлов
(NamedNodeMap), подобный списку узлов за исключением некоторых отличий
методов и свойств. Аналогично массиву элементных узлов массив атрибутов
перестраивается автоматически при удалении или добавлении атрибутов.
Глава 5. Объектная модель документа 65
Объект Element
Объект Element наследует свойства объекта Node и имеет ряд своих специ-
фических свойств, которые можно читать и изменять (табл. 5.4 и 5.5).
Таблица 5.4. Свойства объекта Element
Свойство Описание
Attributes Возвращает список атрибутов элемента в виде объекта
NamedNodeMap
Объект NodeList
Объект NodeList представляет упорядоченный список узлов. Узлы в списке
можно перебрать по их номерам, начиная с нуля.
Объект NodeList всегда содержит актуальную информацию. Если элемент
удаляется из документа или, наоборот, добавляется к документу, список уз-
лов автоматически обновляется. Элементы указываются в списке в том по-
рядке, в котором указаны в самом документе.
Свойство length объекта NodeList возвращает число узлов в списке.
Метод item возвращает тот узел, номер которого указан в качестве аргумента
этого метода.
Объект NamedNodeMap
Атрибуты элемента перечислены в NamedNodeMap — списке атрибутов. Этот
объект представляет собой неупорядоченный список узлов. Доступ к узлам
в объекте NamedNodeMap осуществляется по имени атрибута.
Объект NamedNodeMap всегда содержит актуальную информацию: если ат-
рибут добавляется или удаляется, то список атрибутов обновляется автома-
тически.
68 Часть I. Технологии, составляющие AJAX
Объект 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 Возвращает или устанавливает значение атрибута name
элемента
id Возвращает или устанавливает идентификатор элемента
clientHeight Внутренняя высота элемента
clientLeft Ширина левой границы элемента
clientTop Ширина верхней границы элемента
clientWidth Внутренняя ширина элемента
offsetHeight Высота элемента в скомпонованной странице
70 Часть I. Технологии, составляющие AJAX
Таблица 6.1 (окончание)
Свойство Описание
offsetLeft Расстояние от левой границы элемента до левой границы эле-
мента offsetParent
offsetParent Элемент, от которого ведутся расчеты сдвига текущего элемента
offsetTop Расстояние от верхней границы элемента до верхней границы
элемента offsetParent
offsetWidth Ширина элемента в скомпонованной странице
scrollHeight Видимая высота прокручиваемого элемента
scrollLeft Возвращает или устанавливает размер прокрутки в окне влево
для элемента
scrollTop Возвращает или устанавливает размер прокрутки в окне вниз
для элемента
scrollWidth Видимая ширина прокручиваемого элемента
myDiv = document.getElementById("my");
myDiv.appendChild(paragraf);
}
</script>
</head>
<body onclick="obrabotka()">
<div id="my" />
</body>
</html>
Для того чтобы передать клиенту созданный документ, нам подойдет метод
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');
header('Content-Type: text/xml');
echo $doc->saveXML();
?>
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);
80 Часть I. Технологии, составляющие AJAX
$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 />";
}
?>
Проблема русификации
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 является кодировкой по умолчанию.
82 Часть I. Технологии, составляющие AJAX
и т. д.
Перед каждым шестнадцатеричным кодом байта ставится знак процента. При
таком кодировании на один символ уходит 3 байта.
Альтернативой URL-кодированию является схема base64. Суть этой схемы
состоит в кодировании передаваемой информации 6-битными символами.
84 Часть I. Технологии, составляющие AJAX
Кодирование символов
в сценарии JavaScript
Если в сценарии есть не ASCII-символы, то надо побеспокоиться о том,
чтобы текст сценария был в той же кодировке, что и HTML-страница, в кото-
рую он подключается.
Если сценарий динамически создает элементы, имеющие внешние ссылки, то
компоненты строки нужно закодировать методом escape глобального объек-
та JavaScript. Метод escape преобразует входную строку в шестнадцатерич-
ную кодировку. При этом все символы, не являющиеся символами латиницы,
заменяются на их шестнадцатеричные escape-коды %xx.
Глобальный объект создается исполняющей системой JavaScript перед нача-
лом исполнения сценария и располагает методами, указанными в табл. 8.4.
Таблица 8.4. Методы глобального объекта
Метод Описание
decodeURI Декодирует URI
decodeURIComponent Декодирует компонент URI
encodeURI Кодирует URI
encodeURIComponent Кодирует компонент URI
И все бы хорошо, но работать это будет корректно, только пока ваш сервер
настроен на дефолтовую кодировку Windows-1251. А если вы настроили на
UTF-8, то с удивлением обнаруживаете, что функция strftime в приведен-
ном примере выдает русское название дня недели по-прежнему в кодировке
Windows-1251. Приходится применять функцию iconv:
setlocale(LC_ALL, 'RUS');
echo iconv('windows-1251', 'UTF-8', strftime('%A'));
$russian_chars = array("А", "Б", "В", "Г", "Д", "Е", "Е", "Ж", "З",
"И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х",
"Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я", "а", "б", "в", "г",
"д", "е", "е", "ж", "з", "и", "й", "к", "л", "м", "н", "о", "п", "р",
"с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", "ь", "э", "ю",
"я");
return str_replace($russian_codes,$russian_chars,$string);
}
?>
Локализация 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
Объект XMLHttpRequest
Сначала сценарий JavaScript должен создать сам объект запроса. При генера-
ции объекта не происходит никаких видимых изменений на странице, не от-
правляется еще и сам запрос. После создания объекта запроса его надо на-
полнить данными методом open. Метод open получает в качестве параметра
название HTTP-метода (GET или POST), который будет задействован, адрес
запрашиваемого ресурса, значение параметра async и данные для аутентифи-
кации клиента (логин и пароль), если они необходимы.
Параметр async определяет, в каком режиме будет работать запрос. Если
значение этого параметра true (по умолчанию так и есть), то будет использо-
ваться асинхронный запрос, т. е. выполнение сценария продолжится после
отправки запроса. Пользователь не заметит отправки запроса и сможет про-
должать работу со страницей, не дожидаясь ответа сервера. Если же значение
параметра async равно false, то сценарий будет ожидать ответа сервера,
чтобы продолжить работу. Ясно, что второй вариант может привести к непри-
ятным последствиям при возникновении проблем с сервером или каналами
связи с ним.
Для отправки подготовленного запроса используется метод send. Его единст-
венным параметром является параметр content, значением которого могут
быть данные для POST-запроса или пустая строка в случае использования ме-
тода GET.
Итак, мы видим, что методы объекта XMLHttpRequest позволяют создать
и отправить запрос, а затем прочитать заголовки ответа сервера. Посмотрим
теперь, где находятся данные из ответа сервера (табл. 9.2).
Глава 9. Объект XMLHttpRequest 95
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
{
98 Часть II. Создание AJAX-приложений
// Запрос файла 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
}
catch (e)
{
alert("Невозможно соединиться с сервером:\n" + e.toString());
}
}
}
{
alert("Ошибка при обработке ответа сервера: " + e.toString());
}
}
else
{
// Показываем статус ответа сервера
alert("Проблема с получением данных от сервера:\n" +
xmlHttp.statusText);
}
}
}
header('Content-Type: text/xml');
echo $doc->saveXML();
}
?>
Затем, если нам нужны все данные из ответа, надо перебирать узлы получен-
ного дерева, но мы сделаем проще: найдем узел, содержащий цвет автомоби-
ля. Сделаем это, запросив список элементов, вложенных в корневой элемент.
Свойство childNodes даст желаемый результат, вернув нам массив таких уз-
лов. Из этого массива выберем третий элемент (считаем с 0), найдем первый
вложенный в него элемент с помощью свойства firstChild, это будет тек-
стовый узел, и запросим его свойство data для получения требуемого значе-
ния. Вот теперь переменная responselcolor будет содержать цвет выбранно-
го автомобиля, и этим цветом мы закрасим маленький квадрат, добавив его
в страницу.
Мы получили нужный результат, но, как только требуется более основа-
тельный анализ полученного XML-документа, сценарий обработки разрас-
тается, да и сам XML-формат при большом количестве передаваемой ин-
формации становится неудобным: слишком много в нем избыточности,
поэтому неудивительно, что, несмотря на то, что сама аббревиатура AJAX
включает в себя XML, в настоящее время большинство разработчиков
предпочитает передавать данные в формате JSON, что мы и продемонстри-
руем в следующих главах.
Запрос данных
с сервера 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
);
// Выполнение запроса
if ($result = $this->mysqli->query($query)){
// Выбор данных из ответа сервера MySQL
$arr = $result->fetch_array(MYSQLI_ASSOC);
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);
}
}
}
118 Часть II. Создание AJAX-приложений
Посмотрите на рис. 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>
foreach($massiv as $key=>$value){
120 Часть II. Создание AJAX-приложений
$this->$key = $value;
}
$result->close();
}
}
?>
при более или менее значительном размере данных становится просто удру-
чающим. К счастью, в настоящее время нет необходимости проходить все
эти испытания вновь и вновь. Мировым сообществом программистов создано
значительное количество библиотек, позволяющих ускорить Web-разработку.
Мало кто из разработчиков придерживается до сих пор того аскетичного
подхода, который исключает возможность применения одной или несколь-
ких библиотек. Поэтому следующие главы посвящены применению таких
библиотек, и именно с их помощью мы покажем, как создавать красивые
и удобные Web-приложения на основе технологии AJAX.
Обзор библиотек
для создания 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
Библиотека Prototype
Для применения метода $$() надо знать, как именно обозначаются парамет-
ры этого метода. Приведем несколько коротких примеров:
$$('div') — указываем имя тега, метод возвращает массив всех элемен-
тов, созданных тегом div;
$$('#contents') — указываем значение идентификатора, и, несмотря на
то, что в документе может быть только один элемент с таким идентифика-
тором, метод $$() все равно возвращает массив, состоящий, правда, из
одного элемента;
$$('.faux') — выбираем все элементы, относящиеся к стилевому классу
faux;
$$('li.faux') — выбираем все элементы списка li, относящиеся к сти-
левому классу faux.
Метод f() — первый из методов Prototype, на которых начинаешь чувство-
вать, что жизнь становится легче и приятнее. Посмотрите пример, приведен-
ный в листинге 13.2, и вы поймете, что это действительно так. Здесь метод
f() возвращает строку, содержащую значение поля ввода формы, длину по-
лученной строки мы читаем из ее свойства length.
134 Часть III. Библиотеки для работы с AJAX
<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 [...]]]) во display в значение '' (т. е. пустая строка)
hide(elem1 [, elem2 [, Прячет элементы, устанавливая их стилевое
elem3 [...]]]) свойство display в значение 'none'
addClassName(element, Добавляет элементу стилевой класс
className)
136 Часть III. Библиотеки для работы с AJAX
Таблица 13.2 (окончание)
Метод Описание
setStyle(element, styles) Устанавливает стилевое свойство для указанно-
-> HTMLElement го элемента (см. листинг 13.8)
removeClassName(element, Удаляет указание стилевого класса элемента
className)
hasClassName(element, Возвращает true, если элемент относится
className) к указанному классу
getHeight(element) Возвращает свойство offsetHeight элемента
toggleClassName(element, Меняет CSS-класс элемента и возвращает сам
className) -> HTMLElement элемент
getDimensions(element) -> Вычисляет ширину и высоту элемента (width
{height: Number, width: и height) и возвращает их в виде литерального
Number} объекта
hasClassName(element, Проверяет, относится ли элемент к указанному
className) -> Boolean классу CSS
DOM- функции
remove(element) Удаляет элемент из документа
replace(element[, html]) -> Заменяет элемент на содержимое аргумента
HTMLElement html и возвращает удаленный элемент
update(element[, Заменяет содержимое элемента на значение
newContent]) аргумента newContent и возвращает элемент
Расширение DOM
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
Класс Array
Метод $$(), как мы уже упоминали, возвращает массив DOM-элементов
в том порядке, в каком они встречаются в документе. А раз это массив, то
и обращаться с ним можно как с объектом Array из Prototype. В табл. 13.3
описаны основные методы этого объекта.
Эти методы представляют собой расширение набора функций JavaScript об-
работки массивов: сортировка, удаление повторяющихся элементов, преоб-
разование массива в другие форматы данных и т. п.
Таблица 13.3. Методы объекта Array
Метод Описание
clone Возвращает копию массива, исходный массив не изменяется
compact Удаляет из массива пустые элементы
each Перебирает все элементы массива
138 Часть III. Библиотеки для работы с AJAX
Таблица 13.3 (окончание)
Метод Описание
first Выбирает первый элемент массива
last Выбирает последний элемент массива
size Возвращает размер массива
toJSON Возвращает элементы массива в формате toJSON
font-weight: bold;
font-size: 10pt;
}
.TableContent2
{ font-family: Verdana, Arial;
font-size: 10pt;
}
AJAX в Prototype
Для создания AJAX-запросов с помощью Prototype можно использовать объ-
екты классов, наследующих базовому классу, определенному в Prototype —
это класс Ajax.Base. Приведем фрагмент его кода, чтобы читателю стало
ясно, какие опции запросов установлены с самого начала:
Ajax.Base = function() {};
Ajax.Base.prototype = {
setOptions: function(options) {
this.options = {
method: 'post',
asynchronous: true,
142 Часть III. Библиотеки для работы с AJAX
contentType: 'application/x-www-form-urlencoded',
encoding: 'UTF-8',
parameters: ''
}
}
}
Первый параметр объекта — адрес запроса, запрос будет выполнен, если за-
данный здесь адрес указывает на тот же хост, откуда была получена текущая
страница. Второй параметр вызова 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,
обеспечивая работу с ним некоторыми дополнительными возможностями.
144 Часть III. Библиотеки для работы с AJAX
Класс 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
Класс 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 });
Как видите, свойство colorcode вполне подходит для того, чтобы его значе-
ние стало цветом квадратика на нашей странице.
Подводя итоги этой главы, можно сказать, что Prototype позволяет облегчить
жизнь Web-программиста, хотя и не содержит поражающие воображение
средства создания пользовательских интерфейсов. Несмотря на некоторые
недостатки официальной документации, использовать эту библиотеку
несложно.
Глава 14
Библиотека script.aculo.us
Так можно указать файлы библиотек builder, effects, dragdrop, controls и slid-
er. Обратите внимание, что для обеспечения их правильной работы сценарии
нужно подключать в указанном порядке.
Эффекты
Библиотека визуальных эффектов (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]);
Легко понять, что для создания эффекта достаточно вызвать метод 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
});
<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>
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);
158 Часть III. Библиотеки для работы с AJAX
}
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>
Класс Ajax.InPlaceEditor
Класс Ajax.InPlaceEditor позволяет редактировать текст и сохранять его на
сервере. Текст может загружаться в область редактирования из серверного
файла или создаваться пользователем с нуля.
Синтаксис создания объекта:
new Ajax.InPlaceEditor(element, url, {options});
Структура и идеология
библиотеки
Вокруг уже шуршали пески и шумели
водопады милой моему сердцу Внут-
ренней Монголии.
В. Пелевин
Соглашения об именах
При создании библиотеки 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>
170 Часть IV. Библиотека ExtJS
П РИМЕЧАНИЕ
Обратите внимание, что, как и в других библиотеках JavaScript, в ExtJS исполь-
зуется исключительно кодировка UTF-8.
В файле ExtStart.js у нас будет следующий код (листинг 15.2).
Листинг 15.2. Сценарий ExtStart.js
Ext.onReady(function() {
alert("Поздравляю! Ваша Ext сконфигурирована правильно!");
});
Далее в этой главе будет много теории. При первом чтении ее можно пропустить,
но после разбора примеров следует возвращаться к ней и перечитывать различ-
ные фрагменты, что приведет к более глубокому пониманию логики ExtJS.
Объект Ext.Element
Вся работа в JavaScript начинается с выбора элемента HTML-страницы.
Мы запрашиваем его, вызывая привычный метод getElementById:
var myDiv = document.getElementById('myDiv');
Контекст
Мы будем применять термин "контекст" для обозначения области действия
или области видимости свойства (переменной) или метода (функции). Тер-
мин этот соответствует английскому "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); }};
Еще бы! Объект 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
Ext.extend(MyNewClass, SomeBaseClass, {
theDocument: Ext.get(document),
myNewFn1: function() {
// Необходимые действия
},
myNewFn2: function() {
// Еще какие-то нужные действия
}
});
События DOM
Браузеры применяют механизм обработки событий при отображении
XHTML-страниц и создают события, когда пользователь выполняет какие-
либо действия над элементами DOM.
Привычный способ обработки выглядит так:
<div id="mydiv" onclick="alert('Щелкнули!')">Щелкни здесь!</div>
События JavaScript
Элементы DOM — не единственные источники событий. Вполне естественно
распространить логику генерации и обработки событий на любой объект
JavaScript. Многие объекты ExtJS могут извещать обработчиков о наступле-
нии события.
178 Часть IV. Библиотека ExtJS
Пользовательские события
Весьма часто требуется добавить пользовательское событие, которое не
встроено в Framework ExtJS. Рассмотрим, например, два класса: Employee
(Сотрудники) и OrgChart (Структура фирмы). Хочется устроить так, чтобы
можно было простым перетаскиванием элементов менять принадлежность
сотрудника к тому или иному подразделению, меняя при этом и должность
этого сотрудника.
В следующем примере мы создадим и такие обработчики событий, которые
будут рассылать письма, оповещающие сотрудников о новых назначениях.
Вот как это делается (листинг 15.5).
Xtypes
Xtype — это псевдоним класса, только и всего. Есть, например, у нас класс
Ext.ux.MyGrid. Это обычное имя класса, которе мы используем, создавая
объект.
Глава 15. Структура и идеология библиотеки 181
Иными словами:
return new Ext.ux.MyGrid(config);
Классы ExtJS
Опишем некоторые важные классы ExtJS, к ним надо будет обращаться либо
явно, либо по цепочке наследования.
Класс Component
Класс Component предоставляет унифицированную модель для создания
компонентов страницы, их отображения, управления ими и, наконец, унич-
тожения.
182 Часть IV. Библиотека ExtJS
viewport — Ext.Viewport;
window — Ext.Window;
form — 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
Поиск элементов:
класс 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.
188 Часть IV. Библиотека ExtJS
Селекторы элементов
Ext.query("span") — функция вернет массив элементов span;
Ext.query("span" "foo") — элементы span с идентификатором foo;
Ext.query("#foo") — элементы с идентификатором foo;
Ext.query(".foo") — отобрать элементы класса foo;
Ext.query("*") — все элементы документа;
Ext.query("div span") — отобрать все элементы span внутри элемен-
тов div.
Селекторы атрибутов
Эти селекторы позволяют отобрать элементы по значениям их атрибутов:
Ext.query("*[class]") — все элементы с атрибутом class;
Ext.query("*[class = bar]") — элементы класса bar;
Ext.query("*[class! = bar]") — все элементы кроме тех, что из класса
bar;
Ext.query("*[class^ = b]") — элементы класса, имя которого начинает-
ся с b, т. е. при отборе можно использовать регулярные выражения.
Примеры:
Ext.query("*{color = red}") — текстовые элементы красного цвета;
Ext.query("*{color = red} *{color = pink}") — все розовые элемен-
ты, являющиеся наследниками красных;
Ext.query("*{color! = red}") — отобрать все элементы кроме красных;
Ext.query("*{color^ = yel}") — элементы, название цвета которых на-
чинается с "yel", т. е. снова можно применять регулярные выражения;
Ext.query("*{color* = ow}") — все элементы, в значении атрибута
color которых есть подстрока "ow".
Глава 16. Поиск элементов: класс DomQuery 189
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": highlight(Ext.query('ul .goofy > strong', "extdt"));
break;
192 Часть IV. Библиотека ExtJS
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
Выглядеть у вас в браузере это должно так, как показано на рис. 16.1.
Код получился довольно длинным, объясним его здесь, но при первом чте-
нии это объяснение можно пропустить, вернувшись впоследствии, когда раз-
беретесь с некоторыми методами, описываемыми в следующих главах.
Начнем разбор с конца сценария. В самом конце вызывается функция
Ext.onReady, срабатывающая после загрузки DOM. Она вызывает функцию
domquery.init().
Эта функция и выполняет всю основную работу. Она привязывает событие
click к элементам раздела div класса dom-traversal-toggles. Каждый
Глава 16. Поиск элементов: класс DomQuery 195
Простая панель
Создадим на странице панель, которую можно будет сворачивать и развора-
чивать щелчком по встроенной в нее особой кнопке. Панель должна появить-
ся в самом верху документа, поэтому составляющие ее элементы должны
быть среди первых наследников элемента body. Сделаем так: добавим в нача-
ло документа новый пустой div, а к нему прикрепим панель. Вид простой
панели в браузере представлен на рис. 17.1.
Код создания простой панели дан в листинге 17.1.
Вложенные панели
Вложим теперь одну панель в другую (листинг 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. Панели и компоновка элементов 199
// Конфигурируем Viewport
viewport = new Ext.Viewport({
layout: 'border',
items: [actionPanel,tabPanel]});
var actions = {
'create': function(){
addTab("Новая вкладка",'1.html');
},
'use': function(){
updateTab('tab1','Заменялось' + count + 'раз','sample' +
(count%2) + '.html');
count++;
}
};
Весь наш код обернут в метод Ext.onReady, так что он не будет выполняться
до того, как в браузер будут загружены все элементы HTML-документа.
Первое, что мы делаем, — создаем объект tabActions, представляющий со-
бой панель для меню. Оно будет отображаться слева в окне на синем фоне.
Для этого потребуется конвертировать список <ul> с идентификатором
actions в панель tabActions. Значением конфигурационного параметра
contentEl как раз и должен быть идентификатор подходящего для этой цели
списка.
Параметр frame со значением true позволяет указать, что панель должна
обрамляться скругленными рамочками.
Затем создадим родительскую панель actionPanel для меню; значение пара-
метра items должно указывать на имя панели tabActions, подключаемой
внутрь родительской панели. Панель actionPanel будет позиционирована на
странице с помощью специального менеджера, так что нам потребуется ука-
зать значение параметра region при конфигурировании. Параметр baseCls
указывает на то, какой стилевой класс будет использован для панели (по
умолчанию это 'x-panel').
Третий компонент, который надо создать, — главная панель TabPanel с вклад-
ками. Она должна располагаться в середине страницы, что соответствует
208 Часть IV. Библиотека ExtJS
Формы
Создание элемента формы
Для создания любого элемента формы в 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>
210 Часть IV. Библиотека ExtJS
Компоновка формы
Теперь на очереди создание формы и компоновка ее вызовом соответствую-
щего способа макетирования. Применим здесь компоновку 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>
window.show();
});
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();
});
},
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: true,
title: 'Диапазон дат',
bodyStyle: 'padding:5px 5px 0',
width: 350,
defaults: {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');
220 Часть IV. Библиотека ExtJS
/* =============== Проверка пароля ====================== */
var pwd = new Ext.FormPanel({
labelWidth: 125,
frame: true,
title: 'Проверка пароля',
bodyStyle: 'padding:5px 5px 0',
width: 350,
defaults: {
width: 175,
inputType: '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. Формы 221
Визуальные эффекты.
Drag & drop
</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>
});
}
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);
});
});
Итак, в конце скрипта три раза вызывается метод Ext.get, каждый раз выби-
рая одну из ссылок вверху страницы. Методом on мы для каждой ссылки за-
даем обработчик события click. Обработчик вызывает функцию slideText
с необходимыми параметрами и функцию frame, создающую серую (цвета
#cccccc) подвижную рамку.
Перейдем теперь выше по тексту, к функции slideText. Получив элемент,
на котором произошло событие, мы быстренько записываем его в перемен-
ную slideMe.
Дальше у нас три дороги: параметр direction может принимать одно из сле-
дующих значений — up down или toggle. Последняя — дорога по умолча-
нию. При выборе любой из трех дорог мы вначале проверяем, виден ли раз-
дел слайдера, а потом выполняем необходимые в данном случае действия,
вызывая функции slideIn, slideOut или toggle.
Параметр t сообщает методам slideIn или slideOut, что эффект надо при-
менять, начиная с верхнего края элемента. Можно было указать, например,
l — для применения к левому краю.
При задании конфигурационных параметров метода slideOut мы указали
easing:'easeOut'. Это приводит к тому, что эффект длится полсекунды,
элемент не удаляется из DOM и разрешает соседним элементам занять место,
освободившееся после скрытия нашего элемента, создавая таким образом
требуемый анимационный эффект.
</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');
}
};
}();
Усложним нашу задачу. Допустим, нам надо иметь два контейнера с элемен-
тами, перетаскивать и бросать блоки в любом месте страницы, но в случае
если блок сброшен над каким-то контейнером, он аккуратно размещается
в нем автоматически. Добавляем в предыдущую страницу еще одну область
с перетаскиваемыми элементами (листинг 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, Tutori-
al.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
Мы создали две зоны (dz1 и dz2) для сбрасывания элементов из тех же кон-
тейнеров, которыми оперировали ранее. Кроме собственно перетаскивания
сценарий применяет к блокам на различных этапах различные стили.
В результате усовершенствований сценария блоки можно перетаскивать из
их контейнера и помещать в любом месте на странице. При перетаскивании
элемента по контейнеру последний вспыхивает более ярким цветом. На вре-
мя перетаскивания блок бледнеет. Блок можно бросить и в исходный контей-
нер. Если блок сброшен чуть мимо контейнера, он автоматически сам акку-
ратно встраивается в контейнер. Вот как это выглядит в браузере (рис. 19.3).
Простые виджеты
Всплывающие подсказки
Класс подсказок ToolTip расширяет класс Ext.Panel и дает возможность вы-
водить на экран дополнительную информацию при наведении курсора на
какой-либо элемент.
HTML-документ, в котором работают подсказки, приведен в листинге 20.1.
<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>
target: 'track-tip',
title: 'Mouse Track',
width:200,
html: 'Эта подсказка будет следовать за курсором, пока он нахо-
дится на элементе',
trackMouse:true
});
Ext.QuickTips.init();
});
(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`, `last-
name`, `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);
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
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;
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,
252 Часть IV. Библиотека ExtJS
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
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,
256 Часть IV. Библиотека ExtJS
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
Знакомство с 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
<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>
этого кода, первый и пятый элементы input только в первой форме будут
иметь бирюзовый цвет фона.
jQuery(html)
Эта функция создает элементы DOM "на лету", используя переданную в ка-
честве аргумента строку, содержащую HTML-код.
Единственный параметр — html. Это строка, содержащая HTML-код для
создания элементов DOM.
Функции можно передавать строку HTML-кода как написанную "руками",
так и созданную автоматически, например шаблонными движками или пла-
гинами. Строку HTML-кода можно передавать в функцию и через AJAX.
Пример представлен в листинге 23.3.
В листинге 23.3 приведен код, который создает элемент div и внутри него
параграф со знакомым выражением "Hello, world!". Затем созданные элемен-
ты добавляются в элемент body. И опять все начинается с функции $().
Только здесь ей передается не селектор, а строка HTML-кода.
Глава 23. Функции ядра jQuery 269
each(callback)
Итак, мы имеем элемент body, который содержит три элемента div. Элемен-
ты div в свою очередь содержат текст. Шрифт текста имеет по умолчанию
черный цвет. При щелчке мышью в любом месте элемента body будет создан
набор элементов, содержащий все div-элементы на текущей странице, и для
каждого из них последовательно будет выполнена функция, которую мы пе-
редали методу each в аргументе callback. Для первого элемента набора бу-
дет установлен красный цвет шрифта, для второго — синий, для третьего —
зеленый.
length
Селекторы jQuery
Базовые селекторы
Базовые селекторы — наиболее употребляемые на практике, понадобятся
в большинстве случаев.
#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>
276 Часть V. jQuery
<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>
<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"); // эт