Современные веб-технологии

Создание динамического наполнения страницы. JavaScript-библиотеки и технология Comet

Разбить на страницы
Показывать лекцию целиком

Презентацию к данной лекции Вы можете скачать здесь.

13.1. Представление объектов в виде JSON

JSON ( JavaScript Object Notation) – текстовый формат обмена данными, основанный на JavaScript и обычно используемый именно с этим языком [1]. Как и многие другие текстовые форматы, JSON легко читается людьми.

Несмотря на происхождение от JavaScript, формат считается независимым от языка и может использоваться практически с любым языком программирования. Для многих языков существует готовый код для создания и обработки данных в формате JSON.

13.1.1. Использование

Практическая польза использования JSON открывается при использовании технологии AJAX. Формат JSON является более кратким и удобочитаемым по сравнению с XML, является "родным" для JavaScript (преобразуется в структуру данных встроенной функцией eval() ). Кроме того, в JSON -код возможна вставка вполне работоспособных функций.

13.1.2. Синтаксис

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

{
   "firstName": "Иван",
   "lastName": "Иванов",
   "address": {
       "streetAddress": "Московское ш., 101, кв.101",
       "city": "Ленинград",
       "postalCode": 101101
   },
   "phoneNumbers": [
       "916 123-4567"
   ]
}

На языке XML подобная структура выглядела бы примерно так:

<person>
  <firstName>Иван</firstName>
  <lastName>Иванов</lastName>
  <address>
    <streetAddress>Московское ш., 101, кв.101</streetAddress>
    <city>Ленинград</city>
    <postalCode>101101</postalCode>
  </address>
  <phoneNumbers>
    <phoneNumber>916 123-4567</phoneNumber>
  </phoneNumbers>
</person>

13.1.3. Использование JSON в Ajax

Следующий фрагмент Javascript -кода показывает, как клиент может использовать XMLHttpRequest, чтобы запрашивать с сервера объект в формате JSON:

var the_object;
var http_request = new XMLHttpRequest();
http_request.open("GET", url, true);
http_request.send(null);
http_request.onreadystatechange = function() {
    if (http_request.readyState == 4) {
        if (http_request.status == 200) {
            the_object = eval("(" + http_request.responseText + ")");
        } else {
            alert("Проблема с URL.");
        }
        http_request = null;
    }
};

Полезность XMLHttpRequest ограничивается политикой ограничения домена: URL ответа на запрос должен проживать в том же DNS домене, что и сервер, на котором находится страница, содержащая запрос.

Браузеры могут также использовать элементы <iframe> асинхронный запрос JSON данных в кросс-браузер мода, или использовать простые <form action="url_to_cgi_script" target="name_of_hidden_iframe"> представлений. Эти подходы были распространены до появления широкой поддержки XMLHttpRequest.

13.1.4. Вопросы безопасности

Хотя JSON предназначен для передачи данных в сериализованном виде, его синтаксис соответствует синтаксису JavaScript и это создает ряд проблем безопасности. Зачастую для обработки данных, полученных от внешнего источника в формате JSON, к ним применяется функция eval() без какой-либо предварительной проверки.

13.1.4.1. JavaScript eval()

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

Техника использования eval() делает систему уязвимой, если источник используемых JSON -данных не относится к доверенным. В качестве таких данных может выступать вредоносный JavaScript код для атак класса "Внедрение кода". С помощью данной уязвимости можно осуществить кражу данных, подделку аутентификации. Тем не менее, уязвимость можно устранить за счет использования дополнительных средств проверки данных на корректность. Например, до выполнения eval() полученные от внешнего источника данные могут проверяться с помощью регулярных выражений. В RFC, определяющей JSON, предлагается использовать следующий код для проверки его соответствия формату JSON:

var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, '')))  eval('(' + text + ')');

Как более безопасная альтернатива eval() была предложена новая функция parseJSON(), способная обрабатывать только JSON -данные.

13.1.4.2. Подделка кроссдоменного запроса

Непродуманное использование JSON делает сайты уязвимым к подделке межсайтовых запросов (CSRF или XSRF). Поскольку тэг <script> допускает использование источника, не принадлежащего к тому же домену, что и использующий ресурс, это позволяет выполнять код под видом данных, представленных в формате JSON, в контексте произвольной страницы, что делает возможным компрометацию паролей или другой конфиденциальной информации пользователей, прошедших авторизацию на другом сайте.

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

13.2. Библиотека ExtJS

ExtJS – библиотека JavaScript для разработки веб-приложений и пользовательских интерфейсов, изначально задуманная как расширенная версия Yahoo! UI Library, однако, преобразовавшаяся затем в отдельный фреймворк. Использует адаптеры для доступа к библиотекам YUI, jQuery или Prototype /script.aculo.us. Поддерживает технологию AJAX, анимацию, работу с DOM, реализацию таблиц, вкладок, обработку событий и все остальные новшества "Веб 2.0" [2, 3].

С версии 2.1 библиотека ExtJS распространяется по условиям трех лицензий: Commercial License, Open Source License и OEM / Reseller License.

Начиная с версии Ext JS 3.0, библиотека разбивается на две части: Ext Core (набор JavaScript функций, позволяющий создавать динамические веб-страницы и распространяемый по MIT-лицензии) и Ext JS (набор виджетов для создания пользовательских интерфейсов, распространяемый аналогично Ext JS 2.1 по условиям трех лицензий).

13.2.1. Объектно-ориентированное программирование

Одна из задач, которую ставили себе разработчики библиотеки ExtJS при ее проектировании, была возможность сосуществования с другими JavaScript -библиотеками и фреймворками [4, 5]. Для этого были добавлены удобные методы, помогающие разработчику создавать, наследовать и сопровождать код различных классов. Несмотря на то, что все они были специально созданы для работы с компонентами ExtJS, их можно использовать с любыми объектами JavaScript, в том числе в проектах, не задействующих визуальные компоненты.

13.2.1.1. Ext.namespace

Синтаксис:

Ext.namespace( String namespace1, String namespace2, String etc ) : void

В языке JavaScript пространство имен само является объектом, содержащим список определений других объектов. Правильное использование пространств имен гарантирует, что ваш код не будет конфликтовать с другими библиотеками. Однако чем больше становится кода в библиотеке, тем глубже иерархия имен и такие классы как Ext.ux.graphing.GraphPanel перестают быть редкостью. В ExtJS существует метод Ext.namespace, который позволяет определить все пространства имен за один вызов, уменьшая не только затраты на поддержку кода, но и сокращая его размер.

Традиционный код JavaScript, создающий иерархию пространства имен, выглядит следующим образом:

var Ext = Ext || {};
Ext.ux = Ext.ux || {};
Ext.ux.graphing = Ext.ux.graphing || {};
Ext.ux.soundFx = Ext.ux.soundFx || {};
Ext.ux.maps = Ext.ux.maps || {};

Того же результата можно достичь вызовом всего одного метода:

Ext.namespace("Ext.ux.graphing", "Ext.ux.soundFx", "Ext.ux.maps");

13.2.1.2. Ext.override

Синтаксис:

Ext.override( Object origclass, Object overrides ) : void

JavaScript очень гибкий язык, и переопределение метода в нем выполняется простым переназначением его объекта другой функции:

// создаем функцию
function doesStuff() {
  alert("Функция один!");
};
// теперь ее перекрываем
doesStuff = function() {
  alert("Функция два!");
};

Перекрытие метода класса осуществляется путем переопределения его прототипа:

function MyClass() {
  // конструктор класса
}
MyClass.prototype.myMethod = function() {
  alert("привет");
};
var x = new MyClass();
x.myMethod(); // отобразит "привет"
MyClass.prototype.myMethod = function() {
  alert("пока");
};

x.myMethod(); // отобразит "пока"

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

Ext.override – это удобный способ переопределения методов класса за один присест. Следующий пример перекрывает два метода класса Ext.ux.graphing.GraphPanel. Следуя общепринятым правилам языка JavaScript, если в оригинальном классе не содержатся определений методов, то они добавляются. Первым параметром идет класс, который будет изменен, а вторым – JSON объект с методами, подлежащими переопределению:

Ext.override(Ext.ux.graphing.GraphPanel, {
  clearAllPoints: function() {
    // выполнить другие действия
  },
  loadData: function(newData) {
    // загрузка новых данных
  }
});

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

13.2.1.3. Ext.extend и соглашения о параметрах конструкторов

Синтаксис:

Ext.extend( Function superclass, Object overrides ) : Function

или

Ext.extend( Function subclass, Function superclass, [Object overrides] ) : Function

Рассмотренный ранее метод Ext.override выполняет замену реализации на новый метод целиком, а Ext.extend позволяет осуществить полноценное наследование согласно парадигме ООП. Имеются два варианта вызова этого метода, каждый из которых возвращает определение нового класса, содержащего объекты, унаследованные от родительского, определенного в параметре superclass, и перекрывает методы, заданные в параметре overrides. Также он добавляет к новому классу служебный метод override(), который дает возможность перекрывать методы экземпляра класса, и такое же служебное свойство superclass, позволяющее получить доступ к родительскому объекту.

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

Ниже приведен пример использования Ext.extend. Обратите внимание, что имеются два способа наследования. Первый:

var newClass = Ext.extend(Ext.form.ComboBox, {
  constructor: function(config) { // явное задание конструктора
    config.emptyText = "'5' не допускается";
    newClass.superclass.constructor.call(this, config);
  },
  setValue: function(v) {
    if (v == 5) { // если v равно 5, сообщить пользователю
      alert("'5' не допускается");
    } else { // иначе вызываем метод родителя
      newClass.superclass.setValue.call(this, v);
    }
  }
});

Второй:

function newClass(config) { // конструктор
  config.emptyText = "'5' не допускается";
  newClass.superclass.constructor.call(this, config);
};
Ext.extend(newClass, Ext.form.ComboBox, {
  setValue: function(v) {
    if (v == 5) { // если v равно 5, сообщить пользователю
      alert("'5' не допускается");
    } else { // иначе вызываем метод родителя
      newClass.superclass.setValue.call(this, v);
    }
  }
});

Так как все объекты ExtJS имеют одну и ту же форму указания параметров в конструкторе, это позволяет работать с ними без особых усилий: достаточно скопировать все значения объекта config. ExtJS имеет два встроенных метода для выполнения этих действий, которые далее будут рассмотрены.

13.2.1.4. Ext.apply

Синтаксис:

Ext.apply( Object obj, Object config, [Object defaults] ) : Object

Метод Ext.apply производит копирование каждого члена объекта config в объект obj. Опционально может быть задан параметр defaults, содержимое которого также копируется в объект obj, но перед выполнением копирования объекта config.

13.2.1.5. Ext.applyIf

Синтаксис:

Ext.applyIf( Object obj, Object config ) : Object

Метод Ext.applyIf производит копирование каждого члена объекта config в объект obj, но только в том случае, когда этот член еще не существует в объекте назначения.

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

function MyClass(config) { // конструктор, который принимает конфигурацию в параметре 'config'
  // присвоение некоторых значений по умолчанию, если они не заданы в конфигурации
  config = Ext.applyIf(config, {
    someValue: defaultValue
  });

  // а здесь присваиваются значения по умолчанию, игнорирующие параметры конструктора
  config = Ext.apply(config, {
    someOtherValue: defaultValue
  });

  // вызов конструктора класса-родителя с передачей новой конфигурации
  MyClass.superclass.constructor.call(this, config);
}

// не забудьте унаследовать родительский класс
Ext.extend(MyClass, MyBase, {
  // ...
});

13.2.2. Построение веб-интерфейсов

Библиотека ExtJS написана на JavaScript и работает во всех популярных сейчас браузерах (хотя и с некоторыми различиями), предназначена для создания сложных и насыщенных интерфейсов, которые очень похожи на аналогичные desktop-программы [5, 6]. Она предоставляет разработчику целый набор графических компонентов, от тривиальных кнопок и расширенных элементов обычных HTML-форм, до сложных компонентов вроде таблиц, лейаутов (layout – компоненты, отвечающие за расположение объектов друг относительно друга) и деревьев. В библиотеке также есть достаточно много невидимых пользователю компонентов, которые и обеспечивают работу того, что пользователь видит на экране. Это и получение данных с сервера в фоновом режиме (в формате JSON или XML), обновление частей страницы, локальные хранилища данных, поддержка cookie и многое другое.

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

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

13.2.3. Использование CSS

В архитектуре библиотеки есть и такое понятие как тема оформления – это обычный CSS-файл, изменяя который можно мгновенно изменить внешний вид всех элементов интерфейса. Уже создано множество различных пользовательских тем, в разной цветовой гамме и стиле, хотя, на мой взгляд, именно стандартная тема наиболее приятная и привычная для большинства пользователей.

13.2.4. Основные компоненты и возможности

Для рассмотрения библиотеки ExtJS можно разделить ее компоненты на несколько групп.

Самый нижний слой – это низкоуровневые функции по работе с DOM-деревом, событиями, стилями и другие функции. Здесь можно использовать адаптеры для подключения нескольких библиотек. В особенности это нужно, когда необходимо смешать в одном приложении функциональность, к примеру, jQuery и ExtJS. Во последних версиях ExtJS можно использовать как родные функции, так и другие библиотеки: jQuery, Yahoo UI!, Prototype.

Core – это основные базовые модули и функционал, который необходим для работы всех остальных компонентов библиотеки и приложения. Он разбит на несколько модулей:

  • Ядро – это обязательный компонент, обеспечивает общий менеджер событий, работу с адаптерами и основные операции с DOM-моделью. Для построения самых простых приложений можно использовать только этот компонент, так как в него включены все необходимые функции, в том числе и UpdateManager для обновления любой части страницы по расписанию или событию от пользователя.
  • Для использования визуальных компонентов (виджетов) нужен модуль, который обеспечивает их работу, рендеринг и управление наборами виджетов. ExtJS поддерживает функции отложенного рендеринга (lazy render) когда все операции по отрисовке или обновлению элементов координируются самой библиотекой, а это экономит время и делает приложение быстрее.
  • Утилиты – добавляют функции работы с JSON, обработку CSS, работу с событиями клавиатуры и базовую поддержку шаблонов.
  • Поддержка кросс-браузерного DragDrop вынесена в отдельный модуль (нужен, если используются виджеты).
  • Возможность хранить состояние интерфейса в независимом хранилище, например cookie (компонент State Manager ) – при повторном входе все открытые окна или вкладки в браузере будут восстановлены.
  • Работа с данными. Так как любое веб-приложение основано на каких-то данных, то этот слой в библиотеке не менее насыщенный различным функционалом, чем визуальные компоненты. Обмен данными с сервером может вестись в двух форматах – JSON, как самый простой для JavaScript, и XML, что позволяет реализовать более широкий функционал, но за счет увеличения ресурсоемкости всего приложения.

    Вся работа с данными построена на следующей компонентной схеме:

  • основной компонент Store – с ним и работают другие объекты, желающие получить или отправить данные;
  • Reader отвечает за структуру данных и их верную интерпретацию, вне зависимости от формата;
  • Proxy обеспечивает прозрачный доступ к серверу через промежуточную абстракцию Connection так, что приложению не важно, да и не надо даже знать, как в реальности данные пришли от сервера;
  • объект (или набор объектов) Record хранит коллекцию записей из данных (согласно заданной структуре).
  • Исходя из нужного функционала, можно использовать компоненты, которые работают с JSON -данными или XML, или и те и другие вместе.

    13.2.5. Визуальные компоненты

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

    Управление общей компоновкой обеспечивают компоненты группы Layout, которые позволяют разметить блоками всю область окна приложения и в дальнейшем работать с ними по отдельности, обеспечивая в то же время общую целостность интерфейса и его структуры (рис. 13.1). Отдельные части страницы можно сворачивать, скрывая полностью или менять их размер, передвигать границы областей – все, что привыкли делать пользователи в обычных приложениях.

    (рис 13.1) Управление общей компоновкой

    Динамическая подсказка или Tooltip – позволяет для любого элемента на странице (как визуального, так и к любому DOM-объекту) прицепить подсказку, которая будет показана при наведении мыши. Сама подсказка может содержать в себе произвольный HTML-код, изображения и даже быть контекстно-зависимой – подгружаться через AJAX.

    При необходимости можно подключать и использовать множество виджетов – кнопки (разного вида, с изображениями и текстом, кнопки-переключатели, кнопки с выпадающим меню и т.п.), меню (как контекстные, так и тулбары с меню, совсем как в обычных приложениях), индикаторы загрузки и прогресс-бары. Есть и такое, свойственное только Веб-приложениям решение, как Loading Mask, которое позволяет "заморозить" для пользователя все приложение на то время, пока получаются и обновляются данные с сервера. Есть и функциональные компоненты для выбора даты и цвета, есть компоненты для вывода сообщений, как модальные MessageBox, так и полноценные окна, в которые, в свою очередь, можно вставлять любые элементы управления или разделять их закладки, сворачивать и развертывать, перетаскивать по экрану и т.п.

    Таб-панель – еще один мощный компонент для построения тех приложений, которые, с одной стороны, требуют, чтобы пользователю показывалось сразу много разной информации, формы, таблицы и другие объекты, в то же время нужны ограничения, и в каждый момент времени показывать только самое необходимое. Для такого структурирования широко применяют вкладочный интерфейс. Даже диалоговые окна в ExtJS имеют свойства для автоматического преобразования набора правильно оформленных DIV-слоев сразу в набор панелей.

    Tree – часто используемый компонент, позволяющий группировать иерархические структуры и отображать их с любой глубиной вложенности, автоматически подгружая новые элементы, менять местами, перетаскивая мышью отдельные элементы и другие стандартные операции (рис. 13.2). В последних версиях деревья были расширенны путем добавления функций редактирования элементов прямо на месте отрисовки (Inline item editing).

    (рис 13.2) Tree

    Форма является одной из основных высокоуровневых абстракций библиотеки, и использует почти все из описанных компонентов, в частности большинство визуальных виджетов, которые расширяют стандартные элементы форм. Она также использует слой доступа к данным для заполнения некоторых компонентов (например, ComboBox может получать данные от сервера). В функциональность форм входит и предварительная проверка вводимых пользователем значений, что уже стало классической функциональностью для Веб. Используя встроенные обработчики, можно проверять вводимые e-mail адреса, URL и просто строки символов, однако никто не мешает расширять встроенные наборы, создавая пользовательские валидаторы (любой виджет для ввода данных позволяет определить произвольную функцию для проверки ввода). Есть и встроенный HTML-редактор. Форма в ExtJS сама умеет передавать себя на сервер в виде XML-документа и обрабатывать ответ, в том числе и валидацию полей на сервере – для этого есть отдельный компонент, стандартно же и спользуется JSON и обычная передача через GET/POST параметры.

    Таблица Grid – самый востребованный и самый мощный из компонентов, которым располагает разработчик на ExtJS (рис. 13.3). Впрочем, он одновременно является и самым сложным. В общих чертах, он также построен на модели абстракции каждого уровня: данные могут получаться из DataStore, внешнее отображение задается как специальными функциями-рендерами для каждого столбца отдельно, так и компонентом GridView, который отвечает, например, за заголовок таблицы и создание тулбара с элементами постраничного управления выводом данных. Для выделения строк и ячеек есть, соответственно, RowSelectinsModel и CellSelectionModel. Также поддерживается особый тип таблиц со встроенной функцией редактирования данные напрямую в ячейке (это позволяет расширить функционал такой таблицы почти до уровня Excel/Calc). В версии ExtJS 2.0 появилась возможность избирательно группировать строки, совмещая, таким образом, функционал ьность таблицы и дерева (Grid и Tree). Конечно, в таблице пол ностью поддерживается и Drag'n'Drop – можно визуально менять расположение столбцов, перетаскивать отдельные строки, менять их ширину, сортировать данные в колонках и другие операции.

    (рис 13.3) AJAX Таблица

    Развитие библиотеки не стоит на месте. Например, вместе с выходом среды Adobe AIR библиотека пополнилась отдельным интерфейсом, позволяющим легко взаимодействовать с этой средой и встраивать ее возможности в приложение. Таким образом, совместно с AIR и ExtJS можно реально создавать приложения, которые, оставаясь веб-ориентированными, вместе с этим просто идентичны традиционным "десктопным", совмещая все преимущества и сильные стороны обоих типов программ.

    13.3. Библиотека Prototype

    PrototypeJavaScript -фреймворк, упрощающий работу с Ajax и некоторыми другими функциями [7]. Несмотря на его доступность в виде отдельной библиотеки, он обычно используется программистами вместе с Ruby on Rails, script.aculo.us и Rico.

    Заявлено, что данный фреймворк поддерживается следующими браузерами: Internet Explorer 6.0+, Mozilla Firefox 1.5+, Apple Safari 2.0+ и Opera 9.25+.

    13.3.1. Возможности

    В Prototype присутствуют самые разные способы упрощения создания JavaScript -приложений, от сокращенного вызова некоторых функций языка до сложных методов обращения к XMLHttpRequest. Ниже приведены некоторые примеры.

    13.3.1.1. Функция $()

    Для обращения к DOM элементу HTML-страницы обычно используется функция:

    document.getElementById:
    document.getElementById("id_of_element")

    Функция $() уменьшает код до [8]:

    $("id_of_element")

    Но в отличие от функции DOM, функции $() можно передавать более одного аргумента и функция вернет массив ( Array ) объектов со всеми соответствующими элементами:

    var ar = $('id_1', 'id_2', 'id_3');
    for (i = 0; i < ar.length; i++) {
        alert(ar[i].innerHTML);
    }

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

    $("id_of_element").style.color = "#ffffff";

    Или, используя расширенные функции Prototype:

    $("id_of_element").setStyle({ color: '#ffffff' });

    Код для версий ниже 1.5:

    Element.setStyle("id_of_element", { color: "#ffffff" });

    13.3.1.2. Функция $$()

    Функция $$() используется для разделения CSS от контента. Она разбивает один или несколько CSS фильтров, которые поступают на вход в виде выражения подобного регулярному выражению и возвращает элементы, которые соответствуют этим фильтрам. Например, при выполнении данного скрипта:

    var f = $$('div#block .inp');

    получим массив, содержащий все элементы с классом .inp, которые находятся в контейнере div с идентификатором id="block".

    13.3.1.3. Функция $F()

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

    $F("id_of_input_element")

    13.3.1.4. Функция $A()

    Функция $A() преобразует один аргумент, который она получает, в объект Array. Эта функция, в сочетании с расширениями для класса Array, облегчает конвертирование или копирование любых перечислимых списков в объект Array. Один из вариантов использования заключается в том, чтобы преобразовать DOM NodeLists в регулярные массивы, которые могут быть более эффективно использованы.

    13.3.1.5. Функция $H()

    Функция $H() преобразовывает объекты в перечислимые Хэш-объекты, которые похожи на ассоциативный массив:

    //Допустим имеем объект:
    var hash = { method: post, type: 2, flag: t };
    //При использовании функции:
    var h = $H(hash);
    //Получим:
    alert(h.toQueryString());
    //На экране будет отображено: posttype=2flag=t

    13.3.1.6. Объект Ajax

    Объект Ajax предоставляет простые методы инициализации и работы с функцией XMLHttpRequest, без необходимости подстраивать код под нужный браузер. Существует два способа вызова объекта: Ajax.Request возвращает XML вывод AJAX -запроса, в то время как Ajax.Updater помещает ответ сервера в выбранную ветвь DOM.

    Ajax.Request в примере ниже находит значения двух полей ввода, запрашивает страницу с сервера, используя значения в виде POST -запроса, а после завершения выполняет пользовательскую функцию showResponse():

    var val1 = escape($F("name_of_id_1"));
    var val2 = escape($F("name_of_id_2"));
    var url = "http://yourserver/path/server_script";
    var pars = { value1: val1, value2: val2 };
    
    var myAjax = new Ajax.Request(
      url,
      {
          method: "post",
          parameters: pars,
          onComplete: showResponse
      });

    13.3.1.7. Класс Element

    Методы класса предназначены для работы с элементами HTML. Для создания HTML элемента используется конструктор класса:

    new Element(tagName[,{attributes}])

    В конструктор передается HTML-тег в строковом виде и, если необходимо, атрибуты тега.

    Пример создания нового элемента:

    // Создаем элемент <div> в памяти и указываем атрибуты id, class
    var newElement = new Element('div',{id: 'childDiv', class: 'divStyle'});
    // Включаем созданный элемент в DOM браузера, а именно в существующий <div> с помощью метода Element.insert
    Element.insert($('parrentDiv'),newElement);

    13.3.2. Объектно-ориентированное программирование

    Prototype также добавляет поддержку более традиционного объектно-ориентированного программирования.

    Для создания нового класса используется метод Class.create(). Классу присваивается прототип prototype, который выступает в качестве основы для каждого экземпляра класса. Старые классы могут быть расширены новыми с помощью Object.extend:

    // создания нового класса в стиле prototype 1.6
    var FirstClass = Class.create({
        // Инициализация конструктора
        initialize: function() {
            this.data = "Hello World";
        }
    });
    
    // создания нового класса в стиле prototype 1.5
    var DataWriter = Class.create();
    DataWriter.prototype = {
        printData: function() {
            document.write(this.data);
        }
    };
    
    Object.extend(DataWriter, FirstClass);

    13.4. Библиотека jQuery

    jQuery – библиотека JavaScript, фокусирующаяся на взаимодействии JavaScript и HTML [9, 10]. Библиотека jQuery помогает легко получать доступ к любому элементу DOM, обращаться к атрибутам и содержимому элементов DOM, манипулировать ими. Также библиотека jQuery предоставляет удобный API по работе с Ajax.

    Возможности:

  • переход по дереву DOM, включая поддержку XPath как плагина;
  • события;
  • визуальные эффекты;
  • AJAX -дополнения;
  • JavaScript -плагины.
  • 13.4.1. Философия

    Точно так же, как CSS отделяет визуализацию от структуры HTML, JQuery отделяет поведение от структуры HTML. Например, вместо прямого указания на обработчик события нажатия кнопки, управление передается JQuery, идентифицирующей кнопки и затем преобразовывающий его в обработчик события клика. Такое разделение поведения и структуры также называется принципом ненавязчивого JavaScript.

    Библиотека jQuery содержит функционал, полезный для максимально широкого круга задач. Тем не менее, разработчиками библиотеки не ставилась задача совмещения в jQuery функций, которые подошли бы всюду, поскольку это привело бы к большому коду, большая часть которого не востребована. Поэтому была реализована архитектура компактного универсального ядра библиотеки и плагинов. Это позволяет собрать для ресурса именно тот JavaScript -функционал, который на нем был бы востребован.

    13.4.2. Использование

    jQuery, как правило, включается в веб-страницу как один внешний JavaScript -файл:

    <head>
        <script type="text/javascript" src="путь/к/jQuery.js"></script>
    </head>

    Вся работа с jQuery ведется с помощью функции $. Если на сайте применяются другие JavaScript библиотеки, где $ может использоваться для своих нужд, то можно использовать ее синоним – jQuery. Второй способ считается более правильным, а чтобы код не получался слишком громоздким можно писать его следующим образом:

    jQuery(function($) {
        // Тут код скрипта, где в $ будет jQuery
    })

    Работу с jQuery можно разделить на 2 типа:

  • Получение jQuery -объекта с помощью функции $(). Например, передав в нее CSS-селектор, можно получить jQuery -объект всех элементов HTML попадающих под критерий и далее работать с ними с помощью различных методов jQuery -объекта.
  • Вызов глобальных методов у объекта $, например, удобных итераторов по массиву.
  • Типичный пример манипуляции сразу несколькими узлами DOM заключается в вызове $ функции со строкой селектора CSS, что возвращает объект jQuery, содержащий некоторое количество элементов HTML-страницы. Эти элементы затем обрабатываются методами jQuery. Например:

    $("div.test").add("p.quote").addClass("blue").slideDown("slow");

    находит все элементы <div> с классом test, а также все элементы <p> с классом quote, и затем добавляет им всем класс blue и визуально плавно спускает вниз.

    Методы, начинающиеся с $., удобно применять для обработки глобальных объектов. Например:

    $.each([1, 2, 3], function() {
        document.write(this + 1);
    });

    добавит на страницу 234.

    Оператор $.ajax и соответствующие функции позволяют использовать методы AJAX. Например:

    $.ajax({
        type: "POST",
        url: "some.html",
        data: { name: 'John', location: 'Boston' },
        success: function(msg) {
            alert("Data Saved: " + msg);
        }
    });

    В этом примере идет обращение к скрипту some. HTML с параметрами name=Johnlocation=Boston и полученный результат выдается в сообщении посредством alert().

    Пример добавления к элементу обработчика события click с помощью jQuery:

    jQuery(function($) {
        $("a").click(function() {
            alert("Привет, Мир!");
        });
    });

    В данном случае при нажатии на элемент <A> происходит вызов alert("Привет, Мир!").

    Рассмотрим пример, в котором необходимо раскрасить таблицу в следующем стиле [11, 12]:

    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре

    Предполагается, что есть CSS-файл, в котором определен класс "odd". Все, что нужно сделать, – это наделить нечетные строки в таблице этиv классом.

    Для начала – решение на чистом JavaScript:

    var tables = document.getElementsByTagName("table");
    for (var t = 0; t < tables.length; t++) {
        var rows = tables[t].getElementsByTagName("tr");
        for (var i = 1; i < rows.length; i += 2)
            if (!/(^|s)odd(s|$)/.test(rows[i].className))
            rows[i].className += " odd";
    }

    Теперь – решение, использующее одну из самых популярных на сегодняшний день библиотек, Prototype:

    $$("table").each(function(table) {
        Selector.findChildElements(table, ["tr"])
        .findAll(function(row, i) { return i % 2 == 1; })
        .invoke("addClassName", "odd");
    });

    Ну а теперь – jQuery:

    $("tr:nth-child(odd)").addClass("odd");

    Как видно из этого примера, библиотека jQuery позволяет находить простые и изящные решения для, казалось бы, сложных задач.

    13.4.3. Функция $()

    Ключом к пониманию работы jQuery является функция $(). Эта функция, так или иначе, вызывается всеми методами jQuery.

    Определение функции $ можно увидеть в следующем примере:

    $(html)
    $(elems)
    $(fn)
    $(expr, context)

    Вне зависимости от параметров, переданных в функцию, знак доллара вернет список объектов, над которым уже определены все доступные jQuery -функции. Это позволяет работать с любыми объектами – уже существующими на странице, созданными динамически или полученными через AJAX – так, как будто это одни и те же элементы, уже существующие на странице.

    Рассмотрим каждый объект подробнее.

    13.4.3.1. $(html)

    Позволяет создать html-элементы "на лету" из "чистого" HTML. Например, можно создать элемент div, содержащий параграф с текстом "Привет!" и добавить его к элементу с id="body" таким образом:

    var my_div = $("<div><p>Привет, Мир!</p></div>");
    my_div.appendTo("#body");

    Или еще короче:

    $("<div><p>Привет, мир!</p></div>").appendTo("#body");

    Элемент до отработки скрипта:

    <div id="body"></div>

    Элемент после отработки скрипта:

    <div id="body"><div><p>Привет, Мир!</p></div></div>

    13.4.3.2. $(elems)

    Позволяет "прицепить" всю функциональность jQuery к уже существующим элементам страницы (а именно, к элементам из объектной модели документа, из DOM). Примеры:

    $(document.body).css("background-color", "black");
    $(myForm.elements).hide();

    13.4.3.3. $(expr[, context])

    Это наиболее часто используемая форма функции $. Первый, обязательный, параметр – это выражение, которое позволит jQuery найти элемент на странице. Второй, необязательный, параметр указывает, где искать этот элемент (по умолчанию jQuery будет искать по всей странице).

    Пример поиска всех элементов p, находящихся внутри всех элементов div на странице:

    $("div > p");

    В документе:

    <p>один</p> <div><p>два</p></div> <p>три</p>

    Результат:

    [ <p>два</p> ]

    Пример поиска всех радиокнопок в первой форме на странице:

    $("input:radio", document.forms[0]);

    Пример поиска всех элементов div в XML, что прислан с сервера с помощью технологии AJAX:

    $("div", xml.responseXML);

    Рассмотрим, принцип работы. Дело в том, что в jQuery реализован механизм поиска элементов, использующий CSS и Xpath. То есть, для нахождения требуемого элемента можно воспользоваться как механизмом селекторов CSS, так и запросами по документу в стиле Xpath. CSS в версиях 1-3 предлагает разработчику богатые возможности оформления страниц (как минимум, теоретически). Например, чтобы пометить элементы div в параграфах с классом "highlight" красным цветом, разработчик может составить такую конструкцию:

    p.highlight > div 
    {
      color: #F00;
    }

    Если обратиться к рассмотренному ранее примеру с раскраской таблицы, то согласно CSS 3 раскрасить таблицу можно следующим образом:

    table tr:nth-child(odd)
    {
      background-color: magenta;
    }

    С помощью jQuery таблица была раскрашена следующим образом:

    $("tr:nth-child(odd)").addClass("odd");

    Практически все селекторы из CSS 1-3 можно использовать для нахождения элементов на странице.

    Рассмотрим примеры.

    Поиск элемента с id="unique":

    $("#unique"); // аналог из CSS: #unique {...}

    Поиск всех элементов A, имеющие класс "external" (то есть все <a class="external"> ):

    $("a.external"); // аналог из CSS: a.external {...}

    Поиск всех нечетных элементов div, находящиеся внутри элементов div с классом news-item, который находится внутри элемента div с id="outer"

    $("#outer > div.news-item > p:nth-child(odd)");

    Как видно, фантазия разработчика ограничена только возможностями CSS. Когда же возможностей CSS не хватает, на помощь приходит XPath.

    XPath (XML Path Language) является языком для обращения к частям XML-документа, а соответственно с помощью него можно обращаться к частям HTML-документа. Из этого следует, что тоже можно сделать и с помощью jQuery, так как эта библиотека поддерживает довольно большое подмножество XPath, объединяя его с некоторыми селекторами CSS для создания гибкого механизма поиска элементов на странице.

    Поиск всех элементов P, которые содержат ссылку:

    $("p[a]");

    Поиск всех элементов A, которые содержат атрибут rel, равный "nofollow":

    $("//a[@rel='nofollow']");

    Поиск всех видимых элементов LI внутри элемента UL:

    $("ul/li:visible");   // XPath + селектор
    $("ul > li:visible"); // CSS + селектор

    Как видно, если известно, какой элемент необходимо выбрать, jQuery позволит это сделать.

    13.4.4. Инициализация

    Большинство библиотек JavaScript часто использует событие window.onload. Это событие вызывается после того, как документ полностью загрузился в окно браузера. Это значит, что вызываемый в обработчике этого события код не сработает, пока не загрузятся все изображения, баннеры и видеоролики на странице.

    jQuery предлагает решение этой проблемы. У разработчика есть возможность выполнить свой код в тот момент, когда доступна объектная модель документа, то есть когда браузер уже получил исходный код страницы полностью, но, возможно, еще не подгрузил различное мультимедийное содержимое (рисунки, видео, Flash).

    Возможность выполнить такой код предлагается методом .ready():

    $(document).ready(
      function() { /* ... */ }
    );

    или его сокращенным вариантом, $(fn):

    $(
      function() { /* ... */ }
    );

    В качестве аргумента в .ready() передается функция, которая будет выполнять все необходимые действия.

    $(document).ready(
      function() {
          // добавим ко всем ссылкам на странице некий текст
          $("a").append("<span>(открыть в новом окне)</span>");
      }
    );
    $(document).ready(init);
    function init() {
        // находим все элементы Р с классом highlight и задаем им другой фон
        $("p.highlight").css("background-color", "#000");
        // выполняем любые действия
        alert('Функция init()');
        // вызывать другие функции
        anotherFunc();
        // манипулируем объектами в документе
        document.getElementById("some_id");
    }
    // Используем кратую форму записи
    $(
      function() {
          alert('Документ готов!');
      }
    )

    13.4.5. Управление элементами документов и их свойствами

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

    Методы для манипуляции элементами документа в jQuery
    Метод Описание
    append(content)/prepend(content) Добавить переданный элемент или выражение в конец/в начало выбранного элемента.
    appendTo(expr)/prependTo(expr) Добавить выбранный элемент в конец/в начало переданного элемента.
    attr(name) Получить значение атрибута.
    attr(params) Установить значение атрибутов. Атрибуты передаются в виде {ключ1:значение1[, ключ2:значение2[, …]]}
    attr(name, value) Установить значение одного атрибута.
    css(name)/css(params)/css(name, value) Получить/установить значение отдельных параметров CSS. Аналогично функции attr().
    text()/text(val) Получить/задать текст элемента. В случае ввода текста специальные символы HTML заменяются символьными примитивами ( entities, например, знак ">" будет заменен gt; ).
    empty() Удалить все подэлементы текущего элемента.

    В следующих примерах работа происходит со следующим HTML-кодом:

    <div id="my-div">
      <a href="http://example.com/" id="my-link">Ссылка</a>
    </div>

    Пример 1:

    $("#my-div").html();    //Вернет <a href="#" id="my-link">Ссылка</a>

    Пример 2:

    $("#my-link").attr("href");  // Вернет http://example.com/

    Пример 3:

    $("#my-div").append("<strong>Полужирный текст</strong>");
    // или
    $("<strong>Полужирный текст</strong>").appendTo($("#my-div"));

    HTML станет таким:

    <div id="my-div">
      <a href="http://example.com/" id="my-link">Ссылка</a>
      <strong>Полужирный текст</strong>
    </div>

    Пример 4:

    $("#my-div").empty();

    HTML станет таким:

    <div id="#my-div"></div>

    Пример 5:

    $("#my-div").text("<a>Привет!</a>");

    HTML станет таким:

    <div id="#my-div">lt;agt;Привет!lt;/agt;</div>

    Пример 6:

    $("#my-div").css(
      {
          backgroundColor: "#F00",
          color: "#00F"
      }
    );

    HTML станет таким:

    <div id="my-div" style="background-color: #F00; color: #00F">
      lt;agt;Привет!lt;/agt;
    </div>

    В последнем примере стоит обратить внимание на то, что для сложносоставных свойств CSS вроде background-color, font-weight и прочих используются их эквиваленты из Javascript ( backgroundColor, fontWeight и т.п.).

    13.4.5.1. Последовательный вызов функций

    Главная особенность большинства методов jQuery – это возможность связывать их в цепочки. Методы, манипулирующие элементами документа, обычно возвращают эти объекты для дальнейшего использования, что позволяет писать примерно следующее:

    // Заполняем скрытый элемент <div id="divEdit"></div> текстом и показываем его
    $("#divEdit").html(response).show();
    
    // найти <select id="my_select">...</select>
    var sel = $("#my_select");
    
    // добавляем к нему <option value="1">Пример опции</option>
    $("<option></option>")  // создаем требуемый элемент
      .attr("value", 1)     // устанавливаем значение одного из его атрибутов
      .html("Пример опции") // записываем в него текст
      .appendTo(sel);       // прикрепляем к уже существующему элементу

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

    13.4.6. Работа с индивидуальными элементами

    Функция $() возвращает коллекцию элементов. Так, $("p") вернет массив всех элементов <p>, какие только могут присутствовать на странице (или пустой массив, если таких элементов не существует). Одним из условий работы большинства функций jQuery является непременная обработка всех элементов в возвращаемой коллекции.

    Например:

  • 1. Цвет всех ссылок на странице станет зеленым:
    $("a").css("color", "green");
  • 2. Ко всем элементам div с классом test будет добавлен новый элемент:
    $("div.test").append("<span>Тестовый элемент</span>");
  • Но иногда необходимо обратиться к индивидуальным элементам возвращаемой коллекции.

    13.4.6.1. Индексы и get([index])

    Возвращаемая функцией $() коллекция является "псевдо-массивом" DOM-элементов. Поэтому можно обратиться к индивидуальным элементам по их индексу:

    $("p")[0].className = "test";

    Иногда, правда, необходимо обратиться непосредственно к массиву DOM-объектов, скрытых внутри "псевдо-массива", которым является объект jQuery. Для этого используется функция get():

    $("p");       // Получили объект jQuery
    $("p").get(); // Получили соответствующую коллекцию DOM-элементов

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

    $("a").get(0); // Получили первую из всех ссылок на странице

    13.4.6.2. each(fn)

    Иногда нужно пробежаться по всем элементам коллекции и выполнить над ними какие-то действия. Для этого понадобится функция each. Эта функция принимает в качестве аргумента другую функцию. each() работает в контексте найденных элементов и поэтому каждый раз, когда выполняется переданная ей функция, в этой функции доступна переменная this, указывающая на соответствующий DOM-элемент:

    $("p").each(
      function() {
          // внутри функции this указывает на текущий DOM-элемент
          alert(this.innerHTML);
      }
    );

    В этом примере находятся все элементы p на странице, и для каждого из этих элементов вызывается функция, которая показывает его содержимое.

    Функция, передаваемая в метод each(), может принимать на вход один аргумент, который является индексом текущего элемента в коллекции.

    Для иллюстрации перепишем пример с раскраской нечетных строк таблицы с помощью each(). Если есть некая таблица с id="zebra" и CSS-класс "odd" для нечетных строк, то можно написать так:

    $("#zebra tr").each(colorize); // передаем в each функцию colorize
    function colorize(index) {
        if (index % 2 == 1)       // index является индексом текущего элемента в коллекции
            $(this).addClass("odd");
    }

    13.4.7. Анимации

    Грамотная манипуляция свойствами элементов на странице позволила создателям Javascript -библиотек создавать визуальные эффекты, которые раньше были возможны только при использовании технологии Flash. Это и плавное появление, и скрытие объектов, плавное изменение различных свойств этих объектов (фонового цвета, размеров), реализация всевозможных элементов интерфейса вроде сплиттеров, деревьев, перетягиваемых объектов и сортируемых списков.

    В базовой поставке jQuery предлагает лишь ограниченный набор таких эффектов. Остальные эффекты реализуются при помощи модулей расширения (о которых речь пойдет ниже).

    13.4.7.1. Animate

    Ключевой функцией, на которой базируются все остальные, является функция animate:

    animate(params, speed, easing, callback);

    Здесь:

  • params – свойства, которые участвуют в анимации в виде пар {ключ: значение}, например: { height: "show"} или {opacity: 50, width: 100, height: 200} ;
  • speed – скорость в миллисекундах;
  • easing – замедление анимации (замедляется ли к концу, "easein", или, наоборот, ускоряется, "easeout" );
  • callback – функция, которая будет вызвана после завершения анимации.
  • Пусть, например, есть элемент div с каким-нибудь текстом, и необходимо скрыть этот текст, заменить новым и показать обновленный текст:

    $("#mydiv")
      .animate({ height: "hide" }, 300)
      .text("Новый текст")
    .animate({ height: "show" }, 300);

    13.4.7.2. Эффекты

    Метод animate является основой большинства эффектов jQuery и плагинов. Например, jQuery предлагает следующие методы для показа и скрытия элементов:

  • show([speed[, callback]]) – показать элемент;
  • hide([speed[, callback]]) – скрыть элемент;
  • fadeIn(speed[, callback]) – показать элемент путем изменения его прозрачности;
  • fadeOut(speed[, callback]) – скрыть элемент путем изменения его прозрачности;
  • slideDown(speed, callback) – показать элемент, спустив его сверху;
  • slideUp(speed, callback) – показать элемент, подняв его снизу;

    где

  • speed – скорость в миллисекундах или одно из "slow" (600 миллисекунд) или "fast" (200 миллисекунд);
  • callback – функция, которая будет вызвана после выполнения анимации.
  • Скрытая мощь функции animate позволяет создавать поистине удивительные эффекты, многие из которых уже реализованы, например, в модуле расширения Interface (http://interface.eyecon.ro/).

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

    for (i = 0; i < 10; i++) {
        $("#my-div").animate({ height: "show" }, 300);
        $("#my-div").animate({ height: "hide" }, 300);
    }

    В результате будет получено не беспорядочное моргание, пока двадцать эффектов "борются" за право показать/скрыть элемент "my-div", а плавный показ, потом сокрытие, потом снова показ – и так 20 раз. Стоит обратить внимание на то, что очередь эффектов составляется поэлементно. То есть эффекты, примененные к разным элементам одновременно, будут выполняться одновременно.

    13.4.8. AJAX

    Базовыми функциями для работы с AJAX являются post() и get() (есть еще более низкоуровневая, ajax() ):

    $.post(url[, params[, callback]])
    $.get(url[, params[, callback]])

    Здесь:

  • url – адрес страницы, на которую будет отправлен запрос;
  • params – параметры, передаваемые в запросе в виде пар "ключ : значение";
  • callback – функция, которая будет вызвана в случае успешного завершения вызова.
  • Например:

    $.post(
      '/ajaxtest.php',
      {
          type: "test-request",
          param1: "param1",
          param2: 2
      },
      onAjaxSuccess
    );
    function onAjaxSuccess(data) {
        // Здесь получаем данные, отправленные сервером
        alert(data);
    }

    13.5. Модель работы веб-приложений Comet

    Технология Comet – модель работы Веб-приложения, при которой постоянное HTTP-соединение позволяет Веб-серверу отправлять ( push ) данные браузеру без дополнительного запроса со стороны браузера [13, 14, 15, 16]. Название " Comet " используется для обозначения множества техник, позволяющих достичь такого взаимодействия. Общее у этих методов то, что они основаны на технологиях, непосредственно поддерживаемых браузером, таких как JavaScript, а не на плагинах. Теоретически подход Comet отличается от изначальной концепции Всемирной паутины, при которой браузер запрашивает страницу полностью или частично для того, чтобы обновить страницу. Однако на практике приложения Comet обычно используют Ajax с "long polling" для проверки наличия новой информации на сервере.

    13.5.1. HTTP server push

    HTTP server push (так же называемое HTTP streaming) – механизм отправки данных с Веб-сервера к Веб-браузеру (рис. 13.4) [15]. HTTP server push может быть реализовано посредством нескольких механизмов. Основной – это когда Веб-сервер не закрывает соединение после отправки ответа клиенту. Веб-сервер оставляет соединение открытым, так что если произойдет событие, то оно может быть отправлено одному или множеству клиентов. Иначе данные составляются в очередь до следующего запроса клиента. Большинство серверов реализуют эту функциональность через CGI (например, NPH скрипты в Apache).

    (рис 13.4) Схема работы Comet – HTTP-streaming

    Другой механизм относится к специальному MIME type, называемый multipart/x-mixed-replace, который был введен Netscape в 1995. Веб-браузер интерпретирует это как изменения документа всякий раз, когда сервер сообщает о новой версии клиенту. Сегодня это все еще поддерживается Firefox, Opera и Safari, но традиционно игнорируется Internet Explorer. Это применимо к HTML-документам, но также для потокового отображения картинок для приложений работающих с Веб-камерой.

    WHATWG Web Applications 1.0 предлагает включить механизм "проталкивания" содержимого клиенту. 1 сентября 2006 браузер Opera реализовала эту новую экспериментальную технологию в функциональности с названием "Server-Sent Events". Сейчас это часть стандартов HTML5. Другой, относящейся к вопросу, частью HTML5 стали Веб-сокеты, которые доступны в Google Chrome, начиная с версии 4.0.249.0, и существуют так же JavaScript библиотеки, эмулирующие работу Веб-сокетов.

    В последнее время часто можно услышать выражение real-time web, за которым, как говорят, будущее Интернета. По сути, это набор технологий, который позволяет получать в реальном времени данные добавляемые в Сеть. Частным случаем этой идеи является real-time search – поиск в реальном времени.

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

    13.5.2. Pooling

    Один из способов узнать, нет ли на сервере каких либо обновлений, применять технологию AJAX, с определенным интервалом времени делать запрос на сервер (рис. 13.5) [15].

    (рис 13.5) Пример асинхронных запросов в AJAX – short polling

    Такой тип взаимодействия называется polling (от англ. poll – тянуть), именно вытягиванием данных и занимается браузер. Преимущества перед полным обновлением страницы очевидны, но есть и недостатки. Главным из них является "холостая работа" – часто браузер делает сотни запросов, а в ответ узнает, что новых данных нет. Именно для решения этой проблемы и появилась технология long polling, позволяющая делать запросы, которые возвращают результат, как только он появляется. Пример взаимодействия показан на рис. 13.6 [15].

    (рис 13.6) Пример долгих асинхронных запросов – long polling

    Эта техника является золотой серединой между простым AJAX и сложным HTTP-streaming. Главным преимуществом является то, что клиент создает всего одно соединение, через которое получает данные от сервера в реальном времени. Главными недостатками можно назвать сложность реализации и несоответствие духу протокола HTTP.

    13.6. Ключевые термины

    JSON, JavaScript, AJAX, ExtJS, Prototype, jQuery, Comet.

    13.7. Краткие итоги

    JSON – текстовый формат обмена данными, основанный на JavaScript и обычно используемый именно с этим языком.

    Практическая польза использования JSON открывается при использовании технологии AJAX.

    Хотя JSON предназначен для передачи данных в сериализованном виде, его синтаксис соответствует синтаксису JavaScript и это создает ряд проблем безопасности:

  • JavaScript eval() ;
  • подделка кроссдоменного запроса.
  • ExtJS – библиотека JavaScript для разработки веб-приложений и пользовательских интерфейсов, изначально задуманная как расширенная версия Yahoo! UI Library, однако, преобразовавшаяся затем в отдельный фреймворк.

    ExtJS поддерживает свойства объектно-ориентированного программирования посредством следующих методов:

  • Ext.namespace ;
  • Ext.override ;
  • Ext.extend и соглашения о параметрах конструкторов;
  • Ext.apply ;
  • Ext.applyIf.
  • Компоненты библиотеки ExtJS можно разделить на несколько групп:

  • Самый нижний слой;
  • Core:
  • ядро;
  • модуль для использования визуальных компонентов;
  • утилиты;
  • поддержка кросс-браузерного DragDrop;
  • возможность хранить состояние интерфейса в независимом хранилище;
  • Работа с данными:
  • Store ;
  • Reader ;
  • Proxy ;
  • объект Record.
  • ExtJS предлагает следующие средства для визуализации:

  • Layout;
  • Tooltip ;
  • Таб-панель;
  • Tree ;
  • Форма;
  • Grid.
  • В Prototype присутствуют самые разные способы упрощения создания JavaScript -приложений, от сокращенного вызова некоторых функций языка до сложных методов обращения к XMLHttpRequest:

  • $() ;
  • $$() ;
  • $F() ;
  • $A() ;
  • $H() ;
  • объект Ajax ;
  • Element.
  • Prototype добавляет поддержку объектно-ориентированного программирования.

    jQuery – библиотека JavaScript, фокусирующаяся на взаимодействии JavaScript и HTML.

    Возможности jQuery:

  • переход по дереву DOM, включая поддержку XPath как плагина;
  • события;
  • визуальные эффекты;
  • AJAX -дополнения;
  • JavaScript -плагины.
  • Вся работа с jQuery ведется с помощью функции $.

    jQuery предлагает разработчику большое количество методов для манипуляции элементами документа и их свойствами.

    Главная особенность большинства методов jQuery – это возможность связывать их в цепочки.

    Технология Comet – модель работы Веб-приложения, при которой постоянное HTTP-соединение позволяет Веб-серверу отправлять (push) данные браузеру без дополнительного запроса со стороны браузера.

    HTTP server push – механизм отправки данных с Веб-сервера к Веб-браузеру

    Один из способов узнать, нет ли на сервере каких либо обновлений, применять технологию AJAX, с определенным интервалом времени делать запрос на сервер (short polling).

    Технология long polling, позволяет делать запросы, которые возвращают результат, как только он появляется.

    Страницы:

    Презентацию к данной лекции Вы можете скачать здесь.

    13.1. Представление объектов в виде JSON

    JSON ( JavaScript Object Notation) – текстовый формат обмена данными, основанный на JavaScript и обычно используемый именно с этим языком [1]. Как и многие другие текстовые форматы, JSON легко читается людьми.

    Несмотря на происхождение от JavaScript, формат считается независимым от языка и может использоваться практически с любым языком программирования. Для многих языков существует готовый код для создания и обработки данных в формате JSON.

    13.1.1. Использование

    Практическая польза использования JSON открывается при использовании технологии AJAX. Формат JSON является более кратким и удобочитаемым по сравнению с XML, является "родным" для JavaScript (преобразуется в структуру данных встроенной функцией eval() ). Кроме того, в JSON -код возможна вставка вполне работоспособных функций.

    13.1.2. Синтаксис

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

    {
       "firstName": "Иван",
       "lastName": "Иванов",
       "address": {
           "streetAddress": "Московское ш., 101, кв.101",
           "city": "Ленинград",
           "postalCode": 101101
       },
       "phoneNumbers": [
           "916 123-4567"
       ]
    }

    На языке XML подобная структура выглядела бы примерно так:

    <person>
      <firstName>Иван</firstName>
      <lastName>Иванов</lastName>
      <address>
        <streetAddress>Московское ш., 101, кв.101</streetAddress>
        <city>Ленинград</city>
        <postalCode>101101</postalCode>
      </address>
      <phoneNumbers>
        <phoneNumber>916 123-4567</phoneNumber>
      </phoneNumbers>
    </person>

    13.1.3. Использование JSON в Ajax

    Следующий фрагмент Javascript -кода показывает, как клиент может использовать XMLHttpRequest, чтобы запрашивать с сервера объект в формате JSON:

    var the_object;
    var http_request = new XMLHttpRequest();
    http_request.open("GET", url, true);
    http_request.send(null);
    http_request.onreadystatechange = function() {
        if (http_request.readyState == 4) {
            if (http_request.status == 200) {
                the_object = eval("(" + http_request.responseText + ")");
            } else {
                alert("Проблема с URL.");
            }
            http_request = null;
        }
    };

    Полезность XMLHttpRequest ограничивается политикой ограничения домена: URL ответа на запрос должен проживать в том же DNS домене, что и сервер, на котором находится страница, содержащая запрос.

    Браузеры могут также использовать элементы <iframe> асинхронный запрос JSON данных в кросс-браузер мода, или использовать простые <form action="url_to_cgi_script" target="name_of_hidden_iframe"> представлений. Эти подходы были распространены до появления широкой поддержки XMLHttpRequest.

    13.1.4. Вопросы безопасности

    Хотя JSON предназначен для передачи данных в сериализованном виде, его синтаксис соответствует синтаксису JavaScript и это создает ряд проблем безопасности. Зачастую для обработки данных, полученных от внешнего источника в формате JSON, к ним применяется функция eval() без какой-либо предварительной проверки.

    13.1.4.1. JavaScript eval()

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

    Техника использования eval() делает систему уязвимой, если источник используемых JSON -данных не относится к доверенным. В качестве таких данных может выступать вредоносный JavaScript код для атак класса "Внедрение кода". С помощью данной уязвимости можно осуществить кражу данных, подделку аутентификации. Тем не менее, уязвимость можно устранить за счет использования дополнительных средств проверки данных на корректность. Например, до выполнения eval() полученные от внешнего источника данные могут проверяться с помощью регулярных выражений. В RFC, определяющей JSON, предлагается использовать следующий код для проверки его соответствия формату JSON:

    var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
    text.replace(/"(\\.|[^"\\])*"/g, '')))  eval('(' + text + ')');

    Как более безопасная альтернатива eval() была предложена новая функция parseJSON(), способная обрабатывать только JSON -данные.

    13.1.4.2. Подделка кроссдоменного запроса

    Непродуманное использование JSON делает сайты уязвимым к подделке межсайтовых запросов (CSRF или XSRF). Поскольку тэг <script> допускает использование источника, не принадлежащего к тому же домену, что и использующий ресурс, это позволяет выполнять код под видом данных, представленных в формате JSON, в контексте произвольной страницы, что делает возможным компрометацию паролей или другой конфиденциальной информации пользователей, прошедших авторизацию на другом сайте.

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

    13.2. Библиотека ExtJS

    ExtJS – библиотека JavaScript для разработки веб-приложений и пользовательских интерфейсов, изначально задуманная как расширенная версия Yahoo! UI Library, однако, преобразовавшаяся затем в отдельный фреймворк. Использует адаптеры для доступа к библиотекам YUI, jQuery или Prototype /script.aculo.us. Поддерживает технологию AJAX, анимацию, работу с DOM, реализацию таблиц, вкладок, обработку событий и все остальные новшества "Веб 2.0" [2, 3].

    С версии 2.1 библиотека ExtJS распространяется по условиям трех лицензий: Commercial License, Open Source License и OEM / Reseller License.

    Начиная с версии Ext JS 3.0, библиотека разбивается на две части: Ext Core (набор JavaScript функций, позволяющий создавать динамические веб-страницы и распространяемый по MIT-лицензии) и Ext JS (набор виджетов для создания пользовательских интерфейсов, распространяемый аналогично Ext JS 2.1 по условиям трех лицензий).

    13.2.1. Объектно-ориентированное программирование

    Одна из задач, которую ставили себе разработчики библиотеки ExtJS при ее проектировании, была возможность сосуществования с другими JavaScript -библиотеками и фреймворками [4, 5]. Для этого были добавлены удобные методы, помогающие разработчику создавать, наследовать и сопровождать код различных классов. Несмотря на то, что все они были специально созданы для работы с компонентами ExtJS, их можно использовать с любыми объектами JavaScript, в том числе в проектах, не задействующих визуальные компоненты.

    13.2.1.1. Ext.namespace

    Синтаксис:

    Ext.namespace( String namespace1, String namespace2, String etc ) : void

    В языке JavaScript пространство имен само является объектом, содержащим список определений других объектов. Правильное использование пространств имен гарантирует, что ваш код не будет конфликтовать с другими библиотеками. Однако чем больше становится кода в библиотеке, тем глубже иерархия имен и такие классы как Ext.ux.graphing.GraphPanel перестают быть редкостью. В ExtJS существует метод Ext.namespace, который позволяет определить все пространства имен за один вызов, уменьшая не только затраты на поддержку кода, но и сокращая его размер.

    Традиционный код JavaScript, создающий иерархию пространства имен, выглядит следующим образом:

    var Ext = Ext || {};
    Ext.ux = Ext.ux || {};
    Ext.ux.graphing = Ext.ux.graphing || {};
    Ext.ux.soundFx = Ext.ux.soundFx || {};
    Ext.ux.maps = Ext.ux.maps || {};

    Того же результата можно достичь вызовом всего одного метода:

    Ext.namespace("Ext.ux.graphing", "Ext.ux.soundFx", "Ext.ux.maps");

    13.2.1.2. Ext.override

    Синтаксис:

    Ext.override( Object origclass, Object overrides ) : void

    JavaScript очень гибкий язык, и переопределение метода в нем выполняется простым переназначением его объекта другой функции:

    // создаем функцию
    function doesStuff() {
      alert("Функция один!");
    };
    // теперь ее перекрываем
    doesStuff = function() {
      alert("Функция два!");
    };

    Перекрытие метода класса осуществляется путем переопределения его прототипа:

    function MyClass() {
      // конструктор класса
    }
    MyClass.prototype.myMethod = function() {
      alert("привет");
    };
    var x = new MyClass();
    x.myMethod(); // отобразит "привет"
    MyClass.prototype.myMethod = function() {
      alert("пока");
    };
    
    x.myMethod(); // отобразит "пока"

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

    Ext.override – это удобный способ переопределения методов класса за один присест. Следующий пример перекрывает два метода класса Ext.ux.graphing.GraphPanel. Следуя общепринятым правилам языка JavaScript, если в оригинальном классе не содержатся определений методов, то они добавляются. Первым параметром идет класс, который будет изменен, а вторым – JSON объект с методами, подлежащими переопределению:

    Ext.override(Ext.ux.graphing.GraphPanel, {
      clearAllPoints: function() {
        // выполнить другие действия
      },
      loadData: function(newData) {
        // загрузка новых данных
      }
    });

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

    13.2.1.3. Ext.extend и соглашения о параметрах конструкторов

    Синтаксис:

    Ext.extend( Function superclass, Object overrides ) : Function

    или

    Ext.extend( Function subclass, Function superclass, [Object overrides] ) : Function

    Рассмотренный ранее метод Ext.override выполняет замену реализации на новый метод целиком, а Ext.extend позволяет осуществить полноценное наследование согласно парадигме ООП. Имеются два варианта вызова этого метода, каждый из которых возвращает определение нового класса, содержащего объекты, унаследованные от родительского, определенного в параметре superclass, и перекрывает методы, заданные в параметре overrides. Также он добавляет к новому классу служебный метод override(), который дает возможность перекрывать методы экземпляра класса, и такое же служебное свойство superclass, позволяющее получить доступ к родительскому объекту.

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

    Ниже приведен пример использования Ext.extend. Обратите внимание, что имеются два способа наследования. Первый:

    var newClass = Ext.extend(Ext.form.ComboBox, {
      constructor: function(config) { // явное задание конструктора
        config.emptyText = "'5' не допускается";
        newClass.superclass.constructor.call(this, config);
      },
      setValue: function(v) {
        if (v == 5) { // если v равно 5, сообщить пользователю
          alert("'5' не допускается");
        } else { // иначе вызываем метод родителя
          newClass.superclass.setValue.call(this, v);
        }
      }
    });

    Второй:

    function newClass(config) { // конструктор
      config.emptyText = "'5' не допускается";
      newClass.superclass.constructor.call(this, config);
    };
    Ext.extend(newClass, Ext.form.ComboBox, {
      setValue: function(v) {
        if (v == 5) { // если v равно 5, сообщить пользователю
          alert("'5' не допускается");
        } else { // иначе вызываем метод родителя
          newClass.superclass.setValue.call(this, v);
        }
      }
    });

    Так как все объекты ExtJS имеют одну и ту же форму указания параметров в конструкторе, это позволяет работать с ними без особых усилий: достаточно скопировать все значения объекта config. ExtJS имеет два встроенных метода для выполнения этих действий, которые далее будут рассмотрены.

    13.2.1.4. Ext.apply

    Синтаксис:

    Ext.apply( Object obj, Object config, [Object defaults] ) : Object

    Метод Ext.apply производит копирование каждого члена объекта config в объект obj. Опционально может быть задан параметр defaults, содержимое которого также копируется в объект obj, но перед выполнением копирования объекта config.

    13.2.1.5. Ext.applyIf

    Синтаксис:

    Ext.applyIf( Object obj, Object config ) : Object

    Метод Ext.applyIf производит копирование каждого члена объекта config в объект obj, но только в том случае, когда этот член еще не существует в объекте назначения.

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

    function MyClass(config) { // конструктор, который принимает конфигурацию в параметре 'config'
      // присвоение некоторых значений по умолчанию, если они не заданы в конфигурации
      config = Ext.applyIf(config, {
        someValue: defaultValue
      });
    
      // а здесь присваиваются значения по умолчанию, игнорирующие параметры конструктора
      config = Ext.apply(config, {
        someOtherValue: defaultValue
      });
    
      // вызов конструктора класса-родителя с передачей новой конфигурации
      MyClass.superclass.constructor.call(this, config);
    }
    
    // не забудьте унаследовать родительский класс
    Ext.extend(MyClass, MyBase, {
      // ...
    });

    13.2.2. Построение веб-интерфейсов

    Библиотека ExtJS написана на JavaScript и работает во всех популярных сейчас браузерах (хотя и с некоторыми различиями), предназначена для создания сложных и насыщенных интерфейсов, которые очень похожи на аналогичные desktop-программы [5, 6]. Она предоставляет разработчику целый набор графических компонентов, от тривиальных кнопок и расширенных элементов обычных HTML-форм, до сложных компонентов вроде таблиц, лейаутов (layout – компоненты, отвечающие за расположение объектов друг относительно друга) и деревьев. В библиотеке также есть достаточно много невидимых пользователю компонентов, которые и обеспечивают работу того, что пользователь видит на экране. Это и получение данных с сервера в фоновом режиме (в формате JSON или XML), обновление частей страницы, локальные хранилища данных, поддержка cookie и многое другое.

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

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

    13.2.3. Использование CSS

    В архитектуре библиотеки есть и такое понятие как тема оформления – это обычный CSS-файл, изменяя который можно мгновенно изменить внешний вид всех элементов интерфейса. Уже создано множество различных пользовательских тем, в разной цветовой гамме и стиле, хотя, на мой взгляд, именно стандартная тема наиболее приятная и привычная для большинства пользователей.

    13.2.4. Основные компоненты и возможности

    Для рассмотрения библиотеки ExtJS можно разделить ее компоненты на несколько групп.

    Самый нижний слой – это низкоуровневые функции по работе с DOM-деревом, событиями, стилями и другие функции. Здесь можно использовать адаптеры для подключения нескольких библиотек. В особенности это нужно, когда необходимо смешать в одном приложении функциональность, к примеру, jQuery и ExtJS. Во последних версиях ExtJS можно использовать как родные функции, так и другие библиотеки: jQuery, Yahoo UI!, Prototype.

    Core – это основные базовые модули и функционал, который необходим для работы всех остальных компонентов библиотеки и приложения. Он разбит на несколько модулей:

  • Ядро – это обязательный компонент, обеспечивает общий менеджер событий, работу с адаптерами и основные операции с DOM-моделью. Для построения самых простых приложений можно использовать только этот компонент, так как в него включены все необходимые функции, в том числе и UpdateManager для обновления любой части страницы по расписанию или событию от пользователя.
  • Для использования визуальных компонентов (виджетов) нужен модуль, который обеспечивает их работу, рендеринг и управление наборами виджетов. ExtJS поддерживает функции отложенного рендеринга (lazy render) когда все операции по отрисовке или обновлению элементов координируются самой библиотекой, а это экономит время и делает приложение быстрее.
  • Утилиты – добавляют функции работы с JSON, обработку CSS, работу с событиями клавиатуры и базовую поддержку шаблонов.
  • Поддержка кросс-браузерного DragDrop вынесена в отдельный модуль (нужен, если используются виджеты).
  • Возможность хранить состояние интерфейса в независимом хранилище, например cookie (компонент State Manager ) – при повторном входе все открытые окна или вкладки в браузере будут восстановлены.
  • Работа с данными. Так как любое веб-приложение основано на каких-то данных, то этот слой в библиотеке не менее насыщенный различным функционалом, чем визуальные компоненты. Обмен данными с сервером может вестись в двух форматах – JSON, как самый простой для JavaScript, и XML, что позволяет реализовать более широкий функционал, но за счет увеличения ресурсоемкости всего приложения.

    Вся работа с данными построена на следующей компонентной схеме:

  • основной компонент Store – с ним и работают другие объекты, желающие получить или отправить данные;
  • Reader отвечает за структуру данных и их верную интерпретацию, вне зависимости от формата;
  • Proxy обеспечивает прозрачный доступ к серверу через промежуточную абстракцию Connection так, что приложению не важно, да и не надо даже знать, как в реальности данные пришли от сервера;
  • объект (или набор объектов) Record хранит коллекцию записей из данных (согласно заданной структуре).
  • Исходя из нужного функционала, можно использовать компоненты, которые работают с JSON -данными или XML, или и те и другие вместе.

    13.2.5. Визуальные компоненты

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

    Управление общей компоновкой обеспечивают компоненты группы Layout, которые позволяют разметить блоками всю область окна приложения и в дальнейшем работать с ними по отдельности, обеспечивая в то же время общую целостность интерфейса и его структуры (рис. 13.1). Отдельные части страницы можно сворачивать, скрывая полностью или менять их размер, передвигать границы областей – все, что привыкли делать пользователи в обычных приложениях.

    (рис 13.1) Управление общей компоновкой

    Динамическая подсказка или Tooltip – позволяет для любого элемента на странице (как визуального, так и к любому DOM-объекту) прицепить подсказку, которая будет показана при наведении мыши. Сама подсказка может содержать в себе произвольный HTML-код, изображения и даже быть контекстно-зависимой – подгружаться через AJAX.

    При необходимости можно подключать и использовать множество виджетов – кнопки (разного вида, с изображениями и текстом, кнопки-переключатели, кнопки с выпадающим меню и т.п.), меню (как контекстные, так и тулбары с меню, совсем как в обычных приложениях), индикаторы загрузки и прогресс-бары. Есть и такое, свойственное только Веб-приложениям решение, как Loading Mask, которое позволяет "заморозить" для пользователя все приложение на то время, пока получаются и обновляются данные с сервера. Есть и функциональные компоненты для выбора даты и цвета, есть компоненты для вывода сообщений, как модальные MessageBox, так и полноценные окна, в которые, в свою очередь, можно вставлять любые элементы управления или разделять их закладки, сворачивать и развертывать, перетаскивать по экрану и т.п.

    Таб-панель – еще один мощный компонент для построения тех приложений, которые, с одной стороны, требуют, чтобы пользователю показывалось сразу много разной информации, формы, таблицы и другие объекты, в то же время нужны ограничения, и в каждый момент времени показывать только самое необходимое. Для такого структурирования широко применяют вкладочный интерфейс. Даже диалоговые окна в ExtJS имеют свойства для автоматического преобразования набора правильно оформленных DIV-слоев сразу в набор панелей.

    Tree – часто используемый компонент, позволяющий группировать иерархические структуры и отображать их с любой глубиной вложенности, автоматически подгружая новые элементы, менять местами, перетаскивая мышью отдельные элементы и другие стандартные операции (рис. 13.2). В последних версиях деревья были расширенны путем добавления функций редактирования элементов прямо на месте отрисовки (Inline item editing).

    (рис 13.2) Tree

    Форма является одной из основных высокоуровневых абстракций библиотеки, и использует почти все из описанных компонентов, в частности большинство визуальных виджетов, которые расширяют стандартные элементы форм. Она также использует слой доступа к данным для заполнения некоторых компонентов (например, ComboBox может получать данные от сервера). В функциональность форм входит и предварительная проверка вводимых пользователем значений, что уже стало классической функциональностью для Веб. Используя встроенные обработчики, можно проверять вводимые e-mail адреса, URL и просто строки символов, однако никто не мешает расширять встроенные наборы, создавая пользовательские валидаторы (любой виджет для ввода данных позволяет определить произвольную функцию для проверки ввода). Есть и встроенный HTML-редактор. Форма в ExtJS сама умеет передавать себя на сервер в виде XML-документа и обрабатывать ответ, в том числе и валидацию полей на сервере – для этого есть отдельный компонент, стандартно же и спользуется JSON и обычная передача через GET/POST параметры.

    Таблица Grid – самый востребованный и самый мощный из компонентов, которым располагает разработчик на ExtJS (рис. 13.3). Впрочем, он одновременно является и самым сложным. В общих чертах, он также построен на модели абстракции каждого уровня: данные могут получаться из DataStore, внешнее отображение задается как специальными функциями-рендерами для каждого столбца отдельно, так и компонентом GridView, который отвечает, например, за заголовок таблицы и создание тулбара с элементами постраничного управления выводом данных. Для выделения строк и ячеек есть, соответственно, RowSelectinsModel и CellSelectionModel. Также поддерживается особый тип таблиц со встроенной функцией редактирования данные напрямую в ячейке (это позволяет расширить функционал такой таблицы почти до уровня Excel/Calc). В версии ExtJS 2.0 появилась возможность избирательно группировать строки, совмещая, таким образом, функционал ьность таблицы и дерева (Grid и Tree). Конечно, в таблице пол ностью поддерживается и Drag'n'Drop – можно визуально менять расположение столбцов, перетаскивать отдельные строки, менять их ширину, сортировать данные в колонках и другие операции.

    (рис 13.3) AJAX Таблица

    Развитие библиотеки не стоит на месте. Например, вместе с выходом среды Adobe AIR библиотека пополнилась отдельным интерфейсом, позволяющим легко взаимодействовать с этой средой и встраивать ее возможности в приложение. Таким образом, совместно с AIR и ExtJS можно реально создавать приложения, которые, оставаясь веб-ориентированными, вместе с этим просто идентичны традиционным "десктопным", совмещая все преимущества и сильные стороны обоих типов программ.

    13.3. Библиотека Prototype

    PrototypeJavaScript -фреймворк, упрощающий работу с Ajax и некоторыми другими функциями [7]. Несмотря на его доступность в виде отдельной библиотеки, он обычно используется программистами вместе с Ruby on Rails, script.aculo.us и Rico.

    Заявлено, что данный фреймворк поддерживается следующими браузерами: Internet Explorer 6.0+, Mozilla Firefox 1.5+, Apple Safari 2.0+ и Opera 9.25+.

    13.3.1. Возможности

    В Prototype присутствуют самые разные способы упрощения создания JavaScript -приложений, от сокращенного вызова некоторых функций языка до сложных методов обращения к XMLHttpRequest. Ниже приведены некоторые примеры.

    13.3.1.1. Функция $()

    Для обращения к DOM элементу HTML-страницы обычно используется функция:

    document.getElementById:
    document.getElementById("id_of_element")

    Функция $() уменьшает код до [8]:

    $("id_of_element")

    Но в отличие от функции DOM, функции $() можно передавать более одного аргумента и функция вернет массив ( Array ) объектов со всеми соответствующими элементами:

    var ar = $('id_1', 'id_2', 'id_3');
    for (i = 0; i < ar.length; i++) {
        alert(ar[i].innerHTML);
    }

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

    $("id_of_element").style.color = "#ffffff";

    Или, используя расширенные функции Prototype:

    $("id_of_element").setStyle({ color: '#ffffff' });

    Код для версий ниже 1.5:

    Element.setStyle("id_of_element", { color: "#ffffff" });

    13.3.1.2. Функция $$()

    Функция $$() используется для разделения CSS от контента. Она разбивает один или несколько CSS фильтров, которые поступают на вход в виде выражения подобного регулярному выражению и возвращает элементы, которые соответствуют этим фильтрам. Например, при выполнении данного скрипта:

    var f = $$('div#block .inp');

    получим массив, содержащий все элементы с классом .inp, которые находятся в контейнере div с идентификатором id="block".

    13.3.1.3. Функция $F()

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

    $F("id_of_input_element")

    13.3.1.4. Функция $A()

    Функция $A() преобразует один аргумент, который она получает, в объект Array. Эта функция, в сочетании с расширениями для класса Array, облегчает конвертирование или копирование любых перечислимых списков в объект Array. Один из вариантов использования заключается в том, чтобы преобразовать DOM NodeLists в регулярные массивы, которые могут быть более эффективно использованы.

    13.3.1.5. Функция $H()

    Функция $H() преобразовывает объекты в перечислимые Хэш-объекты, которые похожи на ассоциативный массив:

    //Допустим имеем объект:
    var hash = { method: post, type: 2, flag: t };
    //При использовании функции:
    var h = $H(hash);
    //Получим:
    alert(h.toQueryString());
    //На экране будет отображено: posttype=2flag=t

    13.3.1.6. Объект Ajax

    Объект Ajax предоставляет простые методы инициализации и работы с функцией XMLHttpRequest, без необходимости подстраивать код под нужный браузер. Существует два способа вызова объекта: Ajax.Request возвращает XML вывод AJAX -запроса, в то время как Ajax.Updater помещает ответ сервера в выбранную ветвь DOM.

    Ajax.Request в примере ниже находит значения двух полей ввода, запрашивает страницу с сервера, используя значения в виде POST -запроса, а после завершения выполняет пользовательскую функцию showResponse():

    var val1 = escape($F("name_of_id_1"));
    var val2 = escape($F("name_of_id_2"));
    var url = "http://yourserver/path/server_script";
    var pars = { value1: val1, value2: val2 };
    
    var myAjax = new Ajax.Request(
      url,
      {
          method: "post",
          parameters: pars,
          onComplete: showResponse
      });

    13.3.1.7. Класс Element

    Методы класса предназначены для работы с элементами HTML. Для создания HTML элемента используется конструктор класса:

    new Element(tagName[,{attributes}])

    В конструктор передается HTML-тег в строковом виде и, если необходимо, атрибуты тега.

    Пример создания нового элемента:

    // Создаем элемент <div> в памяти и указываем атрибуты id, class
    var newElement = new Element('div',{id: 'childDiv', class: 'divStyle'});
    // Включаем созданный элемент в DOM браузера, а именно в существующий <div> с помощью метода Element.insert
    Element.insert($('parrentDiv'),newElement);

    13.3.2. Объектно-ориентированное программирование

    Prototype также добавляет поддержку более традиционного объектно-ориентированного программирования.

    Для создания нового класса используется метод Class.create(). Классу присваивается прототип prototype, который выступает в качестве основы для каждого экземпляра класса. Старые классы могут быть расширены новыми с помощью Object.extend:

    // создания нового класса в стиле prototype 1.6
    var FirstClass = Class.create({
        // Инициализация конструктора
        initialize: function() {
            this.data = "Hello World";
        }
    });
    
    // создания нового класса в стиле prototype 1.5
    var DataWriter = Class.create();
    DataWriter.prototype = {
        printData: function() {
            document.write(this.data);
        }
    };
    
    Object.extend(DataWriter, FirstClass);

    13.4. Библиотека jQuery

    jQuery – библиотека JavaScript, фокусирующаяся на взаимодействии JavaScript и HTML [9, 10]. Библиотека jQuery помогает легко получать доступ к любому элементу DOM, обращаться к атрибутам и содержимому элементов DOM, манипулировать ими. Также библиотека jQuery предоставляет удобный API по работе с Ajax.

    Возможности:

  • переход по дереву DOM, включая поддержку XPath как плагина;
  • события;
  • визуальные эффекты;
  • AJAX -дополнения;
  • JavaScript -плагины.
  • 13.4.1. Философия

    Точно так же, как CSS отделяет визуализацию от структуры HTML, JQuery отделяет поведение от структуры HTML. Например, вместо прямого указания на обработчик события нажатия кнопки, управление передается JQuery, идентифицирующей кнопки и затем преобразовывающий его в обработчик события клика. Такое разделение поведения и структуры также называется принципом ненавязчивого JavaScript.

    Библиотека jQuery содержит функционал, полезный для максимально широкого круга задач. Тем не менее, разработчиками библиотеки не ставилась задача совмещения в jQuery функций, которые подошли бы всюду, поскольку это привело бы к большому коду, большая часть которого не востребована. Поэтому была реализована архитектура компактного универсального ядра библиотеки и плагинов. Это позволяет собрать для ресурса именно тот JavaScript -функционал, который на нем был бы востребован.

    13.4.2. Использование

    jQuery, как правило, включается в веб-страницу как один внешний JavaScript -файл:

    <head>
        <script type="text/javascript" src="путь/к/jQuery.js"></script>
    </head>

    Вся работа с jQuery ведется с помощью функции $. Если на сайте применяются другие JavaScript библиотеки, где $ может использоваться для своих нужд, то можно использовать ее синоним – jQuery. Второй способ считается более правильным, а чтобы код не получался слишком громоздким можно писать его следующим образом:

    jQuery(function($) {
        // Тут код скрипта, где в $ будет jQuery
    })

    Работу с jQuery можно разделить на 2 типа:

  • Получение jQuery -объекта с помощью функции $(). Например, передав в нее CSS-селектор, можно получить jQuery -объект всех элементов HTML попадающих под критерий и далее работать с ними с помощью различных методов jQuery -объекта.
  • Вызов глобальных методов у объекта $, например, удобных итераторов по массиву.
  • Типичный пример манипуляции сразу несколькими узлами DOM заключается в вызове $ функции со строкой селектора CSS, что возвращает объект jQuery, содержащий некоторое количество элементов HTML-страницы. Эти элементы затем обрабатываются методами jQuery. Например:

    $("div.test").add("p.quote").addClass("blue").slideDown("slow");

    находит все элементы <div> с классом test, а также все элементы <p> с классом quote, и затем добавляет им всем класс blue и визуально плавно спускает вниз.

    Методы, начинающиеся с $., удобно применять для обработки глобальных объектов. Например:

    $.each([1, 2, 3], function() {
        document.write(this + 1);
    });

    добавит на страницу 234.

    Оператор $.ajax и соответствующие функции позволяют использовать методы AJAX. Например:

    $.ajax({
        type: "POST",
        url: "some.html",
        data: { name: 'John', location: 'Boston' },
        success: function(msg) {
            alert("Data Saved: " + msg);
        }
    });

    В этом примере идет обращение к скрипту some. HTML с параметрами name=Johnlocation=Boston и полученный результат выдается в сообщении посредством alert().

    Пример добавления к элементу обработчика события click с помощью jQuery:

    jQuery(function($) {
        $("a").click(function() {
            alert("Привет, Мир!");
        });
    });

    В данном случае при нажатии на элемент <A> происходит вызов alert("Привет, Мир!").

    Рассмотрим пример, в котором необходимо раскрасить таблицу в следующем стиле [11, 12]:

    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре
    Один Два Три Четыре

    Предполагается, что есть CSS-файл, в котором определен класс "odd". Все, что нужно сделать, – это наделить нечетные строки в таблице этиv классом.

    Для начала – решение на чистом JavaScript:

    var tables = document.getElementsByTagName("table");
    for (var t = 0; t < tables.length; t++) {
        var rows = tables[t].getElementsByTagName("tr");
        for (var i = 1; i < rows.length; i += 2)
            if (!/(^|s)odd(s|$)/.test(rows[i].className))
            rows[i].className += " odd";
    }

    Теперь – решение, использующее одну из самых популярных на сегодняшний день библиотек, Prototype:

    $$("table").each(function(table) {
        Selector.findChildElements(table, ["tr"])
        .findAll(function(row, i) { return i % 2 == 1; })
        .invoke("addClassName", "odd");
    });

    Ну а теперь – jQuery:

    $("tr:nth-child(odd)").addClass("odd");

    Как видно из этого примера, библиотека jQuery позволяет находить простые и изящные решения для, казалось бы, сложных задач.

    13.4.3. Функция $()

    Ключом к пониманию работы jQuery является функция $(). Эта функция, так или иначе, вызывается всеми методами jQuery.

    Определение функции $ можно увидеть в следующем примере:

    $(html)
    $(elems)
    $(fn)
    $(expr, context)

    Вне зависимости от параметров, переданных в функцию, знак доллара вернет список объектов, над которым уже определены все доступные jQuery -функции. Это позволяет работать с любыми объектами – уже существующими на странице, созданными динамически или полученными через AJAX – так, как будто это одни и те же элементы, уже существующие на странице.

    Рассмотрим каждый объект подробнее.

    13.4.3.1. $(html)

    Позволяет создать html-элементы "на лету" из "чистого" HTML. Например, можно создать элемент div, содержащий параграф с текстом "Привет!" и добавить его к элементу с id="body" таким образом:

    var my_div = $("<div><p>Привет, Мир!</p></div>");
    my_div.appendTo("#body");

    Или еще короче:

    $("<div><p>Привет, мир!</p></div>").appendTo("#body");

    Элемент до отработки скрипта:

    <div id="body"></div>

    Элемент после отработки скрипта:

    <div id="body"><div><p>Привет, Мир!</p></div></div>

    13.4.3.2. $(elems)

    Позволяет "прицепить" всю функциональность jQuery к уже существующим элементам страницы (а именно, к элементам из объектной модели документа, из DOM). Примеры:

    $(document.body).css("background-color", "black");
    $(myForm.elements).hide();

    13.4.3.3. $(expr[, context])

    Это наиболее часто используемая форма функции $. Первый, обязательный, параметр – это выражение, которое позволит jQuery найти элемент на странице. Второй, необязательный, параметр указывает, где искать этот элемент (по умолчанию jQuery будет искать по всей странице).

    Пример поиска всех элементов p, находящихся внутри всех элементов div на странице:

    $("div > p");

    В документе:

    <p>один</p> <div><p>два</p></div> <p>три</p>

    Результат:

    [ <p>два</p> ]

    Пример поиска всех радиокнопок в первой форме на странице:

    $("input:radio", document.forms[0]);

    Пример поиска всех элементов div в XML, что прислан с сервера с помощью технологии AJAX:

    $("div", xml.responseXML);

    Рассмотрим, принцип работы. Дело в том, что в jQuery реализован механизм поиска элементов, использующий CSS и Xpath. То есть, для нахождения требуемого элемента можно воспользоваться как механизмом селекторов CSS, так и запросами по документу в стиле Xpath. CSS в версиях 1-3 предлагает разработчику богатые возможности оформления страниц (как минимум, теоретически). Например, чтобы пометить элементы div в параграфах с классом "highlight" красным цветом, разработчик может составить такую конструкцию:

    p.highlight > div 
    {
      color: #F00;
    }

    Если обратиться к рассмотренному ранее примеру с раскраской таблицы, то согласно CSS 3 раскрасить таблицу можно следующим образом:

    table tr:nth-child(odd)
    {
      background-color: magenta;
    }

    С помощью jQuery таблица была раскрашена следующим образом:

    $("tr:nth-child(odd)").addClass("odd");

    Практически все селекторы из CSS 1-3 можно использовать для нахождения элементов на странице.

    Рассмотрим примеры.

    Поиск элемента с id="unique":

    $("#unique"); // аналог из CSS: #unique {...}

    Поиск всех элементов A, имеющие класс "external" (то есть все <a class="external"> ):

    $("a.external"); // аналог из CSS: a.external {...}

    Поиск всех нечетных элементов div, находящиеся внутри элементов div с классом news-item, который находится внутри элемента div с id="outer"

    $("#outer > div.news-item > p:nth-child(odd)");

    Как видно, фантазия разработчика ограничена только возможностями CSS. Когда же возможностей CSS не хватает, на помощь приходит XPath.

    XPath (XML Path Language) является языком для обращения к частям XML-документа, а соответственно с помощью него можно обращаться к частям HTML-документа. Из этого следует, что тоже можно сделать и с помощью jQuery, так как эта библиотека поддерживает довольно большое подмножество XPath, объединяя его с некоторыми селекторами CSS для создания гибкого механизма поиска элементов на странице.

    Поиск всех элементов P, которые содержат ссылку:

    $("p[a]");

    Поиск всех элементов A, которые содержат атрибут rel, равный "nofollow":

    $("//a[@rel='nofollow']");

    Поиск всех видимых элементов LI внутри элемента UL:

    $("ul/li:visible");   // XPath + селектор
    $("ul > li:visible"); // CSS + селектор

    Как видно, если известно, какой элемент необходимо выбрать, jQuery позволит это сделать.

    13.4.4. Инициализация

    Большинство библиотек JavaScript часто использует событие window.onload. Это событие вызывается после того, как документ полностью загрузился в окно браузера. Это значит, что вызываемый в обработчике этого события код не сработает, пока не загрузятся все изображения, баннеры и видеоролики на странице.

    jQuery предлагает решение этой проблемы. У разработчика есть возможность выполнить свой код в тот момент, когда доступна объектная модель документа, то есть когда браузер уже получил исходный код страницы полностью, но, возможно, еще не подгрузил различное мультимедийное содержимое (рисунки, видео, Flash).

    Возможность выполнить такой код предлагается методом .ready():

    $(document).ready(
      function() { /* ... */ }
    );

    или его сокращенным вариантом, $(fn):

    $(
      function() { /* ... */ }
    );

    В качестве аргумента в .ready() передается функция, которая будет выполнять все необходимые действия.

    $(document).ready(
      function() {
          // добавим ко всем ссылкам на странице некий текст
          $("a").append("<span>(открыть в новом окне)</span>");
      }
    );
    $(document).ready(init);
    function init() {
        // находим все элементы Р с классом highlight и задаем им другой фон
        $("p.highlight").css("background-color", "#000");
        // выполняем любые действия
        alert('Функция init()');
        // вызывать другие функции
        anotherFunc();
        // манипулируем объектами в документе
        document.getElementById("some_id");
    }
    // Используем кратую форму записи
    $(
      function() {
          alert('Документ готов!');
      }
    )

    13.4.5. Управление элементами документов и их свойствами

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

    Методы для манипуляции элементами документа в jQuery
    Метод Описание
    append(content)/prepend(content) Добавить переданный элемент или выражение в конец/в начало выбранного элемента.
    appendTo(expr)/prependTo(expr) Добавить выбранный элемент в конец/в начало переданного элемента.
    attr(name) Получить значение атрибута.
    attr(params) Установить значение атрибутов. Атрибуты передаются в виде {ключ1:значение1[, ключ2:значение2[, …]]}
    attr(name, value) Установить значение одного атрибута.
    css(name)/css(params)/css(name, value) Получить/установить значение отдельных параметров CSS. Аналогично функции attr().
    text()/text(val) Получить/задать текст элемента. В случае ввода текста специальные символы HTML заменяются символьными примитивами ( entities, например, знак ">" будет заменен gt; ).
    empty() Удалить все подэлементы текущего элемента.

    В следующих примерах работа происходит со следующим HTML-кодом:

    <div id="my-div">
      <a href="http://example.com/" id="my-link">Ссылка</a>
    </div>

    Пример 1:

    $("#my-div").html();    //Вернет <a href="#" id="my-link">Ссылка</a>

    Пример 2:

    $("#my-link").attr("href");  // Вернет http://example.com/

    Пример 3:

    $("#my-div").append("<strong>Полужирный текст</strong>");
    // или
    $("<strong>Полужирный текст</strong>").appendTo($("#my-div"));

    HTML станет таким:

    <div id="my-div">
      <a href="http://example.com/" id="my-link">Ссылка</a>
      <strong>Полужирный текст</strong>
    </div>

    Пример 4:

    $("#my-div").empty();

    HTML станет таким:

    <div id="#my-div"></div>

    Пример 5:

    $("#my-div").text("<a>Привет!</a>");

    HTML станет таким:

    <div id="#my-div">lt;agt;Привет!lt;/agt;</div>

    Пример 6:

    $("#my-div").css(
      {
          backgroundColor: "#F00",
          color: "#00F"
      }
    );

    HTML станет таким:

    <div id="my-div" style="background-color: #F00; color: #00F">
      lt;agt;Привет!lt;/agt;
    </div>

    В последнем примере стоит обратить внимание на то, что для сложносоставных свойств CSS вроде background-color, font-weight и прочих используются их эквиваленты из Javascript ( backgroundColor, fontWeight и т.п.).

    13.4.5.1. Последовательный вызов функций

    Главная особенность большинства методов jQuery – это возможность связывать их в цепочки. Методы, манипулирующие элементами документа, обычно возвращают эти объекты для дальнейшего использования, что позволяет писать примерно следующее:

    // Заполняем скрытый элемент <div id="divEdit"></div> текстом и показываем его
    $("#divEdit").html(response).show();
    
    // найти <select id="my_select">...</select>
    var sel = $("#my_select");
    
    // добавляем к нему <option value="1">Пример опции</option>
    $("<option></option>")  // создаем требуемый элемент
      .attr("value", 1)     // устанавливаем значение одного из его атрибутов
      .html("Пример опции") // записываем в него текст
      .appendTo(sel);       // прикрепляем к уже существующему элементу

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

    13.4.6. Работа с индивидуальными элементами

    Функция $() возвращает коллекцию элементов. Так, $("p") вернет массив всех элементов <p>, какие только могут присутствовать на странице (или пустой массив, если таких элементов не существует). Одним из условий работы большинства функций jQuery является непременная обработка всех элементов в возвращаемой коллекции.

    Например:

  • 1. Цвет всех ссылок на странице станет зеленым:
    $("a").css("color", "green");
  • 2. Ко всем элементам div с классом test будет добавлен новый элемент:
    $("div.test").append("<span>Тестовый элемент</span>");
  • Но иногда необходимо обратиться к индивидуальным элементам возвращаемой коллекции.

    13.4.6.1. Индексы и get([index])

    Возвращаемая функцией $() коллекция является "псевдо-массивом" DOM-элементов. Поэтому можно обратиться к индивидуальным элементам по их индексу:

    $("p")[0].className = "test";

    Иногда, правда, необходимо обратиться непосредственно к массиву DOM-объектов, скрытых внутри "псевдо-массива", которым является объект jQuery. Для этого используется функция get():

    $("p");       // Получили объект jQuery
    $("p").get(); // Получили соответствующую коллекцию DOM-элементов

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

    $("a").get(0); // Получили первую из всех ссылок на странице

    13.4.6.2. each(fn)

    Иногда нужно пробежаться по всем элементам коллекции и выполнить над ними какие-то действия. Для этого понадобится функция each. Эта функция принимает в качестве аргумента другую функцию. each() работает в контексте найденных элементов и поэтому каждый раз, когда выполняется переданная ей функция, в этой функции доступна переменная this, указывающая на соответствующий DOM-элемент:

    $("p").each(
      function() {
          // внутри функции this указывает на текущий DOM-элемент
          alert(this.innerHTML);
      }
    );

    В этом примере находятся все элементы p на странице, и для каждого из этих элементов вызывается функция, которая показывает его содержимое.

    Функция, передаваемая в метод each(), может принимать на вход один аргумент, который является индексом текущего элемента в коллекции.

    Для иллюстрации перепишем пример с раскраской нечетных строк таблицы с помощью each(). Если есть некая таблица с id="zebra" и CSS-класс "odd" для нечетных строк, то можно написать так:

    $("#zebra tr").each(colorize); // передаем в each функцию colorize
    function colorize(index) {
        if (index % 2 == 1)       // index является индексом текущего элемента в коллекции
            $(this).addClass("odd");
    }

    13.4.7. Анимации

    Грамотная манипуляция свойствами элементов на странице позволила создателям Javascript -библиотек создавать визуальные эффекты, которые раньше были возможны только при использовании технологии Flash. Это и плавное появление, и скрытие объектов, плавное изменение различных свойств этих объектов (фонового цвета, размеров), реализация всевозможных элементов интерфейса вроде сплиттеров, деревьев, перетягиваемых объектов и сортируемых списков.

    В базовой поставке jQuery предлагает лишь ограниченный набор таких эффектов. Остальные эффекты реализуются при помощи модулей расширения (о которых речь пойдет ниже).

    13.4.7.1. Animate

    Ключевой функцией, на которой базируются все остальные, является функция animate:

    animate(params, speed, easing, callback);

    Здесь:

  • params – свойства, которые участвуют в анимации в виде пар {ключ: значение}, например: { height: "show"} или {opacity: 50, width: 100, height: 200} ;
  • speed – скорость в миллисекундах;
  • easing – замедление анимации (замедляется ли к концу, "easein", или, наоборот, ускоряется, "easeout" );
  • callback – функция, которая будет вызвана после завершения анимации.
  • Пусть, например, есть элемент div с каким-нибудь текстом, и необходимо скрыть этот текст, заменить новым и показать обновленный текст:

    $("#mydiv")
      .animate({ height: "hide" }, 300)
      .text("Новый текст")
    .animate({ height: "show" }, 300);

    13.4.7.2. Эффекты

    Метод animate является основой большинства эффектов jQuery и плагинов. Например, jQuery предлагает следующие методы для показа и скрытия элементов:

  • show([speed[, callback]]) – показать элемент;
  • hide([speed[, callback]]) – скрыть элемент;
  • fadeIn(speed[, callback]) – показать элемент путем изменения его прозрачности;
  • fadeOut(speed[, callback]) – скрыть элемент путем изменения его прозрачности;
  • slideDown(speed, callback) – показать элемент, спустив его сверху;
  • slideUp(speed, callback) – показать элемент, подняв его снизу;

    где

  • speed – скорость в миллисекундах или одно из "slow" (600 миллисекунд) или "fast" (200 миллисекунд);
  • callback – функция, которая будет вызвана после выполнения анимации.
  • Скрытая мощь функции animate позволяет создавать поистине удивительные эффекты, многие из которых уже реализованы, например, в модуле расширения Interface (http://interface.eyecon.ro/).

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

    for (i = 0; i < 10; i++) {
        $("#my-div").animate({ height: "show" }, 300);
        $("#my-div").animate({ height: "hide" }, 300);
    }

    В результате будет получено не беспорядочное моргание, пока двадцать эффектов "борются" за право показать/скрыть элемент "my-div", а плавный показ, потом сокрытие, потом снова показ – и так 20 раз. Стоит обратить внимание на то, что очередь эффектов составляется поэлементно. То есть эффекты, примененные к разным элементам одновременно, будут выполняться одновременно.

    13.4.8. AJAX

    Базовыми функциями для работы с AJAX являются post() и get() (есть еще более низкоуровневая, ajax() ):

    $.post(url[, params[, callback]])
    $.get(url[, params[, callback]])

    Здесь:

  • url – адрес страницы, на которую будет отправлен запрос;
  • params – параметры, передаваемые в запросе в виде пар "ключ : значение";
  • callback – функция, которая будет вызвана в случае успешного завершения вызова.
  • Например:

    $.post(
      '/ajaxtest.php',
      {
          type: "test-request",
          param1: "param1",
          param2: 2
      },
      onAjaxSuccess
    );
    function onAjaxSuccess(data) {
        // Здесь получаем данные, отправленные сервером
        alert(data);
    }

    13.5. Модель работы веб-приложений Comet

    Технология Comet – модель работы Веб-приложения, при которой постоянное HTTP-соединение позволяет Веб-серверу отправлять ( push ) данные браузеру без дополнительного запроса со стороны браузера [13, 14, 15, 16]. Название " Comet " используется для обозначения множества техник, позволяющих достичь такого взаимодействия. Общее у этих методов то, что они основаны на технологиях, непосредственно поддерживаемых браузером, таких как JavaScript, а не на плагинах. Теоретически подход Comet отличается от изначальной концепции Всемирной паутины, при которой браузер запрашивает страницу полностью или частично для того, чтобы обновить страницу. Однако на практике приложения Comet обычно используют Ajax с "long polling" для проверки наличия новой информации на сервере.

    13.5.1. HTTP server push

    HTTP server push (так же называемое HTTP streaming) – механизм отправки данных с Веб-сервера к Веб-браузеру (рис. 13.4) [15]. HTTP server push может быть реализовано посредством нескольких механизмов. Основной – это когда Веб-сервер не закрывает соединение после отправки ответа клиенту. Веб-сервер оставляет соединение открытым, так что если произойдет событие, то оно может быть отправлено одному или множеству клиентов. Иначе данные составляются в очередь до следующего запроса клиента. Большинство серверов реализуют эту функциональность через CGI (например, NPH скрипты в Apache).

    (рис 13.4) Схема работы Comet – HTTP-streaming

    Другой механизм относится к специальному MIME type, называемый multipart/x-mixed-replace, который был введен Netscape в 1995. Веб-браузер интерпретирует это как изменения документа всякий раз, когда сервер сообщает о новой версии клиенту. Сегодня это все еще поддерживается Firefox, Opera и Safari, но традиционно игнорируется Internet Explorer. Это применимо к HTML-документам, но также для потокового отображения картинок для приложений работающих с Веб-камерой.

    WHATWG Web Applications 1.0 предлагает включить механизм "проталкивания" содержимого клиенту. 1 сентября 2006 браузер Opera реализовала эту новую экспериментальную технологию в функциональности с названием "Server-Sent Events". Сейчас это часть стандартов HTML5. Другой, относящейся к вопросу, частью HTML5 стали Веб-сокеты, которые доступны в Google Chrome, начиная с версии 4.0.249.0, и существуют так же JavaScript библиотеки, эмулирующие работу Веб-сокетов.

    В последнее время часто можно услышать выражение real-time web, за которым, как говорят, будущее Интернета. По сути, это набор технологий, который позволяет получать в реальном времени данные добавляемые в Сеть. Частным случаем этой идеи является real-time search – поиск в реальном времени.

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

    13.5.2. Pooling

    Один из способов узнать, нет ли на сервере каких либо обновлений, применять технологию AJAX, с определенным интервалом времени делать запрос на сервер (рис. 13.5) [15].

    (рис 13.5) Пример асинхронных запросов в AJAX – short polling

    Такой тип взаимодействия называется polling (от англ. poll – тянуть), именно вытягиванием данных и занимается браузер. Преимущества перед полным обновлением страницы очевидны, но есть и недостатки. Главным из них является "холостая работа" – часто браузер делает сотни запросов, а в ответ узнает, что новых данных нет. Именно для решения этой проблемы и появилась технология long polling, позволяющая делать запросы, которые возвращают результат, как только он появляется. Пример взаимодействия показан на рис. 13.6 [15].

    (рис 13.6) Пример долгих асинхронных запросов – long polling

    Эта техника является золотой серединой между простым AJAX и сложным HTTP-streaming. Главным преимуществом является то, что клиент создает всего одно соединение, через которое получает данные от сервера в реальном времени. Главными недостатками можно назвать сложность реализации и несоответствие духу протокола HTTP.

    13.6. Ключевые термины

    JSON, JavaScript, AJAX, ExtJS, Prototype, jQuery, Comet.

    13.7. Краткие итоги

    JSON – текстовый формат обмена данными, основанный на JavaScript и обычно используемый именно с этим языком.

    Практическая польза использования JSON открывается при использовании технологии AJAX.

    Хотя JSON предназначен для передачи данных в сериализованном виде, его синтаксис соответствует синтаксису JavaScript и это создает ряд проблем безопасности:

  • JavaScript eval() ;
  • подделка кроссдоменного запроса.
  • ExtJS – библиотека JavaScript для разработки веб-приложений и пользовательских интерфейсов, изначально задуманная как расширенная версия Yahoo! UI Library, однако, преобразовавшаяся затем в отдельный фреймворк.

    ExtJS поддерживает свойства объектно-ориентированного программирования посредством следующих методов:

  • Ext.namespace ;
  • Ext.override ;
  • Ext.extend и соглашения о параметрах конструкторов;
  • Ext.apply ;
  • Ext.applyIf.
  • Компоненты библиотеки ExtJS можно разделить на несколько групп:

  • Самый нижний слой;
  • Core:
  • ядро;
  • модуль для использования визуальных компонентов;
  • утилиты;
  • поддержка кросс-браузерного DragDrop;
  • возможность хранить состояние интерфейса в независимом хранилище;
  • Работа с данными:
  • Store ;
  • Reader ;
  • Proxy ;
  • объект Record.
  • ExtJS предлагает следующие средства для визуализации:

  • Layout;
  • Tooltip ;
  • Таб-панель;
  • Tree ;
  • Форма;
  • Grid.
  • В Prototype присутствуют самые разные способы упрощения создания JavaScript -приложений, от сокращенного вызова некоторых функций языка до сложных методов обращения к XMLHttpRequest:

  • $() ;
  • $$() ;
  • $F() ;
  • $A() ;
  • $H() ;
  • объект Ajax ;
  • Element.
  • Prototype добавляет поддержку объектно-ориентированного программирования.

    jQuery – библиотека JavaScript, фокусирующаяся на взаимодействии JavaScript и HTML.

    Возможности jQuery:

  • переход по дереву DOM, включая поддержку XPath как плагина;
  • события;
  • визуальные эффекты;
  • AJAX -дополнения;
  • JavaScript -плагины.
  • Вся работа с jQuery ведется с помощью функции $.

    jQuery предлагает разработчику большое количество методов для манипуляции элементами документа и их свойствами.

    Главная особенность большинства методов jQuery – это возможность связывать их в цепочки.

    Технология Comet – модель работы Веб-приложения, при которой постоянное HTTP-соединение позволяет Веб-серверу отправлять (push) данные браузеру без дополнительного запроса со стороны браузера.

    HTTP server push – механизм отправки данных с Веб-сервера к Веб-браузеру

    Один из способов узнать, нет ли на сервере каких либо обновлений, применять технологию AJAX, с определенным интервалом времени делать запрос на сервер (short polling).

    Технология long polling, позволяет делать запросы, которые возвращают результат, как только он появляется.

    Вернуться к учебному плану