I’ve created JS carousels for a few websites and every time they want different functionalities… sometimes they want the arrow buttons, sometimes they want to change the transition between the images, or add some tabs, and maybe later they will ask me to create a carousel with a thousand images loading them dynamically.So i have created a MooTools Class and finally I modified it to be flexible to changes. This is not a copy/paste carousel, you’ll need to know mootools to use this code. The advantage: you’ll be able to modify or add a new functionality according to your needs.
The basic carousel is an horizontal list of images that are been displayed from right to left, and there are two buttons: next and previous, to switch the images. I know, I know, you want the code and an example:

var Carousel = new Class({
Implements : [ Options, Events ],
options : {
wrapperId : "wrapper",
maskId : "mask",
slideSelector : ".slide",
speed : 3000,
auto_slide : true,
// no need to initalize the next options
num : 0,
current : 0,
mask_width : 0,
slides : [],
plugins : []
},
initialize : function(options) {
this.setOptions(options);
this.wrapper = $(this.options.wrapperId);
this.current = 0;
this.options.plugins.each(this.addListener, this);
this.load();
if (this.options.auto_slide) {
this.play();
}
this.moveTo(0);
},
addListener : function(observer) {
observer.setCarousel(this);
},
/**
* notify all plugins of an event
*/
notify : function(event) {
this.options.plugins.each(function(plugin) {
plugin.listen(event);
});
},
/**
* Start the timer
*/
play : function() {
this.stop();
this.timer = this.next.periodical(this.options.speed, this);
},
/**
* clear the timer
*/
stop : function() {
clearInterval(this.timer);
},
/**
* Set the style for the slides (horizontal by default)
*/
load : function() {
this.slides = this.wrapper.getChildren(this.options.slideSelector);
this.options.num = this.slides.length;
this.options.mask_width = $(this.options.maskId).getSize().x;
this.wrapper.setStyle('width', this.slides[0].getSize().x
* this.options.num);
this.notify('load');
},
/**
* changes the current index and calls the transition
*/
moveTo : function(index) {
this.current = index;
this.transition(this.current);
this.notify('move');
},
/**
* Executes the transition
*/
transition : function(offset) {
new Fx.Tween(this.wrapper).start('left', this.wrapper.getStyle('left'),
offset * (-this.options.mask_width));
},
/**
* go to the next slide
*/
next : function() {
this.current++;
if (this.current >= this.options.num) {
this.current = 0;
}
this.moveTo(this.current);
this.notify('next');
},
/**
* go to the previous slide
*/
prev : function() {
this.current--;
if (this.current < 0) {
this.current = this.options.num - 1;
}
this.moveTo(this.current);
this.notify('prev');
},
/**
* stops the timer and prev()
*/
manualPrev : function(index) {
this.stop();
this.prev();
this.notify('manualprev');
},
/**
* stops the timer and next()
*/
manualNext : function(index) {
this.stop();
this.next();
this.notify('manualnext');
},
/**
* stops the timer and go to the slide
*/
manualMoveTo : function(index) {
this.stop();
this.moveTo(index);
this.notify('manualmove');
}
});
var CarouselObserver = new Class({
carousel : null,
setCarousel : function(carousel) {
this.carousel = carousel;
},
listen : function(event) {
switch (event) {
case "load": this.load(); break;
case "move": this.move(); break;
case "next": this.next(); break;
case "prev": this.prev(); break;
case "manualprev": this.manualprev(); break;
case "manualnext": this.manualnext(); break;
case "manualmove": this.manualmove(); break;
default: break;
}
},
load : function() {},
move : function() {},
next : function() {},
prev : function() {},
manualprev : function() {},
manualnext : function() {},
manualmove : function() {}
});
var carousel;
window.addEvent('domready', function(){
carousel = new Carousel({auto_slide:true,
wrapperId:"works"});
});
Ok, there’s nothing special in this carousel. But now let’s say that you need to add some buttons (one for each slide) and every time you click one of them, the carousel will move to the corresponding image. We’ll create a class that listens to the “move” event
var carousel , tabs;
var CarouselTabs = new Class({
Extends : CarouselObserver,
Implements : [ Options, Events ],
options : {
tabsSelector : ".tabs"
},
initialize : function(carousel, options) {
this.setOptions(options);
this.tabs = $$(this.options.tabsSelector);
this.carousel = carousel;
},
move : function() {
this.tabs.each(function(tab) {
tab.removeClass('current');
});
this.tabs[this.carousel.current].addClass('current');
}
});
window.addEvent('domready', function(){
tabs = new CarouselTabs(carousel,{tabsSelector:"#tabs li"});
carousel = new Carousel({
auto_slide:true,
wrapperId:"works",
plugins:[ tabs ]
});
});
Hey! we have built a simple plugin to our carousel class!. Now let’s build another plug in to display labels that we’ll retrieve using ajax. Maybe this last example is not too practical, but I wanted to show you that you could develop your own plugins, instead of having to search for a carousel that has that specific functionality that you need.
var CarouselLabels = new Class({
Extends : CarouselObserver,
Implements : [ Options ],
initialize : function(carousel) {
this.carousel = carousel;
},
load : function() {
this.request(0)
},
move : function() {
this.request(this.carousel.current)
},
request : function (index){
new Request({
'method': 'get',
'url': 'ajax.php',
'onSuccess': function(result){
$('label').set('html',result);
},
'data':{'a':index}
}).send();
}
});
Until know, you have seen that for every action, the carousel is throwing an event that any plugin can subscribe to, and do something at that time. This will allow you to create as many functionalities as you want, and easily remove them without changing the original class. But if you need to change the behavior of the carousel you’ll need to Extend the original class and modify the actions. For example, let’s change the transition of the slides to create a vertical carousel.
var VerticalCarousel = new Class({
Extends: Carousel,
load : function() {
this.slides = this.wrapper.getChildren(this.options.slideSelector);
this.options.num = this.slides.length;
this.options.mask_width = $(this.options.maskId).getSize().y;
this.notify('load');
},
transition : function(offset) {
new Fx.Tween(this.wrapper).start('top', this.wrapper.getStyle('top'),
offset * (-this.options.mask_width));
}
});
Did you see?? we haven’t event touched the code on the plugins and they are still working. Now instead of spending hours on the web, trying to find the carousel that does everything you need, you could just create a plugin or extend the class and easily reuse that code in the future.
This is the first version of this class so I’m really interested in any comments or suggestions to improve this code.Thanks for reading
Muito bom!!
Very nice!!
I just created a new listener to initialize the carousel within the plugin.
Thus, it is not necessary to pass the carousel parameter when create instance of plugin…
https://gist.github.com/3657689
This is pretty nice. Not weighed down with too many complex options but easily extensible for specific needs. How about putting it on GitHub? If you do, I’ll surely fork it.
thx
i wanted to put it on github.. but then I started to think of a better way to do a carousel.. Using the observer pattern: A class that periodically fire events and “plugin” classes subscribing to those events.. seems more “elegant”
i’ll write another article for that
Thanks a lot for the material, and your blog really looks outstanding. What word press theme are you utilizing?
thanks
, it’s the default theme.. a web designer did the style and i just transformed it into Html/Css