В одной из прошлых статей я писал об основах программирования с использованием Mootools, а именно о работе с элементами и событиями. В этом уроке речь пойдет о несколько более сложной теме - объектно-ориентированном программировании, в частности о создании классов и объектов, а также о наследовании. В качестве примера я покажу процесс создания класса SlideShow (для отображения слайдов) и двух его дочерних классов - ImageSlideShow (для показа изображений) и RssSlideShow (для показа лент новостей).
Для начала немного теории. Новые классы в Mootools создаются при помощи класса Class, который принимает один аргумент - свойства (properties):
var newCls = new Class(properties);
В качестве свойств могут передаваться три специльных свойства - функция initialize, явлющаяся конструктором класса, Extend, для реализации наследования, и Implement - для заимствования свойств других объектов или классов.
Итак, создаем каркас нашего приложения-слайдшоу:
var SlideShow = new Class({
options: {
slides : [],
startIndex : 0,
wrap : true
},
// конструктор класса
initialize: function(options){
this.setOptions(options);
},
// заимствуем методы классов Options и Events
Implements : [Options, Events]
});
При помощи свойства Implements мы заимствуем методы классов Options и Events, которые доступны в библиотеке Mootools. Метод setOptions() класса Options используется в конструкторе initialize для установки значений опций options. В данном случае в качестве опций мы указываем на массив слайдов в структуре DOM (например так: $$(’slides’)), индекс слайда, с которого начинать показ, и значение wrap, указывающего на цикличность показа слайдов. Такой шаблон можно использовать для создания подавляющего большинства классов в Mootools.
Далее мы добавим функции, которые будут управлять работой слайдшоу - добавлять и поочередно показывать слайды:
var SlideShow = new Class({
options: {
slides : [],
startIndex : 0,
wrap : true
},
initialize: function(options){
this.setOptions(options);
this.slides = [];
this.addSlides(this.options.slides);
if (this.slides.length)
this.showSlide(this.options.startIndex);
},
Implements : [Options, Events],
/**
* Добавление слайдов в слайдшоу
*/
addSlides: function(slides){
$$(slides).each(function(slide){
this.slides.include($(slide));
slide.addEvent('click', this.cycleForward.bind(this));
}, this);
},
/**
* Добавление одного слайда
*/
addSlide: function(slide){
this.addSlides([slide]);
},
/**
* Прокрутка слайдов вперед
*/
cycleForward: function(event){
if($chk(this.now) && this.now < this.slides.length-1)
this.showSlide(this.now+1);
else if (this.now && this.options.wrap)
this.showSlide(0);
else if(!$defined(this.now))
this.showSlide(this.options.startIndex);
},
/**
* Прокрутка слайдов назад
*/
cycleBack: function(){
if(this.now > 0)
this.showSlide(this.now-1);
else if(this.options.wrap)
this.showSlide(this.slides.length-1);
},
/**
* Показ слайда
*/
showSlide: function(iToShow){
var now = this.now;
var current = this.slides[now];
var slide = this.slides[iToShow];
if(slide){
if($chk(now) && now != iToShow){
var hideEffect = new Fx.Morph(current, {duration: 'long'});
hideEffect.start({'opacity':0}).chain(
function(){
current.setStyle('display','none');
slide.setStyles({
'display':'block',
'opacity':'0'
});
var showEffect = new Fx.Morph(slide, {duration: 'long'});
showEffect.start({'opacity':1});
}.bind(this)
);
} else {
slide.setStyle('display','block');
}
}
this.now = iToShow;
}
});
Здесь мы добавили пять новых функций - addSlide() и addSlides() для добавления одного и группы слайдов в список отображения, cycleForward() и cycleBack() для просмотра следующего (предыдущего) слайда, и showSlide() для показа слайда (здесь используется эффект Fx.Morph для плавного появления слайдов). В функци addSlides() мы добавляем обработчик cycleForward() для события onclick, следовательно переход от слайда к слайду будет осуществляться по щелчку мыши на нем. В конструктор же добавляем вызов функции showSlide() для того, чтобы после создания объекта класса сразу показать первый слайд.
В итоге мы получили вполне работоспособный класс. Создаём объект класса:
window.addEvent('domready', function(){
new SlideShow({
slides: $$('div.slide')
});
});
Здесь в качестве слайдов выступают несколько div-ов с атрибутом class="slide". Посмотреть демо этого примера можно здесь.
Теперь рассмотрим пример создания дочернего класса ImageSlideShow для создания слайдшоу изображений. Класс мы будем создавать точно также, только вместо свойства Implements (его исользовать не нужно, так как методы классов Options и Events будут наследоваться от SlideShow), будем использовать свойство Extends, указывающее на родительский класс:
var ImageSlideShow = new Class({
options: {
imgUrls: [], // URL изображений
container: false // ID контейнера
},
Extends : SlideShow,
initialize: function(options){
this.parent(options);
// получаем ссылку на контейнер
this.container = $(this.options.container);
if(!this.container) {
return; // нет такого контейнера, завершаем работу
}
this.options.imgUrls.each(this.addImg.bind(this));
this.showSlide(this.options.startIndex);
},
/**
* Добавление изображений в контейнер
*/
addImg: function(url){
var img = new Element('img', {
src: url,
styles: {
display: 'none'
}
}).injectInside($(this.options.container))
this.addSlide(img);
}
});
В этом классе нам не нужно заново объявлять функции addSlide() или showSlide(), так как они наследуются от класса SlideShow. ImageSlideShow в отличии от SlideShow будет принимать в качестве аргументов массив URL-ов изображений и ID контейнера, в который нужно добавлять слайды. В свою очередь слайды будут создаваться и добавляться в контейнер динамически при помощи функции addImg().
Демо этого примера смотрите здесь.
Ну и наконец последний, более практичный на мой взгляд пример, демонстрирующий создание виджета, который показвает слайдшоу последних новостей, получаемых через RSS. Содержимое ленты новостей будем получать через прокси скрипт rssproxy.php посредством AJAX, а слайды будем прокручивать автоматически используя функцию setInterval().
var RssSlideShow = SlideShow.extend({
options : {
rssUrl : '',
container : false
},
interval : null, // интервал слайдшоу (setInterval)
initialize : function(options){
this.parent(options);
this.container = $(this.options.container);
if (!this.container) {
return;
}
this.initChannel();
},
/**
* Получение канала rss через прокси rssproxy.php,
* в параметре get указываем URL канала
*/
initChannel : function(){
if (!this.options.rssUrl || this.options.rssUrl == '') {
return;
}
// получаем содержимое ленты новостей используя AJAX
var request = new Request({
url : 'rssproxy.php?get='+encodeURI(this.options.rssUrl),
method : 'post',
onSuccess : this.addNews.bind(this)
}).send();
},
/**
* Добавление слайдов с новостями в контейнер
*/
addNews : function(txt, xml) {
var items = xml.getElementsByTagName('item');
for (var i=0, len=items.length; i < len; i++){
var title = items[i].getElementsByTagName('title')[0];
var desc = items[i].getElementsByTagName('description')[0];
var slide = new Element('div', {
'class': 'rssSlide',
styles: {
display: 'none'
}
});
slide.setHTML('<h4>'+title.firstChild.nodeValue+'</h4><p>'+desc.firstChild.nodeValue+'</p>');
slide.injectInside(this.container)
this.addSlide(slide);
}
this.start();
},
/**
* Запуск слайдшоу
*/
start : function(){
this.showSlide(this.options.startIndex);
var that = this;
this.interval = setInterval(function(){that.cycleForward()}, 10000);
},
/**
* Остановка слайдшоу
*/
stop : function(){
clearInterval(this.interval);
}
});
Здесь, вместо свойства Extends я использую функцию SlideShow.extends(), просто чтобы показать еще один способ наследования классов. Класс RssSlideShow принимает два аргумента - URL ленты новостей и ID контейнера для слайдов. Автопрокрутку слайдов обеспечиваем при помощи двух простых функций start() и stop(), которые используют setInterval() с параметром задержки равным 10 секунд. Функция initChannel(), которая вызывается из конструктора, выполняет AJAX запрос к серверу, а полученный ответ адресует в функцию addNews(), которая парсит полученную RSS ленту, создаёт слайды и добавляет их в контейнер.
Демо RssSlideShow доступно здесь.
Заключение
Из представленных выше примеров хорошо видно, насколько просто и удобно работать с классами в Mootools. Самое главное - это определиться со свойствами и методами, общими для всех классов в иерархии, а дальнейшее их наследование чрезвычайно просто выполняется посредством функции extend(). Использование же свойства Implements (или функции implement), позволяет наделить ваши классы множеством полезных функций и свойств, доступных в богатой коллекции библиотеки Mootools.