Библиотеки JavaScript

advertisement
2009
Библиотеки JavaScript
Лескин Филипп
Группа КН-302
12.09.2009
1
Оглавление
1. Mootools
a. Возможности Mootools
b. Анимация в MooTools
c. Интересные Примеры Применения Mootools
2. JQuery
a. Немного Теории
b. jQuery в Примерах
c. Интересные Примеры Применения jQuery
3. Dojo
a. Общий взгляд на Dojo Toolkit
b. Dojo в примерах
4. MochKit
a. MochiKit — библиотеки на все случаи жизни
5. Prototype
a. Краткий обзор самых полезных возможностей
2
2
6
10
11
12
19
25
26
28
32
44
44
49
49
2
MooTools
Тип: Библиотека JavaScript
Разработчик: Valerio Proietti
Написана на: JavaScript
ОС: кроссплатформенное
Версия: 1.2.3 — 23 апреля 2009
Сайт: mootools.net
MooTools — это свободный JavaScript фреймворк для разработки кроссбраузерных вебприложений и веб-сервисов. MooTools является компактным, модульным, объектноориентированным фреймворком, созданным для помощи опытным разработчикам JavaScript. Он
позволяет писать мощный, гибкий и кроссбраузерный код благодаря элегантному, хорошо
документированному и последовательному API. MooTools совместим и протестирован с
браузерами: Safari 2+, Internet Explorer 6+, Firefox 2+ (и другими, основанными на движке Gecko),
Opera 9+.
Возможности Mootols
Ядро фреймворка можно загрузить с официального сайта: http://mootools.net/download. В
дальнейшем можно загружать только необходимые модули.
Подключаем файл к странице, скажем в head:
<html>
<head>
<title>MooTools начинающих</title>
<script type="text/javascript" src="mootools.js"></script>
</head>
<body> </body>
</html>
Получение DOM (Document Object Model) элементов:
Для того, чтобы получить элемент по его id, необходимо сделать следующее:
var myElement = $('elementId');
А чтобы получить все элементы с каким-то классом, можно сделать вот так:
var myElements = $$('.elementsClassName'); // в итоге получим массив
Вообще говоря, аргументом оператора $$() может быть любой CSS селектор. Кроме того, у
функции может быть любое количество аргументов, каждый из которых представляет собой один
или несколько элементов. Более наглядные примеры:
$$('#myWrapper a') // все <a> внутри элемента с id="myWrapper"
3
$$('img[alt=^Image]') // все картинки, alt которых начинается с Image
$$(myelement1, 'a', '#myid', document.getElementsByTagName('div')); // массив этих элементов
MooTools содержит мощную коллекцию классов и продвинутую систему наследования, которая
позволяет вторичное использование кода, а так же его расширение. Например:
var someClass=new Class({
initialize: function(){
alert('Hello, world!');
}
});
Результат — алерт со словами: «Hello, world».
initialize — конструктор класса. Вызывается автоматически при создании экземпляра класса.
У класса могут быть методы:
var testClass=new Class({
initialize: function(element){
this.element=$(element);
},
draw: function(){
this.element.setStyles({
'background-color': 'red',
border: 'solid 1px blue'
});
},
hide: function(){
this.element.setStyle('display', 'none');
},
show: function(){
this.element.setStyle('display', 'block');
},
toggle: function(){
this.element.style.display == 'none'? this.show(): this.hide();
}
});
Здесь четыре метода(не включая конструктора) — draw, hide, show и toggle.
draw делает элемент красным с синей границей;
hide скрывает элемент;
show показывает элемент;
toggle изменяет видимость элемента;
Extends:
С помощью него создается новый класс расширяющий существующий. Пример:
Расширим класс из первого примера (Hello, world):
var extendedSomeClass=new Class({
Extends: someClass,
initialize: function(){
this.parent();//вызываем метод initialize базового класса
alert('extended Hello, world!');
4
}
});
Результат — алерт «Hello, world!» из метода initialize базового класса и алерт «extended
Hello, world!»
Class.Extras:
В MooTools есть несколько классов, которые очень часто используются. Это классы Options, Events
и Chain.
var exampleClass6=new Class({
Implements: [Options, Events],
options: { // опции по умолчанию
bgColor: 'red',
borderWidth: 1
},
initialize: function(element, options){
this.setOptions(options);
this.element=$(element);
},
draw: function(){
this.element.setStyles({
'background-color': this.options.bgColor,
'border-style': 'solid',
'border-color': 'black',
'border-width': this.options.borderWidth
});
this.fireEvent('draw');//event draw
}
});
var obj=new exmpleClass6('el', {
bgColor: '#561',
borderWidth: 4
});
obj.addEvent('draw', function(){
alert('draw выполнен');
});
obj.draw();
addEvent — добавить событие
fireEvent — выполнить событие
В данном примере событие добавлено явно с помощью метода addEvent. Событие можно также
добавить, указав его в options с префиксом on и первой прописной:
var obj=new exampleClass6('el', {
bgColor: '#561',
borderWidth: 4,
onDraw: function(){
alert('draw выполнен');
}
5
});
Манипулирование классами и свойствами:
Теперь давайте посмотрим, как управлять классами и другими аттрибутами элементов.
Рассмотрим, что мы
можем делать с классом элемента:
myElement.addClass('test-class'); // добавляет класс к элементу
myElement.removeClass('test-class'); // убирает класс у элемента
myElement.hasClass('test-class'); // проверяет, есть ли такой класс у элемента
myElement.toggleClass('test-class'); // если есть класс, убирает его, в противном случае добавляет
Для получения какого-либо аттрибута элемента можно использовать функции get('attr'),
getProperty('attr') или
getProperties('attr1', 'attr2', ...) (последняя возвращает ассоциативный массив значений).
Соответственно, для
установки аттрибута используем set('attr', 'val'), setProperty('attr', 'val') и setProperties({attr1: val1,
attr2: val2}).
Рассмотрим пример. Допустим у вас есть две картинки и вы хотите взять значение alt у первой и
поставить его в
alt второй:
<img src="img1.png" alt="Картинка №1" id="img1" />
<img src="img2.png" alt="Картинка №2" id="img2" />
<script type="text/javascript">
$('img2').set('alt', $('img1').getProperty('alt'));
</script>
Привязка событий:
Привязка события осуществляется функцией addEvent(). Ее первый аргумент — тип события,
второй — фунция,
которая будет выполняться при этом событие на этом элементе.
Например, для того, чтобы выполнить какую-то функцию сразу после загрузки DOM, нужно
написать вот так:
document.addEvent('domready', function() {
myFuntion();
});
А если вы хотите, чтобы при клике на ссылку выполнялась какая-то функция, то сделать это
можно вот так:
<a href="/" id="my-link">ссылка</a>
<script type="text/javascript">
document.addEvent('domready', function() {
$('my-link').addEvent('click', function(event) {
6
event.preventDefault(); // отменяем стандартное действие при клике, чтобы не было перехода по
ссылке
myFunction();
});
});
</script>
Анимация в MooTools
Основные принципы. Класс Fx:
Анимация в MooTools — это изменение какого-либо свойства какого-либо элемента во времени
(переход), например, изменение CSS-свойства left — это движение по горизонтали, а backgroundcolor — плавное изменение цвета заливки.
Для создания анимационных эффектов в MooTools используется класс Fx. Все классы,
реализующие какую-либо анимацию наследуются от Fx. В этом классе определены
вспомогательные методы для анимации:
start — этот метод запускает переход.
set — изменяет значение анимируемого параметра. Вызывается на каждом шаге перехода. По
смыслу это абстрактный метод, если его можно так назвать, т. е. каждая реализация
анимационного плагина должна переопределить set по-своему.
cancel — отменяет текущую анимацию.
pause — приостанавливает текущую анимацию.
resume — возобновляет приостановленную анимацию.
Конструктор принимает следующие опции:
fps — количество кадров в секунду для анимации. Характеризует, если можно так сказать,
«плавность» анимации — чем он больше, тем «плавнее».
unit — единица измерения, например «em», «px», «%». Используется для преобразований CSSсвойств.
link — опция, характеризующая поведение анимации, когда во время перехода был вызван start.
Может принимать следующие значения (пример с разными значениями link):
'ignore' — все вызовы start во время анимации игнорируются. Используется по умолчанию.
'cancel' — текущая анимация будет остановлена и начата новая с параметрами, пришедшими в
start.
'chain' — все вызовы start во время анимации будут добавлены в цепь и их анимация начнется
сразу после того, как закончится текущая.
duration — длительность анимации.
transition — уравнение перехода.
Работает Fx следующим образом. В вызов метода start передаются начальное и конечное
значения изменяемого параметра, затем запускается таймер, который периодически (период
равен 1 / fps) вызывает метод step, где на каждом шаге анимации вычисляется значение
анимируемого параметра. А вычисляется оно так:
Время, прошедшее со старта перехода до текущего момента делится на duration, таким образом
получается число от 0 до 1 характеризующее прогресс анимации. Назовем его progress.
Это число передается в функцию перехода (будет рассмотрено ниже). Результат вычисления
функции перехода назовем delta.
7
Значение же анимируемого параметра вычисляется так: param = (to − from) × delta + from, где from
— начальное значение параметра, to — конечное значение параметра. Очевидно, что при delta
равном 0 мы получаем начальное значение параметра, а при 1 — конечное.
После вычисления значения параметра, в методе set происходит уже непосредственная
визуализация анимации (например, изменение CSS-свойства left).
Стоит подчеркнуть, что сам по себе класс Fx является абстрактным, то есть ничего сам не
анимирует, он оперирует только общими данными для реализации принципов анимации.
Непосредственной анимацией занимаются наследники Fx. В основном для анимации элементов
на HTML-страницах используются классы, которые анимируют различные CSS-свойства элементов.
Об этих классах мы поговорим, а также рассмотрим случай с созданием своего класса, который
реализует анимацию не-CSS-свойств.
Уравнения переходов:
Согласитесь, не очень красиво бы смотрелась анимация перемещения элемента на странице, если
бы он резко стартовал и так же резко останавливался в конце пути. Так происходит, если
приращение delta (которое упоминалось выше) на протяжении всего времени остается
постоянным. Это называется — линейный переход, его уравнение delta = progress. Если взять
последовательно несколько значений progress от 0 до 1 и подставить в уравнение, то получим
обычную прямую, которая резко начинается в значении 0 и резко заканчивается в 1.
Стандартные уравнения:
Linear — прямая (delta = progress).
Quad — квадратичная функция (delta = progress2).
Cubic — кубическая функция (delta = progress3).
Quart — delta = progress4.
Quint — delta = progress5.
Pow — общая степенная функция delta = progressx (по умолчанию степень равна 6).
Expo — экспоненциальная функция (delta = 2(progress − 1) × 8).
Circ — четверть окружности (delta = 1 − sin(acos(progress))).
Sine — кусок синусоиды (delta = 1 − sin((1 − progress) × π / 2)).
Back — сначала оттягивает delta в минус, а потом плавно доводит до 1.
Bounce — прыгающий переход.
Elastic — эластичный переход (единственная ассоциация — резинка :).
Все эти уравнения доступны в классе Fx.Transitions. Они используются в качестве значения опции
transition конструктора Fx. Помимо прямого использования этих уравнений можно еще применять
к ним модификаторы:
easeIn — используется по умолчанию и ничего не меняет.
easeOut — вычисляет значение delta по формуле 1 − transition(1 − progress), таким образом
разворачивая кривую перехода.
easeInOut — до середины перехода вычисляет delta по формуле transtition(2 × progress), а после
— по (2 − transition(2 × progress)) / 2, совмещая в одном переходе две кривых: прямую и
развернутую.
Цепочки. Сцепление анимационных преобразований:
8
Очень часто бывает нужно задать несколько последовательных преобразований так, чтобы
каждое следующее осуществлялось после окончания предыдущего. Если просто вызвать их
последовательно, то по умолчанию каждый следующий вызов будет игнорироваться (смотрим
опцию link класса Fx). Если же указать link равным 'chain', то все последующие вызовы добавятся в
цепочку и будут выполнены последовательно.
Однако, существует и специальный синтаксис для создания цепочек. Реализуются они в классе
Chain. Он содержит аж целых три метода:
chain — дописывает функцию в конец цепочки.
callChain — вызывает следующую в цепочке функцию и удаляет ее из цепочки.
clearChain — очищает цепочку.
Стандартные анимационные плагины:
Fx.Tween.
Этот класс является простейшим классом, анимирующим любое CSS-свойство элемента. Для
работы ему нужна ссылка на анимируемый элемент, имя анимируемого CSS-свойства и диапазон
значений. Пример создания:
var myFx = new Fx.Tween(element, [, options]);
На этом классе основан пример с применением различных уравнений переходов:
// Здесь мы будем менять CSS-свойство top у элемента с идентификатором 'Ball'.
var fx = new Fx.Tween('Ball', { property: 'top', duration: 900, link: 'cancel' });
var transitionSelect = $('Transition');
var modifierSelect = $('Modifier');
var test = $('Test');
// При возникновении одного из следующих событий запустить анимацию.
modifierSelect.addEvent('change', startAnimation);
transitionSelect.addEvent('change', startAnimation);
test.addEvent('click', startAnimation);
function startAnimation(){
// Получаем имя перехода в виде 'sine:in'.
fx.options.transition = transitionSelect.value + modifierSelect.value;
// Двигаем шарик с выбранным переходом.
fx.start(60, 400);
}
В HTML-коде примера два выпадающих списка, анимируемый шарик и кнопка на всякий
пожарный.
9
Fx.Morph.
Используется чаще, чем Fx.Tween потому что может одновременно анимировать несколько CSSсвойств элемента. Конструктор почти такой же как и у Fx.Tween за исключением отсутствия опции
property, вместо нее в метод start передается объект, описывающий, какие CSS-свойства
необходимо анимировать и в каких диапазонах. Например:
morph.start({
'margin-left': [0, 10],
'background-color': ['#EEE', '#555']
});
означает, что анимироваться будут два CSS-свойства: margin-left (от 0 до 10) и background-color (от
'#EEE' до '#555'). Диапазоны значений можно и не указывать, а только указать конечное значение,
тогда в качестве начального будет взято текущее значение, указанное в стиле элемента
Fx.Slide.
Довольно полезный класс, когда нужно чтобы что-то откуда-то выехало или заехало. Конструктор
очень похож на все предыдущие, поэтому особо останавливаться на нем не буду. Скажу только,
что он понимает еще несколько опций, самая важная их которых — это mode, определяющая
направление «выезжания»: вертикальное или горизонтальное. На основе этого класса сделаны
еще два примера с анимированными меню: первый и второй.
Fx.Elements.
Позволяет удобно анимировать любое количество CSS-свойств для любого количества элементов
одновременно. В его конструктор передается массив элементов, над которыми будут
осуществляться преобразования. А вся прелесть заложена в методе start — туда передается
массив объектов для преобразования. Получается, каждому элементу из массива, переданного в
конструкторе соответствует элемент объект из массива, переданного в start. Таким образом
осуществляется массовая анимация элементов.
В примере ниже изменяется прозрачность массива элементов при наведении на них курсора,
причем чем ближе элемент к тому, что в данный момент под курсором, тем меньше его
прозрачность, а чем дальше — тем больше:
var elements = $$('#ElementsContainer .Element');
var elementsFx = new Fx.Elements(elements, { duration: 500, link: 'cancel' });
elements.each(function(element, i){
// При наведении курсора на элемент пересчитываем прозрачность соседей.
element.addEvent('mouseenter', function(){
var arg = {};
// Для всех соседних элементов вычисляем значение их прозрачности
// на основании их удаленности от текущего.
elements.each(function(element, j){
10
arg[j] = { opacity: 1 - Math.min(Math.abs(i - j), 5) / 8 };
});
// Стартуем изменение прозрачности.
elementsFx.start(arg);
});
});
Интересные Примеры Применения Mootools
-
Интересная реализация галереи для сайта
Плагин, который позволяет обратить внимание посетителей на любой объект на странице.
11
jQuery
Тип: JavaScript-библиотека
Разработчик: Джон Ресиг
Написана на: JavaScript
ОС: Кроссплатформенное ПО
Версия: 1.3.2 — 20 февраля 2009
Лицензия: MIT License и GNU GPL
Сайт: http://jquery.com/
jQuery — библиотека JavaScript, фокусирующаяся на взаимодействии JavaScript и HTML.
Библиотека jQuery помогает легко получать доступ к любому элементу DOM, обращаться к
атрибутам и содержимому элементов DOM, манипулировать ими. Также библиотека jQuery
предоставляет удобный API по работе с Ajax.
Точно так же, как CSS отделяет визуализацию от структуры HTML, JQuery отделяет поведение
от структуры HTML. Например, вместо прямого указания на обработчик события нажатия
кнопки, управление передаётся JQuery, идентифицирующей кнопки и затем
преобразовывающий его в обработчик события клика. Такое разделение поведения и
структуры также называется принципом ненавязчивого JavaScript.
Библиотека jQuery содержит функционал, полезный для максимально широкого круга задач.
Тем не менее, разработчиками библиотеки не ставилась задача совмещения в jQuery
функций, которые подошли бы всюду, поскольку это привело бы к большому коду, бо́ льшая
часть которого не востребована. Поэтому была реализована архитектура компактного
универсального ядра библиотеки и плагинов. Это позволяет собрать для ресурса именно тот
JavaScript-функционал, который на нём был бы востребован.
Работу с jQuery можно разделить на 2 типа:
- Получение jQuery-объекта с помощью функции $(). Например, передав в неё CSS-селектор,
можно получить jQuery-объект всех элементов HTML попадающих под критерий и далее
работать с ними с помощью различных методов jQuery-объекта.
- Вызов глобальных методов у объекта $, например, удобных итераторов по массиву.
jQuery совместим и протестирован с браузерами: Safari 2+, Internet Explorer 6+, Firefox 2+ (и
другими, основанными на движке Gecko), Opera 9+.
12
Немного Теории
Пространство имен (namespacing):
Ключевым моментом в создании хорошего JavaScript-кода для дальнейшего использования
является тщательное управление пространством имен. В JavaScript существует единое
глобальное пространство имен (объект window), и многие программисты (и даже некоторые
библиотеки) засоряют его безо всякой надобности. Глобальные переменные — зло! Более
благоразумные разработчики сводят к минимуму свое вторжение в это пространство,
используя некоторые методы, например, модульную модель.
jQuery вводит единственный объект в глобальное пространство имен — функцию/объект
jQuery. Все остальное — это либо непосредственное свойство jQuery, либо метод объекта,
возвращаемого вызовом функции jQuery.
Что можно сказать об улучшениях языка? Большинство библиотек предоставляют некоторое
подобие функций отображения, фильтрации и обрезания, которые, к несчастью, отсутствуют в
тех движках JavaScript, которые включены в большинство браузеров. Некоторые библиотеки
напрямую расширяют встроенные в JavaScript классы String и Array, но также не до конца
безопасно. String.prototype и Array.prototype являются самостоятельными глобальными
пространствами имен, и добавление в них каких-либо свойств влечет опасность коллизий,
связанных с использованием одних и тех же имен переменных в разных контекстах.
В jQuery имеется некоторое количество функций, расширяющих возможность JavaScript, но
каждая из них является доступной только как свойство объекта jQuery: jQuery.each,
jQuery.extend, jQuery.grep, jQuery.map, jQuery.merge и jQuery.trim. Они по определению не
будут конфликтовать с каким-либо другим кодом.
Печально известная функция $:
Я был не до конца честен, когда заявил о том, что jQuery вводит только один символ в
глобальное пространство имен, на самом деле есть еще и $: он используется как сокращение
для jQuery. Слава богу, это производится достаточно мягко: если вам снова требуется ваша
прежняя функция $ (например, если у вас есть часть кода, основанная на Prototype), вы
можете вызвать jQuery.noConflict(), чтобы вернуть свою старую функцию $.
Если вам требуется ограничить использование функции $ для jQuery не опасаясь коллизий при
каком-либо другом использовании глобальной функции $, документация по jQuery предлагает
следующий способ:
(function($) {
// Внутри этого блока <code>$</code> относится к jQuery
// Изящно, правда?
})(jQuery);
13
В начале я расценивал повсеместное использование $ в jQuery не более, чем хитроумный
трюк. Но если рассматривать его только в контексте jQuery, то такое решение выглядит очень
гибким, и я с радостью использую сокращение $ и в своем собственном коде.
Выбираем элементы:
Каждый jQuery-оператор начинается с выбора одного или нескольких узлов DOM. Синтаксис
селекторов jQuery (внутренний язык этой библиотеки) является интересным гибридом
спецификаций CSS 1, 2, немного CSS 3, немного XPath и еще малость других расширений. Я не
буду углубляться в детали, просто приведу несколько полезных примеров:
jQuery('div.panel')
Все div'ы с class="panel"
jQuery('p#intro')
Параграф с id="intro"
jQuery('div#content a:visible')
Все видимые ссылки внутри div с id="content"
jQuery('input[@name=email]')
Все поля ввода с name="email"
jQuery('table.orders tr:odd')
Все четные строки в таблице с class="orders"
jQuery('a[@href^="http://"]')
Все внешние ссылки (те, которые начинаются с http://)
jQuery('p[a]')
Все параграфы, в которых есть хотя бы одна ссылка
Наибольший интерес из вышеописанного представляют :visible и :odd, которые являются
специфичными только для jQuery. Стоит также отметить, что выбор атрибутов использует знак
@, что более соответствует XPath нежели CSS 2.
Язык селекторов весьма богат и очень похож на регулярные выражения, так что время,
потраченное на его изучение, окупится сполна.
Чего-нибудь с ними делаем:
Объект, который возвращают селекторы jQuery, является довольно интересным зверем. Он
представляет собой набор DOM-элементов и ведет себя немного как массив: у него есть
свойство length, к его элементам можно получить доступ по индексу и (что более важно)
Firebug расценивает его именно как массив при отображении в своей консоли. Это не более,
чем красивая иллюзия: набор элементов, на самом деле, — это объект jQuery, у которого есть
большое число методов для выбора, изменения или расширения имеющегося набора.
В jQuery имеется три разных категории методов: первые манипулируют со всеми элементами,
которые подходят по шаблону, вторые возвращают значение от первого найденного
элемента, и третьи изменяют саму выборку.
14
Я не буду перечислять все доступные методы (эта можно посмотреть и на visualjquery.com), но
хочу привести некоторые примеры. Если у вас есть Firebug, вы можете попробовать их
самостоятельно: просто нужно воспользоваться закладкой «Вставить jQuery» (
javascript:void(function(){var
s=document.createElement('script');s.src='http://code.jquery.com/jquery-1.1.2.js';
document.getElementsByTagName('head')[0].appendChild(s);}()) ) для загрузки самой библиотеки
для любой страницы, а затем вставить примеры кода в консоль Firebug (прим.: можно и без
Firebug: достаточно загрузить jQuery с помощью указанной ссылки и вызвать приведенные
примеры в адресной строке браузера, не забыв в начале javascript: и какой-либо alert в конце
(чтобы на страницу не выводилось возвращаемое значение)).
jQuery('div#primary').width(300);
Выставляет ширину div id="primary" в 300 пикселей.
jQuery('p').css('line-height', '1.8em');
Выставляет высоту строки в 1.8em для всех параграфов.
jQuery('li:odd').css({color: 'white', backgroundColor: 'black'});
Применяет 2 CSS-правила для каждого пункта списка; заметьте, что функция css() может
принимать объект таблицы стилей вместо двух строк.
jQuery('a[@href^="http://"]').addClass('external').attr('target', '_blank');
Добавляет класс "external" для всех внешних ссылок (тех, что начинаются с http://), затем
добавляет target="_blank", чтобы увеличить различие. В данном примере используется
цепочка вызовов, описанная ниже.
jQuery('blockquote').each(function(el) { alert(jQuery(this).text()) });
Для каждого тега blockquote на странице выводит сообщение (alert) с его текстовым
содержанием (включая HTML-теги).
jQuery('a').html('Нажми здесь!');
Заменяет весь текст в ссылках на странице призывом «Нажми здесь!».
Ниже несколько примеров методов, которые возвращают значение от первого элемента,
найденного по шаблону:
var width = jQuery('div').width();
Какая ширина у первого div на странице?
var src = jQuery('img').attr('src');
Какое значение у атрибута src у первой картинки на странице?
var color = jQuery('h1').css('color');
Какой цвет у первого h1?
Хочу отметить удобную симметрию таких методов: они используются как для выставления
атрибутов (когда принимают 2 аргумента или у передаваемого объекта несколько свойств),
так и для прочтения значений этих атрибутов (если передается только один аргумент). Такая
симметрия используется во всей библиотеке jQuery, что очень сильно облегчает запоминание
API.
Наконец, есть несколько методов, которые изменяют весь набор найденных элементов.
Многие из них также обеспечивают перемещение по DOM-дереву:
jQuery('div').not('[@id]')
15
Возвращает все div, у которых нет атрибута id.
jQuery('h2').parent()
Возвращает все элементы, которые являются непосредственными родителями h2.
jQuery('blockquote').children()
Возвращает все элементы, вложенные в blockquote.
jQuery('p').eq(4).next()
Находит пятый параграф на странице, потом находит следующий элемент (т.е.
непосредственного соседа справа).
jQuery('input:text:first').parents('form')
Находит родительский элемент для формы, которая содержит первое поле input type="text" на
странице. Опциональным параметром для parents() является другой селектор.
Цепочки вызовов:
Команда разработчиков jQuery часто хвастается по поводу поддержки в этой библиотеке
цепочки вызовов, доходя до заявлений типа «jQuery создана для того, чтобы изменить ваш
стиль программирования на JavaScript» прямо на главной странице. Честно говоря, лично я
нахожу эту технику скорее боковым ответвлением, чем дорогой в будущее, но с радостью
заявляю, что вы можете использовать jQuery, избежав длинных цепочек вызовов.
Короче говоря, цепочки можно использовать для нескольких интересных трюков. В
дополнение к использованию набора DOM-выборок вы можете применить jQuery-метод end()
для перемещения по стеку объектов и выхода из текущего контекста. Это немного сложно
объяснить, но когда вы используете метод, который изменяет текущий (объектный) контекст
(например, children() или filter()), вы можете использовать end() чуть позже, чтобы вернуться к
предыдущей выборке. Jesse Skinner приводит хороший пример использования этой
возможности в своем учебнике Делаем разработку на AJAX проще с jQuery:
$('form#login')
// прячем внутри формы все label с классом optional
.find('label.optional').hide().end()
// добавляем красную границу ко всем полям типа password в форме
.find('input:password').css('border', '1px solid red').end()
// добавляем обработчик на событие submit для формы
.submit(function(){
return confirm('Вы действительно хотите отправить данные?');
});
Все это большое преобразование займет всего одну строку. Оно находит форму, находит
некоторые элементы внутри нее, применяет к ним изменения, возвращается обратно к форме
и добавляет к ней обработчик события submit().
Это милая концепция, но никто не заставляет ее использовать, если вы этого не хотите. Я был
просто счастлив визуально разбить свой код с помощью нескольких достаточно
красноречивых имен переменных.
16
Манипулируем с DOM:
В jQuery имеется несколько способов для объемных манипуляций с DOM. Первый является
несколько неожиданным: функция jQuery может принимать в качестве аргумента кусок HTMLкода, который преобразуется в DOM-элемент (фактически, это просто строка):
var div = jQuery('
Some text
');
Можно использовать цепочку вызовов, чтобы добавить атрибуты к div, как только он был
создан:
var div = jQuery('
Some text
').addClass('inserted').attr('id', 'foo');
Теперь добавим его к тегу body:
div.appendTo(document.body)
Или вставим его, используя селектор, в имеющийся код:
div.prependTo('div#primary')
Перехватываем события:
Все JavaScript-библиотеки нуждаются в методах для обработки событий, и jQuery не является
исключением. Как и в случае attr() и css(), методы для событий могут служить двум целям: их
вызов с функцией в качестве аргумента назначает обработчик события, вызов без аргумента
эмулирует возникновение этого события:
jQuery('p').click(function() { jQuery(this).css('background-color', 'red'); });
Выставляем для всех параграфов обработчик клика мыши, по которому они становятся
красными.
jQuery('p:first').click()
Эмулируем клик для первого параграфа на странице.
Похожие функции используются для других событий браузера: mouseover, keyup и т.д. Следует
заметить, что при вызове обработчика событий ключевое слово this ссылается на элемент,
который это событие вызвал; использование jQuery(this) является расхожим приемом, чтобы
вызвать методы jQuery для этого элемента.
Пара функций, относящихся к событиям, заслуживает отдельного упоминания:
jQuery('a').hover(function() {
jQuery(this).css('background-color', 'orange');
}, function() {
jQuery(this).css('background-color', 'white');
});
17
hover() является сокращением для сразу двух событий: onmouseover и onmouseout.
jQuery('p').one('click', function() { alert(jQuery(this).html()); });
one() выставляет обработчик событий, который будет удален после первого своего вызова. В
вышеприведенном примере всем параграфы должны сообщить (alert) свое содержание после
первого клика по ним.
jQuery также поддерживает собственные события через методы bind() и trigger() (подобные
click()). Собственные события могут принимать аргументы, передаваемые при помощи
массива в вызове trigger():
jQuery(document).bind('stuffHappened', function(event, msg) {
alert('Что прозошло: ' + msg);
});
jQuery(document).trigger('stuffHappened', ['Привет!']);
Ненавязчивое (unobtrusive) программирование:
Эта тема мне очень близка. Я по-прежнему верую в то, что лучшие веб-приложения — это те,
которые могут функционировать и при отключенных скриптах, и лучшим методом для
достижения этой цели будет ненавязчивый JavaScript, когда события назначаются элементам
только после того, как вся HTML-страница у пользователя загрузится (для более подробной
информации можно ознакомиться с ненавязчивым программированием и Hijax).
В jQuery присутствует замечательная поддержка этого подхода. Во-первых, парадигма
селекторов для выбора узла является основополагающей как для jQuery, так и для
ненавязчивого программирования в целом. Во-вторых, jQuery обеспечивает решения для
проблемы window.onload, основанной на исследованиях Dean Edwards по поводу работы
события "DOM loaded" для различных браузеров (прим.: подробнее о данной проблеме
можно посмотреть в моем переводе, опубликованном ранее). Вы можете выставить функциюобработчик тогда, когда DOM уже будет к ней готов:
jQuery(document).ready(function() {
alert('DOM готов!');
});
И даже больше, вы можете сократить эту запись, назначив вашу функцию напрямую jQuery():
jQuery(function() {
alert('DOM готов!');
});
jQuery и AJAX:
У jQuery лучший API для работы с AJAX, который я когда-либо видел в больших библиотеках.
Наиболее простая форма AJAX-вызова выглядит следующим образом:
jQuery('div#intro').load('/some/fragment.html');
18
Он выполнит GET-запрос к /some/fragment.html и вставит в div#intro HTML-код, который
получит.
Первый раз, когда я это увидел, то не был сильно впечатлен. Всего лишь красивое
сокращение, но если требуется что-то более сложное, например, вывести индикатор AJAXзагрузки? jQuery предоставляет набор собственных событий (ajaxStart, ajaxComplete, ajaxError
и другие) для использования в случае необходимости. Также в этой библиотеки есть и более
продвинутый API низкого уровня для сложных AJAX-взаимодействий:
jQuery.get('/some/script.php', {'name': 'Simon'}, function(data) {
alert('Сервер ответил: ' + data);
}); // GET-запрос к /some/script.php?name=Simon
jQuery.post('/some/script.php', {'name': 'Simon'}, function(data) {
alert('Сервер ответил: ' + data);
}); // POST-запрос к /some/script.php
jQuery.getJSON('/some.json', function(json) {
alert('JSON выдал: ' + json.foo + ' ' + json.bar);
}); // Возвращает и преобразует ответ от /some.json как JSON
jQuery.getScript('/script.js'); // GET-запрос к /script.js и eval()
Расширения
Рассматривая весь этот набор функций в стандартной поставке, стоит заметить, что ужатый
вариант jQuery занимает всего 20 КБ, и даже еще меньше, если применить архивирование
(.gz). Дополнительная функциональность, выходящая за пределы это поставки, может быть
организована с помощью расширений, которые могут (и так и делают) добавить новые
методы к существующему объекту jQuery. При желании можно выполнить что-то вроде этого:
jQuery('p').bounceAroundTheScreenAndTurnGreen();
Механизм расширений в jQuery обеспечивает задокументированные методы для их
добавления в систему. Простота и удобство их использования привлекли большое сообщество
авторов таких расширений; справочник расширений насчитывает уже более ста примеров.
Еще одной приятной особенностью является возможность добавлять собственные селекторы
так же, как и собственные методы. Расширение moreSelectors добавляет методы типа
div:color(red), который, например, выбирает все div с красным текстом.
19
jQuery в примерах
Для начала Вам понадобиться сам фреймворк, его вы сможете скачать с домашней страницы
проекта http://jquery.com/, затем проинициализировать:
<head>
<script type="text/javascript" src="jquery.js"></script>
</head>
А основные моменты Вам поможет понять следующая диаграмма:
Как получить элемент с помощью jQuery:
Для того чтобы понимать как работает селектор Вам все-же необходимы базовые знания CSS, т.к.
именно от принципов CSS отталкивает селектор jQuery:
$("#header") — получение элемента с id=«header»
$(«h3») — получить все <h3> элементы
$(«div#content .photo») — получить все элементы с классом =«photo» которые находятся в
элементе div с id=«content»
$(«ul li») — получить все <li> элементы из списка <ul>
$(«ul li:first») — получить только первый элемент <li> из списка <ul>
Выдвижная панель
Начнем с простенького примера — слайд-панель, она у нас будет двигаться вверх/вниз по клику
на ссылке (см. пример)
20
Реализуем это следующим образом, по клику на ссылку, у нас будет переключаться её класс
(между «active» и «btn-slide»), а панелька с id=«panel» будет выдвигаться/прятаться. (класс
«active» изменяет позицию фонового изображения, см. CSS файл во вложении).
$(document).ready(function(){
$(".btn-slide").click(function(){
$("#panel").slideToggle("slow");
$(this).toggleClass("active");
});
});
Магические исчезновения:
Этот пример покажет как можно красиво и легко убирать растворять элементы (см. пример):
Когда мы кликаем по картинке <img class=«delete»>, будет найден родительский элемент <div
class=«pane»>, и его прозрачность будет медленно изменяться от opacity= 1.0 до opacity=hide:
$(document).ready(function(){
$(".pane .delete").click(function(){
$(this).parents(".pane").animate({ opacity: "hide" }, "slow");
});
});
Связанная анимация:
Теперь пример посложнее, но он поможет Вам лучше понять jQuery. Всего несколько строк кода
заставят квадрат двигаться, изменять размер и прозрачность. (см. пример):
Line 0: когда прогрузилась страница (DOM готов к манипуляциям)
Line 1: привязываемся к событию click для элемента <a class=«run»>
Line 2: манипулируем элементом <div id=«box»> — уменьшаем его прозрачность до 0.1,
наращиваем позицию left еще на 400px, со скоростью 1200 (milliseconds)
Line 3: затем медленно изменяем следующие параметры: opacity=0.4, top=160px, height=20,
width=20; скорость анимации указывается вторым параметром: «slow», «normal», «fast» или в
миллисекундах
Line 4: затем opacity=1, left=0, height=100, width=100, скорость — «slow»
Line 5: затем opacity=1, left=0, height=100, width=100, скорость — «slow»
Line 6: затем top=0, скорость — «fast»
Line 7: затем slideUp (с дефолтной скоростью анимации — «normal»)
Line 8: затем slideDown, скорость — «slow»
Line 9: возвращаем false для того чтобы браузер не перешел по ссылке
21
$(document).ready(function(){
$(".run").click(function(){
$("#box").animate({opacity: "0.1", left: "+=400"}, 1200)
.animate({opacity: "0.4", top: "+=160", height: "20", width: "20"}, "slow")
.animate({opacity: "1", left: "0", height: "100", width: "100"}, "slow")
.animate({top: "0"}, "fast")
.slideUp()
.slideDown("slow")
return false;
});
});
Гармошка:
Пример реализации «гармошки». (см. пример)
Теперь приступим к разбору полетов:
Первой строчкой мы добавляем класс «active» первому элементу <h3> внутри <div
class=«accordion»> (класс«active» отвечает за позиционирования фонового рисунка — иконки со
стрелочкой). Во второй строчке мы прячем все не первые <p> элементы внутри <div
class=«accordion»>.
Когда происходит клик по заголовку <h3>, для следующего в нём элемента <p> будет применен
эффект slideToggle, затем для всех остальных элементов <p> будет применен эффект slideUp.
Следующие действие изменяет класс заголовка на «active», затем ищем все остальные заголовки
<h3> и убираем у них класс «active»
$(document).ready(function(){
$(".accordion h3:first").addClass("active");
$(".accordion p:not(:first)").hide();
$(".accordion h3").click(function(){
$(this).next("p").slideToggle("slow")
.siblings("p:visible").slideUp("slow");
$(this).toggleClass("active");
$(this).siblings("h3").removeClass("active");
});
});
Анимация для события hover:
Данный пример поможет создать Вам очень красивую анимацию для события hover (см. пример):
Когда Вы наводите мышкой на элемент меню (mouseover), происходит поиск следующего
элемента <em>, и анимируется его прозрачность и расположение:
22
$(document).ready(function(){
$(".menu a").hover(function() {
$(this).next("em").animate({opacity: "show", top: "-75"}, "slow");
}, function() {
$(this).next("em").animate({opacity: "hide", top: "-85"}, "fast");
});
});
Кликабельные блоки:
Этот пример демонстрирует как сделать кликабельным блок с текстом, а не только ссылку (см.
пример):
Создадим список <ul> с классом class=«pane-list» и мы хотим сделать элементы <li>
кликабельными. Для начала привяжемся к событию click для элемента ".pane-list li"; когда
пользователь будет кликать по элементу списка, наша функция произведет поиск тэга <a> и
сделает редирект на страницу указанную в атрибуте href.
$(document).ready(function(){
$(".pane-list li").click(function(){
window.location=$(this).find("a").attr("href"); return false;
});
});
Складывающиеся панельки:
Ну а теперь чуть-чуть скомбинируем предыдущие примеры и создадим ряд складывающихся
панелек (см. пример)
- скрываем все элементы <div class=«message_body»> после первого.
- скрываем все элементы <li> после пятого
- клик по <p class=«message_head»> — вызывает метод slideToggle для следующего
элемента <div class=«message_body»>
- клик по <a class=«collpase_all_message»> — вызывает метод slideUp для всех <div
class=«message_body»>
- клик по <a class=«show_all_message»> — скрывает элемент, и отображает <a
class=«show_recent_only»>, так же вызывается метод slideDown для всех <li> послепятого
- клик по <a class=«show_recent_only»> — скрывает элемент, и отображает <a
class=«show_all_message»>, так же вызывается метод slideUp для всех <li> после пятого
$(document).ready(function(){
//hide message_body after the first one
$(".message_list .message_body:gt(0)").hide();
//hide message li after the 5th
$(".message_list li:gt(4)").hide();
//toggle message_body
$(".message_head").click(function(){
23
$(this).next(".message_body").slideToggle(500)
return false;
});
//collapse all messages
$(".collpase_all_message").click(function(){
$(".message_body").slideUp(500)
return false;
});
//show all messages
$(".show_all_message").click(function(){
$(this).hide()
$(".show_recent_only").show()
$(".message_list li:gt(4)").slideDown()
return false;
});
//show recent messages only
$(".show_recent_only").click(function(){
$(this).hide()
$(".show_all_message").show()
$(".message_list li:gt(4)").slideUp()
return false;
});
});
Галерея изображений:
Простейший пример реализации галереи, без перезагрузки страницы. (см. пример)
Для начала добавим тэг <em> в заголовки <h2>
По клику на изображения в <p class=thumbs> выполняем следующие действия:
- сохраняем значение атрибута «href» в переменной «largePath»
- сохраняем значение атрибута «title» в переменной «largeAlt»
- заменяем в элементе <img id=«largeImg»> значение атрибута «scr» и «alt» значениями из
переменных «largePath» и «largeAlt»
- так же присваиваем элементу «h2 em» значение из «largeAlt»
$(document).ready(function(){
$("h2").append('<em></em>')
$(".thumbs a").click(function(){
var largePath = $(this).attr("href");
var largeAlt = $(this).attr("title");
$("#largeImg").attr({ src: largePath, alt: largeAlt });
$("h2 em").html(" (" + largeAlt + ")"); return false;
24
});
});
Стилизируем ссылки:
Большинство нормальных браузеров легко понимают когда мы хотим добиться от них стилизации
ссылок для различного типа файлов, для это цели можно использовать следующее CSS правило:
a[href $='.pdf'] {… }. Но как обычно IE6 отличается умом и сообразительностью, по этой причине
будем ставить ему костыли используя jQuery. (см. пример)
Для начала добавим класс для каждой ссылки, в соответствии с типом файла.
Затем выберем все элементы <a> которые не содержат ссылки на
"http://www.webdesignerwall.com" и не начинающиеся на "#" в «href», затем добавим им класс
«external» и устанавливаем target= "_blank".
$(document).ready(function(){
$("a[@href$=pdf]").addClass("pdf");
$("a[@href$=zip]").addClass("zip");
$("a[@href$=psd]").addClass("psd");
$("a:not([@href*=http://www.webdesignerwall.com])").not("[href^=#]")
.addClass("external")
.attr({ target: "_blank" });
});
25
Интересные Примеры Применения jQuery
-
Затемнение Фона
Страничка Авторизации
Меню
26
Dojo
Тип: JavaScript toolkit
Разработчик: Dojo Foundation
Написана на: JavaScript
ОС: Кроссплатформенное
Версия: 1.3.2 — 15 июля, 2009
Лицензия: BSD License and the Academic Free License
Сайт: http://dojotoolkit.org
Dojo (доджо) — свободная модульная библиотека JavaScript. Разработана с целью упростить
ускоренную разработку основанных на JavaScript или AJAX приложений и сайтов. Разработка
библиотеки была начата Алексом Русселом в 2004 году. Библиотека находится под двойной
лицензией: BSD License и Academic Free License. Dojo Foundation — некоммерческая организация,
созданная для продвижения Dojo. Dojo используется в Zend Framework, начиная с версии
Элементы интерфейса:
Элементы интерфейса dojo — это пакеты, сформированные из компонентов: JavaScript-кода,
разметки HTML и CSS. Они могут быть использованы для добавления различных интерактивных
возможностей к сайту:
- меню, закладок, всплывающих подсказок;
- селекторов даты, времени; часов;
- сортируемых таблиц, динамических диаграмм, векторной 2D графики;
- элементов интерфейса «дерево», с возможностями drag-and-drop;
- Rich Text Editor;
- различных HTML-форм с возможностью проверки ввода пользователя;
- анимированных эффектов, и возможностей построения своих собственных эффектов.
Асинхронная связь:
Одной из важных особенностей AJAX-приложений является асинхронная связь между браузером
и сервером: благодаря обмену информацией, вид страницы обновляется без перезагрузки
страницы. Обычно это делается с помощью JavaScript-объекта XMLHttpRequest. Dojo
предоставляет класс-«обертку» (dojo.io.bind), который позволяет абстрагироваться от различных
реализаций XMLHttpRequest в браузерах, в том числе работающих через iframe. Используя эту
технологию, становится просто создавать приложения, передающие вводимые пользователем
данные на сервер. Сервер может ответить на это определённым JavaScript’ом и обновить вид
страницы.
27
JavaScript-программирование:
Абстракция от среды выполнения; Dojo предоставляет возможности для определения и работы с
различными веб-браузерами и другими средами выполнения JavaScript (такими как Rhino).
Dojo поддерживает разработку и распространение собственных элементов интерфейса и
включает в себя библиотеку утилит для манипуляций с DOM.
Система событий, которая позволяет реагировать не только на события DOM, но и на другие
события, такие как, например, вызов определённой функции. Это позволяет в большей степени
использовать аспектно-ориентированное программирование.
Абстрагированный интерфейс для манипулирования JavaScript-классами, предоставляющий
возможность наследования и позволяющий расширять существующие классы с помощью набора
функций, вместо того, чтобы управлять непосредственно прототипами объектов.
Система пакетов:
Dojo предоставляет систему пакетов, облегчающую модульную разработку функциональности
пакетов и суб-пакетов. «Загрузочный» скрипт инициализирует набор иерархических пакетных
пространств имён: «io», «event», и т. д. — внутри корневого пространства имён «dojo». После
инициализации корневого пространства имён, любой пакет Dojo может быть загружен (с
помощью XMLHttpRequest или сходного способа) с помощью специальных функций загрузочного
скрипта. Возможно также инициализировать дополнительные пространства имён внутри или вне
пространства имён «dojo», что позволяет расширять dojo или разрабатывать закрытые
пространства имён для сторонних библиотек и приложений. Пакет dojo может содержать
множество файлов. Любой пакет или файл могут определять зависимости от других файлов или
пакетов. Когда такой пакет загружается, все необходимые зависимости подгружаются
автоматически.
Хранилище на стороне клиента:
Dojo предоставляет абстрактное хранилище данных на стороне клиента — Dojo Storage. Dojo
Storage позволяет веб-приложениям хранить мегабайты данных на стороне клиента, постоянно и
защищённо, с правами пользователя. Это работает для всего существующего веба, включая
Internet Explorer, Mozilla Firefox и Safari. Будучи включённой в веб-страницу, Dojo Storage
определяет лучший метод хранения информации. Для Firefox 2 используется «родной»
интерфейс, в других браузерах используется скрытый Flash-апплет. Так как Flash 6+ установлен на
около 95 % компьютеров по всему миру,[1] этот механизм хранения информации доступен
большинству. Для тех веб-приложений, которые загружаются из файловой системы (то есть
URL=file://…), Dojo Storage будет прозрачно использовать XPCOM в Firefox или ActiveX в Internet
Explorer. Программист, использующий dojo, не обязан задумываться о тонкостях реализации — он
просто получает в свои руки абстрагированные методы put() и get().
28
Общий взгляд на Dojo Toolkit
Если “аякс” для вас не пустой звук или отзвук эпохи веб 2.0, но и нечто большее — работа,
увлечение или стиль мышления, то кроме инструментов, что у всех на слуху, jQuery или Prototype,
вы заинтересованы в более мощных решениях. И, желательно, пригодных как для простого
“украшательства” веб-страницы, так и для разработки серьезного корпоративного приложения.
Одним из флагманов AJAX-индустрии, без сомнения, является Dojo Toolkit, несмотря на то, что в
широкой ИТ-прессе о нем не говорят столько же, как о других решениях. А мы вот поговорим.
И так, Dojo Toolkit — это AJAX фреймворк, развиваемый группой Dojo Fundation (у которой есть
еще немало интересных проектов) и нацеленный на нишу серьезных проектов, где вам может
потребоваться все — от обычных функций манипуляций с DOM до работы с Jabber-сервером. При
этом, Dojo пытается сохранить свою основу, обладая одним из самых компактных и мощных
базовых наборов классов (27 Кб в сжатом варианте). Все остальные функциональности доступны
опционально, поэтому не стоит пугаться дистрибутива размером больше десятка Мб. Начать
использовать Dojo в своем проекте можно очень быстро, просто подключив один единственный
файл, или даже не копируя ничего на сервер, ведь Dojo доступен через AOL СDN. В будущем, если
где-то понадобится новая функциональность, используя встроенную модульную структуру и
динамическую загрузку модулей, вы просто расширяете свое приложение шаг за шагом.
Поскольку знание конкретных функций вам особой пользы не принесет, я сосредоточусь далее на
описании базовой архитектуры и возможностей фреймворка. Отмечу также, что в большинстве
случаев в скобках я буду указывать прямые имена компонент, их можно просто скопировать в
вызов функции dojo.require для подключения к вашему проекту.
На плечах гигантов…
Сначала выделим три основные момента, на которые опирается весь Dojo.
Модульная архитектура и динамический загрузчик модулей, подгружающий указанные
компоненты и разрешающий зависимости. Достаточно вызвать одну функцию dojo.require() и
необходимые компоненты будут доступны на странице. Однако учитывайте, что такой подход
значительно увеличивает количество HTTP-запросов от страницы. Использование загрузчика
очень упрощает те проекты, где на разных страницах используются разные возможности, вместе с
тем, если у вас монолитное приложение, где загружается только одна страница, или же вы
используете AIR-платформу, лучше соберите собственную сборку со всеми модулями в виде
одного файла. А вот для веб-сайтов, которые сначала экспериментируют с AJAX, добавляя
незначительные возможности, вроде формы авторизации, и только потом постепенно заменяют
все на новомодные “штучки”, загрузчик позволит пользователям не чувствовать замедления
работы и получать все возможности только там, где они в реальности используются. К слову, в
одном из пользовательских расширений для уже описываемой нами библиотеки ExtJS, также
реализована очень схожая технология под названием $JIT, однако в Dojo это введено на уровне
основы архитектуры, тогда как все другие библиотеки требуют ручной обработки для достижения
того же эффекта.
29
Смешанная модель описания виджетов. Разработчики Dojo явно тяготеют к описанию интерфейса
при помощи специальных атрибутов HTML-тегов, а не напрямую в JS-коде. В ранних версиях
библиотеки (ветка 0.4, с которой я как раз начинал освоение библиотеки) была доступа именно
декларативная модель, когда в HTML-коде разработчик при помощи атрибутов описывал форму, а
после загрузки в браузере движок Dojo заменял описания на отрендеренные элементы. Позже,
после кардинальной смены архитектуры и перехода на ветку 0.9, а затем и в 1.х оказалось, что
программная модель привычнее и, что важнее всего, быстрее, особенно в условиях сложных
форм. Да и отладка кажется более простой, а возможности шире.
Но работа с специальной разметкой не исчезла, как и прежде, вы можете использовать
специальные конструкции вида dojoType=”dijit.Declaration” widgetClass=”test.TextField”
defaults=”{defaultText:”,width:200}. Вообще-то говоря, декларативный подход упрощает работу,
если у вас одно-два поля, тогда задержки на обработку ничтожны. Но вот сопровождать проект с
сотнями форм, описанными в виде смешанного HTML-кода, Dojo-разметки и разбросанного
посреди этого месива JS-скриптов, очень и очень сложно. Сейчас все виджеты реализованы в
качестве встроенных классов (подсистема Dijit), однако система декларативного описания
присутствует и вынесена в отдельный пакет, dijit.Declaration (не спутайте с dojo.declare, который
служит для расширения классов), который можно использовать в любой момент после загрузки
страницы.
Ранее я считал, что именно декларативность была самым узким моментом в архитектуре Dojo, ее
сложность и скудность компонент для построения интерфейса, сосредоточенность больше на
AJAX-замене традиционных форм, позволила другим фреймворкам в визуальном плане
опередить его, хотя в последних релизах 1.х веток Dojo активно развивается и совершенствуется,
так что конкуренция тому же ExtJS намечается серьезная.
Трёхкомпонентная архитектура — по моему, самое интересное и важное. Начать надо с того, что
уже с первых релизов (ветка 0.3 и 0.4, в далеких 2003-2004 годах) команда Dojo пыталась
реализовать все, что только возможно было сделать с помощью Ajax и JavаScript. По сути, во
многих областях Dojo является первопроходцем, продвинувшим теперь обычные технологии в
массы. Это касается идеи реализовать локальное хранилище данных (dojo.Storage с
использованием всех доступных вариантов хранения, начиная от cookie и заканчивая Flashмодулем и Google Gears), различные варианты доступа к данным (как кроссбраузерные так и
обычные, через XMLHTTPRequest объект, IFrame, ScriptTag и даже Java-апплет), встроенный
дебаггер, позже научившийся работать с Firebug-ом, рисование графики, в том числе и 3D,
реализация RPC и многое-многое другое.
К сожалению, это сказывалось на производительности, да и не все компоненты были одинаково
отлажены, поэтому в одном глобальном объекте dojo.* были сосредоточены как ранние
экспериментальные разработки, так и стандартные отлаженные вещи, составляющие основу
основ любого фреймворка (работа с DOM-деревом, стилями, ввод/вывод, обработка строк, дат и
т.п.). Поэтому после глобального редизайна всего проекта, который произошел одновременно со
сменой версий и подходом вплотную к заветной версии 1.0, весь пакет был разделен на три,
функционально совершенно разные, но дополняющие друг друга в любом проекте, части.
Dojo/Dijit/Dojox
30
В пакет Dojo вошли все базовые функции, отлаженные и проверенные, необходимые в
подавляющем большинстве приложений. Для работы с данными (вообще) сделан целый
унифицированный API чтобы отвлечься от нюансов получения и хранения (dojo.data.api.*),
отдельно в утилиты вынесены методы обработки вроде сортировок или фильтров
(dojo.data.util.*). Серьезно подошли разработчики к работе с датой и временем, предоставляя все
необходимые функции расчета разности и суммы дат, локализации и календарей (dojo.date.*).
Стремясь предоставить веб-приложениям функциональные возможности и поведенческие
реакции обычных десктопных приложений, необходимо реализовать функции Drag&Drop, к чему
основательно подошли в Dojo, выделив все, связанное в dojo.dnd.*. Контролю со стороны
программы поддаются все стадии от выбора элемента, процесса перетаскивания и бросание —
все конфигурируется и программируется так, как надо разработчику. От этого модуля сильно
зависит, в основном, только графическая подсистема виджетов Dijit (о которой ниже).
Единственное, что меня смущает — почему среди IO пакета, который отвечает за работу с
сервером и кроссдоменность, в базовом пакете dojo.io оставлено только, по моему, не самые
востребованные Iframe и ScriptTag, остальные транспорты вынесены в dojox (пакет
экспериментальных расширений). А стандартные возможности по коммуникации через
XMLHTTPRequest (то самое, что часто и подразумевают под словом AJAX) вынесено в самый
корень в виде функций объекта dojo (dojo.xhr и другие). Это немного портит впечатление о
целостности библиотеки.
Об остальных стандартных уже функциональностях я даже не буду упоминать отдельно — они
конечно же есть. И расширения стандартных объектов языка, и реализация наследования и
расширения прототипов, работа с DOM-деревом и CSS (доступно два варианта CSS-движков —
собственный Acme и Sizzle, который выделен как самостоятельная разработка и, по некоторых
тестах, достаточно быстр), базовые визуальные эффекты, определения типа переменной,
встроенный механизм конфигурирования (djConfig и dojo.config), определение браузера и
платформы — это все есть и по возможностям более-менее аналогично всем остальным
фреймворкам.
Все компоненты для построения интерфейсной части веб-приложения были выделены в пакет
Dijit, который сосредоточился на виджетах. Конечно, он не самостоятельный — требует для
работы основной пакет Dojo, сам же добавляет компоненты, замещающие стандартные элементы
форм (dijit.form) и добавляющие расширенные возможности, например, кнопки с встроенным
меню и т.п. Глобальную разметку всей страницы можно сделать при помощи ряда компонентов
dijit.layout (в принципе, все лейауты стандартные и очень схожие с теми, что мы видели в ExtJS). В
дополнение предоставляется собственный WYSIWYG-редактор текстов (dijit.Editor), а также
достаточно большой набор других компонентов, которые часто используются для реализации
серьезных интерфейсов — тулбары (dijit.Toolbar), динамические всплывающие подсказки
(серьезно, очень полезная вещь), индикатор прогресса (да, то, что в ExtJS, как основном
конкуренте Dijit, появилось лишь недавно). Конечно, не обошлось без уже почти канонического
виджета — дерева (dijit.Tree), расширенного разными моделями хранения и получения данных
(здесь пригодится как раз унифицированный слой для работы с данными из dojo.data.api).
31
И, наконец, самое интересное — пакет экспериментальных решений DojoX, где собраны
интересные, специализированные или просто не до конца отлаженные модули. Зачастую здесь
можно встретить такие решения, что раньше сложно было мечтать реализовать это при помощи
JavaScript. Тем не менее, несмотря на нестабильный статус, некоторые компоненты из DojoX очень
пригодятся даже в обычных проектах, другие же могут понадобиться в очень особенных случаях
уникальных проектов. Например, в Dojox одним из первых появился собственный порт
библиотеки аналитики Google Analytics, а значит вы безо всяких сложностей можете собирать
статистику даже в полностью Ajax приложениях.
Сильная привязанность разработчиков к протоколу Atom видна невооруженным взглядом —
работе с RSS, Atom и GData отведено большое количество компонент и они обладают широкой
функциональностью. Если вспомнить, что Atom это не только трансляция лент обновления блога,
то можно реализовать множество различного функционала в приложении, основной которого
будет Atom. Обновление каких-либо частей сайта, оповещения и т.п., простой RSS веб-ридер и
агрегатор лент можно реализовать сразу буквально парой строк, так как в DojoX есть для этого не
только все составные части, но и сразу целый компонент (dojox.atom.widget.FeedViewer). Вам надо
вставлять мультимедийные ролики в проект и управлять воспроизведением? Не проблема, не
нужно искать сторонние компоненты — dojox.av.FLAudio/Video реализует все основные
возможности флеш-плееров с программным управлением, скрывая нюансы JS<>Flash
коммуникации. В Todo компонента нас ожидают еще более интересные вещи, вроде глобального
регулятора громкости, работы с ID3 тегами, перемотка, кроссдоменность и многое другое.
Сильной стороной DojoX является все, что связанно с рисованием и эффектами — мощнейший
компонент двух и трехмерных графиков (dojox.charting.*), а также отдельно компоненты
рисования, спецэффектов и 3D (dojox.fx, dojox.gfx, dojox.gfx3d). В зависимости от возможностей
среды, используется или рендеринг через SVG, VML или MS Silverlight (что достаточно уникально).
Из интересного отмечу:
встроенную поддержку CometD (конечно, а как же, если технология создана и поддерживается
как раз самим разработчиком),
унифицированный интерфейс для встраивания любых объектов на страницу (Flash, Quicktime, а
также API для работы с тегом Object),
реализацию различных методов работы с данными (компрессия LZW, Base64, Splay, easy64,
криптография и хеширование MD5, SHA1),
собственный компонент унифицирующий взаимодействие с внедренными Flash-объектами (берет
на себя все функции вроде проверки версии, вызов методов и передача параметров).
DojoX расширяет и виджеты, добавляя множество элементов к формам (dojox.form), реализует
очень мощный и, вероятно, главный “аяксовый” компонент — таблицу для отображения данных
(dojox.grid) и множество встроенного функционала для их обработки, таймеры, дополнительные
объекты вроде очереди или связанных списков, офлайн хран
Возможно, многие уже читали статьи из серии jQuery для начинающих, да вот с некоторых пор
меня заинтересовал еще один JavaScript фреймворк, и зовется он Dojo Toolkit.
32
Dojo в Примерах
Подключение:
Ну, для начала Вам понадобится сам фреймворк, его вы сможете скачать с домашней страницы
проекта, затем подключаем его одним из следующих способов:
Используя локальный файл:
<script type="text/javascript" src="js/dojo/dojo.js" djConfig="parseOnLoad:true,
isDebug:true"></script>
Данная запись аналогична предыдущей:
<script type="text/javascript">
var djConfig = {
isDebug:true,
parseOnLoad:true
};
</script>
<script type="text/javascript" src="js/dojo/dojo.js"></script>
Dojo так же доступен на следующих хостах:
<!-- AOL -->
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.3/dojo/dojo.xd.js"></script>
<!-- Google -->
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js"></script>
Селекторы:
Перейдем к поиску элементов, для этой цели есть следующие функции:
- самый простой вариант - получить елемент по его Id
var element = dojo.byId('elementId');
- так же можем выбрать несколько элементов, используя селекторы из CSS3, - возвращается
объект NodeList
var elements = dojo.query('.elementsClass')
Список поддерживаемых CSS селекторов можно найти в документации по функции dojo.query
(docs.dojocampus.org).
События:
Для работы с событиями в Dojo используются функции dojo.connect и dojo.disconnect – для
добавления и удаления обработчиков соответственно, приведу простой пример:
33
// некая функция
function update() {
console.log('click!');
}
// и некоторый элемент
var obj = dojo.byId('someId');
// вешаем обработчик
var link = dojo.connect(obj, "onclick", null, "update");
// или
dojo.connect(obj, "onclick", "update");
// или используя анонимную функцию
dojo.connect(obj, "onclick", function update() { console.log('click!'); });
// убираем наш обработчик
dojo.disconnect(link);
Функция connect так же поддерживается NodeList’ом:
// переделаем чуть-чуть предыдущий пример
dojo.query('.someClass').connect("onclick", function update() { console.log('click!'); });
Примечание: dojo одинаково понимает события click и onclick
Простые примеры:
Теперь перейдем непосредственно к работе, начнем с события, которое оповестит нас о
завершении строительства DOM’а:
// создаем функцию init и вызваем ее по событию OnLoad
var init = function(){
console.log("DOM построен...");
};
dojo.addOnLoad(init);
// и/или используем анонимную функцию
dojo.addOnLoad(function(){
console.log("...спасибо");
});
Тривиальная задача – организация “зебры” из некой таблицы:
// таблица c "id=tabular_data" берем ее "tbody"
// (используем селектор ">" дабы выбрать лишь нужные элементы, исключая подтаблицы)
// и каждому нечетному элементу "tr" добавляем класс "odd"
dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
Изменение атрибутов, классов и стилей объектов:
// получаем атрибут title
dojo.attr("Id/Node", "title"); // title="bar"
// устанавливаем атрибут title
34
dojo.attr("Id/Node", "title", "A new Title");
// устанавливаем несколько атрибутов
dojo.attr("Id/Node", {
"tabindex": 2, // add to tab order
"onclick": function(e) {
// add a click event to this node
}
});
// добавляем класс anewClass
dojo.addClass("Id/Node", "anewClass");
// удаляем класс anewClass
dojo.removeClass("Id/Node", "anewClass");
// переключатель класса (нет - добавит, есть - удалит)
dojo.toggleClass("Id/Node", "anewClass");
// работает так же и с NodeList (т.е. массивом элементов)
dojo.query(".selector").toggleClass("anewClass");
// вернет объект стиля
dojo.style("Id/Node");
// вернет значение opacity
dojo.style("Id/Node", "opacity");
// установит значение opacity=0.5
dojo.style("Id/Node", "opacity", 0.5);
// изменяем несколько свойств
dojo.style("Id/Node", {
"opacity": 0.5,
"border": "3px solid black",
"height": "300px"
});
// работает так же и с NodeList (т.е. массивом элементов)
dojo.query(".selector").style({
"opacity": 0.5,
"border": "3px solid black",
"height": "300px"
});
Примечание: если какая-либо функция требует указания node в качестве параметра объекта (см.
dojo.animateProperty), значит она не сможет переварить NodeList, даже если там содержится лишь
один объект, конечно, это логично, но после jQuery – немного напрягает
Выдвижная панель
35
Начнем с простенького примера – слайд-панель, она у нас будет двигаться вверх/вниз по клику на
ссылке (см. пример)
Реализуем это следующим образом, по клику на ссылку, у нас будет переключаться её класс
(между “active” и “btn-slide”), а панелька с id=”panel” будет выдвигаться/прятаться. (класс “active”
изменяет позицию фонового изображения, см. CSS код).
dojo.addOnLoad(function(){
dojo.query(".btn-slide").connect("onclick",function(e){
dojo.stopEvent(e);
var panel = dojo.byId('panel');
if (dojo.style(panel, 'height') != 0) {
dojo.anim(panel, {height:0}).play();
} else {
dojo.anim(panel, {height:200}).play();
}
dojo.toggleClass(this, "active");
});
});
Магические исчезновения:
Этот пример покажет, как можно красиво и легко убирать растворять элементы (см. пример):
Когда мы кликаем по картинке <img class=”delete”>, будет найден родительский элемент <div
class=”pane”>, и его прозрачность будет медленно изменяться от opacity= 1.0 до opacity=0 – для
этого воспользуемся функцией dojo.fadeOut:
dojo.addOnLoad(function(){
dojo.query(".pane .delete").connect("onclick",function(){
dojo.fadeOut({node:this.parentNode, duration:1000, onEnd: dojo.partial(dojo.style, this.parentNode,
"display", "none")}).play();
// по завершению - прячем элемент display:none
});
});
Связанная анимация #1:
Теперь пример посложнее, но он поможет Вам лучше понять Dojo. Всего несколько строк кода
заставят квадраты двигаться, изменять размер и прозрачность. (см. пример):
// когда прогрузилась страница (DOM готов к манипуляциям)
dojo.addOnLoad(function(){
// привязываемся к событию click для элемента с class="down"
dojo.query(".down").connect("onclick",function(e){
e.preventDefault(); // удаляем событие по умолчанию
// бежим по всем найденым элементам с class="box"
36
dojo.query('.box').forEach(function(el){
dojo.anim(el,{ top: dojo.style(el,'top')+160 // наращиваем позицию top на 160px
, left: dojo.style(el,'left')+160
// наращиваем позицию left на 160px
, width: dojo.style(el,'width')+10 // наращиваем ширину на 10px
, height: dojo.style(el,'height')+10 // наращиваем высоту на 10px
, opacity: dojo.style(el,'opacity')-0.2 // уменьшаем opacity на 0.2
}, 1000).play(); // запускаем анимацию, указывая время в 1000ms = 1s
});
});
});
// привязываемся к событию click для элемента с class="up", далее все аналогично
dojo.query(".up").connect("onclick",function(e){
e.preventDefault();
dojo.query('.box').forEach(function(el){
dojo.anim(el,{ top: dojo.style(el,'top')-160
, left: dojo.style(el,'left')-160
, width: dojo.style(el,'width')-10
, height: dojo.style(el,'height')-10
, opacity: dojo.style(el,'opacity')+0.2}, 1000).play();
});
});
});
});
Примечание: в Opera 9.63 неправильно определяется первоначальное положение элементов
Связанная анимация #2
А теперь будем анимировать каждый box по отдельности. Для движения “вниз” будем
использовать dojo.fx.chain – и вся анимация будет выполняться последовательно. Для движения
вверх будем использовать dojo.fx.combine – анимация будет происходить параллельно (см.
пример):
dojo.require("dojo.fx");
dojo.addOnLoad(function(){
// выбираем каждый элемент по отдельности
var box1 = dojo.query('.box:nth-child(1)')[0];
var box2 = dojo.query('.box:nth-child(2)')[0];
var box3 = dojo.query('.box:nth-child(3)')[0];
// вешаем обработчик на "down"
dojo.query(".down").connect("onclick",function(e){
e.preventDefault();
dojo.fx.chain(
[
dojo.animateProperty({node:box1,properties:{top: dojo.style(box1,'top')+160,duration:1000}}) //
изменяем позицию первого квадрата
37
,dojo.animateProperty({node:box1,properties:{left: dojo.style(box1,'left')+160,duration:1000}})
,dojo.animateProperty({node:box2,properties:{top: dojo.style(box2,'top')+160,duration:1000}}) //
изменяем позицию второго квадрата
,dojo.animateProperty({node:box2,properties:{left: dojo.style(box2,'left')+160,duration:1000}})
,dojo.animateProperty({node:box3,properties:{top: dojo.style(box3,'top')+160,duration:1000}}) //
изменяем позицию третьего квадрата
,dojo.animateProperty({node:box3,properties:{left: dojo.style(box3,'left')+160,duration:1000}})
]
).play();
});
// вешаем обработчик на "up"
dojo.query(".up").connect("onclick",function(e){
e.preventDefault();
dojo.fx.combine(
[
dojo.animateProperty({node:box1,properties:{top: dojo.style(box1,'top')-160,duration:1000}})
,dojo.animateProperty({node:box1,properties:{left: dojo.style(box1,'left')-160,duration:1000}})
,dojo.animateProperty({node:box2,properties:{top: dojo.style(box2,'top')-160,duration:1000}})
,dojo.animateProperty({node:box2,properties:{left: dojo.style(box2,'left')-160,duration:1000}})
,dojo.animateProperty({node:box3,properties:{top: dojo.style(box3,'top')-160,duration:1000}})
,dojo.animateProperty({node:box3,properties:{left: dojo.style(box3,'left')-160,duration:1000}})
]
).play();
});
});
Примечание: функции dojo.fx.chain и dojo.fx.combine – работают с dojo.animateProperty и не
понимают dojo.anim
Гармошка #1:
Пример реализации “гармошки”. (см. пример)
Теперь приступим к разбору полетов:
// подключаем dojo.fx и dojo.NodeList-fx
dojo.require("dojo.fx");
dojo.require("dojo.NodeList-fx");
dojo.addOnLoad(function(){
// прячем все параграфы (можете использовать CSS, но будет не так интересно)
dojo.query(".accordion div p").style({ display:'none' });
// вешаемся на событие onclick
dojo.query(".accordion h3").connect("onclick",function(){
// получаем индекс текущего элемента в предке
var index = dojo.query(".accordion h3").indexOf(this);
// прячем все параграфы кроме текущего
38
dojo.forEach(dojo.query(".accordion div").query("p"), function(item, idx){
if (idx != index && dojo.style(item, 'display') != 'none') {
dojo.query(item).wipeOut().play();
}
});
// получаем необходимый нам <p>
var p = dojo.query("p", this.parentNode);
// проверяем наличие класса active
if (dojo.hasClass(this,'active')) {
dojo.removeClass(this,'active'); // удаляем класс active
p.wipeOut().play(); // прячем <p>
} else {
dojo.addClass(this,'active'); // добавляем класс active
p.wipeIn().play(); // показываем <p>
}
});
});
Приведу сразу код HTML, чтобы далеко не ходить:
<div class="accordion">
<div>
<h3>Question One Sample Text</h3>
<p>Lorem ipsum dolor sit amet...</p>
</div>
<div>
<h3>This is Question Two</h3>
<p>Lorem ipsum dolor sit amet...</p>
</div>
<div>
<h3>Another Questio here</h3>
<p>Lorem ipsum dolor sit amet...</p>
</div>
</div>
Примечание: наткнулся на различное поведение селектора :first-child в Dojo и jQuery в
приведенном примере. Dojo не находит элемент <p>, т.к. он не является first-child’ом
относительно парента, jQuery же находит, т.к. он является первым потомком <p>. Кто прав я не
могу сказать точно, но браузеры считают, что таки Dojo… (селектор :nth-child в Dojo так же считает
вхождения потомков иначе).
Еще брошу камень в огород Dojo – кода в jQuery значительно меньше, и структура документа
проще (хотя, возможно, я просто не знаю, как получить в Dojo следующий элемент в доме, имея
лишь текущий node).
39
Так же не совсем понятно, почему при переборе dojo.query(”.accordion div p”).forEach(…) элементы
идут в обратном порядке (лишь в webkit’е правильно), при этом dojo.query(”.accordion
div”).query(”p”).forEach(…) работает верно во всех браузерах.
Гармошка #2:
Этот пример схож с предыдущим, лишь отличается тем, что мы указываем открытую по
умолчанию панельку. (см. пример)
В CSS у нас указано для всех элементов <p> display:none. Теперь нам необходимо открыть третью
панель. Для этого мы можем написать следующий код:
dojo.addOnLoad(function(){
// выбираем третий div и добавляем к заголовку класс active
dojo.query(".accordion div:nth-child(3) h3").addClass("active");
// работает лишь с webkit'ом
// dojo.query(".accordion div:not(:nth-child(3)) p").style({ display:'none' });
var els = dojo.query(".accordion div p"); // выбираем все параграфы
els.splice(2,1); // вырезаем третий (тут отсчет с нуля идет)
els.style({ display:'none' }); // все остальные скрываем
/* ... */
});
Примечание: Селектор вида div:not(:nth-child(2)) заработал лишь в Safari и Chrome. Кстати,
реализовал я анимацию в данном примере иначе – использовал dojo.fx.Toggler – достаточно
забавная вещь, правда я так и не понял как правильно переключаться между show() и hide() (к
примеру, если мне хочется создать функцию аля slideToggle в jQuery)
Анимация для события hover #1:
Данный пример поможет создать Вам очень красивую анимацию для события hover (надеюсь, Вы
знаете что это?), (см. пример):
Когда Вы наводите мышкой на элемент меню (mouseover), происходит поиск следующего
элемента <em> и анимируются его прозрачность и расположение:
dojo.addOnLoad(function(){
// вешаемся на событие onmouseover
dojo.query(".menu a").connect("onmouseover",function(){
// ищем em элемент и анимируем его
var em = dojo.query('em', this.parentNode);
dojo.animateProperty({ node: em[0], duration:500,
properties: {
opacity: { start: 0, end: 1 },
// прозрачность
top: { start:-85, end:-70, unit:"px" } // располложение
},
beforeBegin:function() { // перед началом анимации надо выставить правильно св-во display
40
em.style({display:'block'});
}
}).play();
});
// вешаемся на событие onmouseout
dojo.query(".menu a").connect("onmouseout",function(){
var em = dojo.query('em', this.parentNode);
dojo.animateProperty({ node: em[0], duration:300,
properties: {
opacity: { start: 1, end: 0 },
top: { start:-70, end: -85, unit:"px" }
},
onEnd:function() {
em.style({display:'none'});
}
}).play();
});
});
Анимация для события hover #2:
Данный пример чуть-чуть посложней предыдущего примера: для формирования подсказки
используется атрибут title (см. пример)
Первым делом добавим тэг <em> в каждый элемент <a>. Когда произойдет событие mouseover,
мы возьмем текст из атрибута “title” и вставим его в тэг <em>:
dojo.addOnLoad(function(){
dojo.query(".menu2 a").connect("onmouseover",function(){
// создаем елемент em и закидываем его в DOM
var em = dojo.query(dojo.create('em', {innerHTML:dojo.attr(this, 'title')})).place(this.parentNode);
// анимация
dojo.animateProperty({ node: em[0], duration:500,
properties: {
opacity: { start: 0, end: 1 },
top: { start:-85, end:-70, unit:"px" }
},
beforeBegin:function() {
em.style({display:'block'});
}
}).play();
});
dojo.query(".menu2 a").connect("onmouseout",function(){
var em = dojo.query('em', this.parentNode);
dojo.animateProperty({ node: em[0], duration:300,
properties: {
41
opacity: { start: 1, end: 0 },
top: { start:-70, end: -85, unit:"px" }
},
onEnd:function() {
// удаляем элемент из DOM'a
em.orphan();
}
}).play();
});
});
Кликабельные блоки:
Этот пример демонстрирует, как сделать кликабельным блок с текстом, а не только ссылку (см.
пример):
Создадим список <ul> с классом class=”pane-list” и мы хотим сделать элементы <li>
кликабельными. Для начала привяжемся к событию click для элемента “.pane-list li”; когда
пользователь будет кликать по элементу списка, наша функция произведет поиск тэга <a> и
сделает редирект на страницу указанную в атрибуте href.
dojo.addOnLoad(function(){
dojo.query(".pane-list li").connect("click",function(){
window.location=dojo.query("a", this).attr("href");return false;
});
});
Складывающиеся панельки:
Ну, а теперь чуть-чуть скомбинируем предыдущие примеры и создадим ряд складывающихся
панелек (наподобие как в Gmail организован inbox). (см. пример)
скрываем все элементы <div class=”message_body”< после первого.
скрываем все элементы <li< после пятого
клик по <p class=”message_head”> – вызывает метод wipeOut/wipeIn для следующего элемента
<div class=”message_body”>
клик по <a class=”collpase_all_message”> – вызывает метод wipeOut для всех <div
class=”message_body”>
клик по <a class=”show_all_message”> – скрывает элемент, и отображает <a
class=”show_recent_only”>, так же вызывается метод wipeIn для всех <li> после пятого
клик по <a class=”show_recent_only”> – скрывает элемент, и отображает <a
class=”show_all_message”>, так же вызывается метод wipeOut для всех <li> после пятого
dojo.require("dojo.fx");
dojo.require("dojo.NodeList-fx");
dojo.addOnLoad(function(){
// hide message_body after the first one
42
dojo.query(".message_list li:not(:first-child) .message_body").style({ display:'none' });
// hide message li after the 5th
// dojo.query(".message_list li").splice(4).style({ display:'none' }); // not equal for FF and IE
dojo.query(".message_list li:nth-child(1n+4)").style({ display:'none' });
// event on header click
dojo.query(".message_head").connect('onclick',function(){
var body = dojo.query(".message_body",this.parentNode)
if (dojo.style(body[0], 'display') != 'none') {
body.wipeOut().play();
} else {
body.wipeIn().play();
}
return false;
});
// collapse all messages
dojo.query(".collpase_all_message").connect('onclick',function(e){
e.preventDefault();
dojo.query(".message_body").wipeOut().play();
});
// show all messages
dojo.query(".show_all_message").connect('onclick',function(e){
e.preventDefault();
dojo.query(this).fadeOut().play();
dojo.style(this,'display','none');
dojo.query(".show_recent_only").style('display','block').fadeIn().play();
dojo.query(".message_list li:nth-child(1n+4)").wipeIn().play();
});
// hide old messages
dojo.query(".show_recent_only").connect('onclick',function(e){
e.preventDefault();
dojo.query(this).fadeOut().play();
dojo.style(this,'display','none');
dojo.query(".show_all_message").style('display','block').fadeIn().play();
dojo.query(".message_list li:nth-child(1n+4)").wipeOut().play();
});
});
Примечание: заметил различное поведение функции dojo.NodeList.splice в различных браузерах
43
Галерея изображений:
Простейший пример реализации галереи, без перезагрузки страницы. (см. пример)
Для начала добавим тэг <em> в заголовки <h2>
По клику на изображения в <p class=thumbs> выполняем следующие действия:
отменяем событие по умолчанию (это переход по ссылке)
сохраняем значение атрибута “href” в переменной “largePath”
сохраняем значение атрибута “title” в переменной “largeAlt”
заменяем в элементе <img id=”largeImg”> значение атрибута “scr” и “alt” значениями из
переменных “largePath” и “largeAlt”
так же присваиваем элементу “h2 em” значение из “largeAlt”
dojo.addOnLoad(function(){
dojo.query(dojo.create('em')).place(dojo.query('h2'));
dojo.query(".thumbs a").connect("onclick",function(e){
e.preventDefault();
// получаем необходимые нам данные
var largePath = dojo.attr(this, "href");
var largeAlt = dojo.attr(this, "title");
// заменяем картинку и alt-текст
dojo.attr("largeImg", {src:largePath,alt:largeAlt});
em = dojo.query("h2 em")[0];
// изменяем описание
em.innerHTML = " (" + largeAlt + ")";
});
});
44
MochiKit — библиотеки на все случаи жизни
Детально о возможностях MochiKit в той или иной области JavaScript-программирования мы
поговорим чуть ниже, а пока скажем о двух преимуществах описываемого продукта, о которых
говорят в один голос и его разработчики, и пользователи, и индустриальные эксперты. Позволю
себе цитату с официального веб-сайта: «MochiKit is a highly documented and well tested suite of
JavaScript libraries that will help you get shit done fast». Ключевые слова здесь «highly documented»
и «well tested», ибо этим может похвастаться далеко не каждая библиотека или оболочка для
JavaScript.
Последнее же, о чем хотелось бы упомянуть во вступлении, — это высокий уровень
масштабируемости проектов, разрабатываемых на базе MochiKit. Иными словами, использование
описываемой JavaScript-библиотеки совершенно не означает полный отказ от других продуктов.
При желании мы можем использовать целый комплекс решений как для клиентской, так и для
серверной стороны (что, в принципе, само собой разумеющееся), если, конечно, понадобится.
Кроме того, MochiKit добавляет лишь три символа в global scope (MochiKit namespace и функции
compare и reduce для устранения багов JScript), избегая хакинга на основе Object.prototype.
Дальше в материале мы рассмотрим основные библиотеки MochiKit с описанием их
возможностей и с краткими примерами.
MochiKit.Base :
Как видно из названия, это базовая библиотека в MochiKit, на основе которой, в том числе,
строятся все остальные. Но, кроме того, она предоставляет своим пользователям еще и мощную
низкоуровневую функциональность. Разработчики утверждают, что создавали описываемую
библиотеку, находясь под впечатлением от Python и Python standard library.
Из основных же возможностей MochiKit.Base стоит выделить следующие:
-
расширенная функциональность для всевозможных сравнений (compare,
registerComparator);
расширенная representation-функциональность (repr, registerRepr);
расширенная функциональность для JSON serialization и JSON evaluation (serializeJSON,
evalJSON, registerJSON);
простая и прозрачная adaptation-схема (AdapterRegistry);
дополнительные функции для манипуляций с объектами и массивами (update, setdefault,
extend и т. д.);
основанное на массивах функциональное программирование (map, filter и т. д.);
расширенная функциональность для работы с функциями (bind, method, partial).
45
Разумеется, многие из этих возможностей вряд ли понадобятся нам во время разработки простого
веб-приложения, где большее внимание уделяется другим вещам (интерфейс, usability и т. п.), но
если речь идет о действительно масштабном и сложном проекте, то без подобных функций
попросту не обойтись.
MochiKit.Async
Эта библиотека отвечает в MochiKit за все необходимое для разработки полноценных AJAXуправляемых веб-приложений. Работая на MochiKit.Async, разработчики черпали вдохновение в
уже всем известном продукте под названием Twisted. Однако не стоит ожидать от MochiKit.Async
того же стандартного подхода, что и во многих других библиотеках, ибо здесь он немного другой.
Все построено вокруг объекта Deferred, который отвечает за все рутинные операции по созданию,
получению и обработке запросов между клиентской и серверной стороной. Если схематично, то
выглядит это следующим образом:
- мы создаем объект Deferred и сохраняем ссылку на него, так как она понадобится нам
позже;
- затем устанавливаем условия для создания серверного запроса (создаем XMLHttpRequest
и настраиваем его onreadystatechange);
- возвращаем объект Deferred.
Ответ на запрос от сервера будет получен, разумеется, не сразу, поэтому объекту Deferred нужно
также сообщить название callback-функции, которая будет выполнена, как только придет
серверный ответ. Это, в принципе, обычное дело для всех современных AJAX-библиотек, но
авторы MochiKit.Async пошли дальше: помимо callback-функции мы назначаем еще и errbackфункцию, которая будет вызвана, если во время взаимодействия с сервером произошла ошибка.
MochiKit.DOM :
Каждый, кто когда-нибудь занимался разработкой более-менее серьезных и масштабных
JavaScript-приложений, знает, насколько мучительно порой бывает производить комплексные (в
принципе и не только комплексные) манипуляции с DOM-элементами. Знают об этом и создатели
MochiKit, которые постарались создать такую библиотеку для работы с DOM, чтобы у
разработчиков не возникало больше головных болей, связанных не непосредственно с их
проектом, а с другими второстепенными вещами вроде совместимости методов с различными
версиями браузеров и так далее.
Лучшей иллюстрацией тут станет короткий жизненный пример, в котором мы с помощью
MochiKit.DOM создадим простенькую таблицу. Вот так будет выглядеть JavaScript-код:
var rows = [
["dataA1", "dataA2", "dataA3"],
["dataB1", "dataB2", "dataB3"]
];
row_display = function (row) {
return TR(null, map(partial(TD, null), row));
}
46
var newTable = TABLE({'class': 'prettytable'},
THEAD(null,
row_display(["head1", "head2", "head3"])),
TFOOT(null,
row_display(["foot1", "foot2", "foot3"])),
TBODY(null,
map(row_display, rows)));
Вначале мы задаем данные для основных строк нашей таблицы. Затем формируем на основе этих
данных HTML-код. А уже после этого создаем HTML-код целой таблицы, причем с элементами
thead и tfoot. Выглядеть результат будет следующим образом:
<table class="prettytable">
<thead>
<tr>
<td>head1</td>
<td>head2</td>
<td>head3</td>
</tr>
</thead>
<tfoot>
<tr>
<td>foot1</td>
<td>foot2</td>
<td>foot3</td>
</tr>
</tfoot>
<tbody>
<tr>
<td>dataA1</td>
<td>dataA2</td>
<td>dataA3</td>
</tr>
<tr>
<td>dataB1</td>
<td>dataB2</td>
<td>dataB3</td>
</tr>
</tbody>
</table>
И вот так просто в MochiKit.DOM можно осуществить практически любое действие практически с
любым DOM-элементом. На этом, разумеется, прелести описываемой библиотеки не
заканчиваются, но рассказать о них всех не хватит и целого материала, поэтому мы двигаемся
дальше.
47
MochiKit.DragAndDrop :
Одним из современных стандартов для веб-приложений можно считать возможность
использовать функцию drag'n'drop для работы с теми или иными элементами страницы.
Реализаций подобной функциональности сегодня насчитывается, наверное, несколько десятков,
если не больше. Создатели MochiKit взяли за основу идеи из Script.aculo.us и чуть адаптировали их
под собственные нужды. Посмотрим на короткий пример.
// Создаем draggable-элемент
new Draggable('mydrag');
// Создаем droppable-элемент
new Droppable('mydrop', {
accept: ['drag-class'],
ondrop: function (element) {
alert('"' + element.id + '" was dropped at me');
}
});
Все очень просто: в MochiKit.DragAndDrop мы можем создавать объекты draggable и droppable,
которые затем будут взаимодействовать друг с другом при помощи конечного пользователя. Но
это еще не все: draggable-элемент на протяжении своего жизненного цикла генерирует несколько
сигналов (start, drag и end), которые можно обрабатывать.
onStart = function (draggable) {
// Содержимое функции
};
connect(Draggables, 'start', onStart);
Как видно из примера, вначале мы описали функцию onStart, которую затем присоединили к
сигналу start у всех draggable-элементов страницы. К слову, делается это с помощью функции
connect, которая является частью другой библиотеки проекта — MochiKit.Signal. Она отвечает за
обработку событий. (см. пример)
MochiKit.Visual :
Последняя библиотека сегодняшнего обзора называется MochiKit.Visual и отвечает за
всевозможные визуальные эффекты, без которых во время разработки современных вебприложений также трудно обойтись. Сюда относятся и базовые эффекты вроде закругления углов
какого-то элемента, и более сложные и динамичные вещи. В MochiKit это все объединено в
рамках одной большой библиотеки. Причем реализация закругленных углов была взята из Rico;
48
она не требует использования внешних изображений или таблиц стилей. А многие другие
визуальные эффекты были перенесены из уже упоминавшегося сегодня Script.aculo.us.
Позволю себе привести несколько коротких примеров, иллюстрирующих схему работы с
MochiKit.Visual.
// Закругляем углы все элементов h1
roundClass("h1", null);
// Закругляем верхний левый угол элемента с атрибутом id со значением "title"
roundElement("title", {corners: "tl"});
// Добавляем эффект fade к элементу
fade('myelement');
Что касается количественных показателей, то различных визуальных эффектов в MochiKit.Visual
достаточно много. Однако там вряд ли удастся найти что-то уж совсем экзотическое. Вообще,
подобное определение верно для всех без исключения библиотек проекта: они отлично
справляются с поставленными задачами, работают стабильно и не требуют каких-то особых
знаний, но в то же время не предлагают своим пользователям новых высот. Иными словами,
MochiKit — это действительно JavaScript-библиотеки практически на все случаи жизни, но назвать
их революционными или новаторскими язык не поворачивается.
49
Prototype
Тип: JavaScript-библиотека
Разработчик: Prototype Core Team
ОС: Кроссплатформенное ПО
Версия:1.6.1_rc2 — 27 марта, 2009
Лицензия:MIT License
Сайт:prototypejs.org
Prototype — JavaScript фреймворк, упрощающий работу с Ajax и некоторыми другими функциями.
Несмотря на его доступность в виде отдельной библиотеки, он обычно используется
программистами вместе с Ruby on Rails, script.aculo.us и Rico.
Заявлено, что данный фреймворк поддерживается следующими браузерами: Internet Explorer
(Windows) 6.0 +, Mozilla Firefox 1.5 +, Apple Safari 2.0 + и Opera 9.25 +. Поддержка данных
браузеров также подразумевает, что фреймворк поддерживается также Camino, Konqueror,
IceWeasel, Netscape 7+, SeaMonkey, и др., которые принадлежат этим же семействам.
Краткий обзор самых полезных возможностей
Для подключения библиотеки достаточно скачать единственный js-файл и добавить его на вашу
страницу: <script type="text/javascript" src="prototype-1.6.0.2.js"></script>
Функция $():
Пожалуй, самая знаменитая и часто используемая функция. В качестве аргумента она принимает
идентификатор (или массив идентификаторов) и отдает ссылку на объект с этим идентификатором
(или массив соот-но).
HTML:
<div id="id_1">First DIV</div>
<div id="id_2">Second DIV</div>
JavaScript:
alert(
$("id_1").innerHTML
); // Выведет 'First DIV'
var divArray = $("id_1","id_2");
alert(divArray.length); // Выведет 2
Функция "доллар" по сути является эквивалентом метода document.getElementById, но более
продвинутым, т.к.:
- более короткая и удобная в записи,
- может принимать несколько аргументов,
- может принимать в качестве аргумента ссылку на элемент и возвращать её же.
50
Последний момент можно проиллюстрировать дополнением к предыдущему примеру:
alert(
$(
$("id_1")
).innerHTML
); // Выведет 'First DIV'
Функция $$():
Функция "доллар-доллар" является существенным расширением предыдущей, т.к. в качестве
аргументов принимает не только идентификаторы, но и в целом CSS-селекторы:
$$('div'); // Вернет все ДИВы, также как document.getElementsByTagName('div')
$$('div','span'); // Вернет массив всех ДИВов и СПАНов
$$('#id_1'); // Вернет элемент с id="id_1", также как $('id_1')
$$('li.faux'); // Вернет все элементы LI с классом 'faux'
Как видно из примера, $$('div') аналогично вызову document.getElementsByTagName('div'). Однако,
в prototype.js существует надстройка в виде метода select(), который также возвращает массив
элементов по селектору, но ищет их в дочерних элементах из контекстного объекта:
HTML:
<div id="parent">
<span>Q</span>
<span>W</span>
<span>E</span>
</div>
<span>R</span>
JavaScript:
$('parent').select('span'); // Вернет массив СПАНов внутри элемента с id="parent"
Реализована поддержка атрибутов в селекторах (CSS 2.1):
$$('input[type=text]'); // Вернет массив ИНПУТов только с type="text"
Также, начиная с версии 1.5.1, заявлено о поддержке селекторов CSS3:
$$('div#parent :first-child') // Вернет первого потомка для ДИВа с id="parent"
Функция "доллар-доллар" имеет все шансы вытеснить своего исторического предка, т.к. дает
огромные возможности разработчику для получения доступа к узлам документа.
Функция $F()
Удобна для работы с полями форм — возвращает их значение. В качестве аргумента принимает
либо ID элемента, либо сам элемент:
51
HTML :
<input type="text" value="Hello!" id="inputId" />
JavaScript :
$F('inputId'); // Вернет 'Hello!'
Функции $A() и $H():
В JavaScript есть базовые объекты, такие как Array, Date, Object и др. В некотором приближении их
можно рассматривать в качестве классов (в терминах ООП). Библиотека prototype.js умеет
создавать собственные классы и расширять уже имеющиеся.
В частности, класс Enumerable (перечисляемый) является расширением Array, а функция $A()
приводит передаваемые ей массивы к виду экземпляра объекта Enumerable. Для примера,
получим массив всех ДИВов на странице и изменим их внешний вид с помощью метода each()
класса Enumerable:
HTML :
<div>First DIV</div>
<div>Second DIV</div>
<div>Third DIV</div>
JavaScript :
var allDiv = document.getElementsByTagName('div'); // Получим все ДИВы на странице
allDiv = $A(allDiv); // Приведем их к виду экземпляра класса Enumerable
allDiv.each(setColor); // С помощью метода each для каждого элемента применим функцию
setColor
function setColor(element,index) { // arg[0] - ссылка на элемент, arg[1] - индекс в массиве
element.style.backgroundColor = 'red'; // Задаем фоновый цвет для ДИВа
}
Класс Hash является расширением класса Object. Экземпляры класса Object — это ассоциативные
массивы, содержащие набор пар ключ=значение. К сожалению, в JavaScript всего один явный
способ перебора ассоциативных массивов — конструкция for in. А к экземплярам класса Hash
можно применить множество полезный методов, таких как keys(), values() и др.
Более подробно о классах и их реализации в prototype речь пойдет в следущей статье.
Функция $w():
Небольшая полезная функция, преобразующая строку в массив слов. В качестве разделителей
метод использует пробелы:
$w('apples bananas kiwis') // Вернет массив ['apples', 'bananas', 'kiwis']
Download