Carousels are very common across many websites, and being a web developer, it is essential you know how to code one.
Before you get started with your carousel, you will need to find yourself five images of the same size to be your carousel content. These can be anything, from simple images numbered 1 to 5 (as I will be using) to photos of friends or family.
We will once again use the template that we previously set up.
We will start by setting up the HTML for our carousel. This will simply consist of an unordered list within a carousel
div
wrapper. Each of the list items is populated with the images that you have selected to use inside your carousel:<div class="carousel"> <ul> <li><img src="images/item1.jpg" alt="" /></li> <li><img src="images/item2.jpg" alt="" /></li> <li><img src="images/item3.jpg" alt="" /></li> <li><img src="images/item4.jpg" alt="" /></li> <li><img src="images/item5.jpg" alt="" /></li> </ul> </div>
Users without JavaScript will simply get a list of the images. Now for users with JavaScript, we are going to add the carousel's functionality to our list.
The first thing we need to do is name our plugin. There are lots of plugins, so simply calling it a carousel will cause issues; so for this example, we will call our plugin
boilerplatecarousel
. We will need to update thepluginName
variable to reflect this as follows:var pluginName = "boilerplatecarousel",
Our next step is to look at our plugin's
init
method:To start with, we want to cache our element as
$element
; we do this by setting the$element
variable equal to$(this.element)
.We then need to determine the width of the items and how many items we will be sliding between; the width will be achieved by using
$element.find('li').width()
, and the number of items will be counted using$element.find('li').length
.We will then store the jQuery object for the carousel slider in
this.carousel
, and then add awidth
parameter equal to the item width multiplied by the number of items.We then need to add a controller to allow the user to interact with the carousel; this should include a
next
andprevious
button.Finally, we need to add a functionality to the buttons that we will use for the event delegation to fire the
this.controller
method:init: function() { var $element = $(this.element); var $items = $element.find('li'); var width = $items.width(); var count = $items.length; this.carousel = $element.find('ul'); this.carousel.css('width',width*count); $element.append('<ul class="controller"><li><a href="#" class="prev">prev</a></li><li><a href="#" class="next">next</a></li></ul>'); $element.on('click', '.controller li a', this.controller); }
Once we have set up our
init
function, we need to add some functionality to ourcontroller
method. As we are sharing one method between both thenext
andprevious
buttons, we will need to identify which button has been clicked on in this method so that we know what the user wants to do. Before we can identify the button that has been clicked on, we need to set up some variables. First we will cache$(this)
as$this
, then we will read theclass
parameter of the button that has been clicked and store it asnavClass
, retrieve the plugin instance asplugin
, and work out which item is currently visible to the user and cache it ascurrentItem
. All this is shown in the following code snippet:controller: function(e){ var $this = $(this), navClass = $this.attr('class'), plugin = $(e.delegateTarget).data("plugin_" + pluginName), $currentItem = $(plugin.element).find('li').first(); if(navClass.indexOf('next') !== -1){ $currentItem.appendTo(plugin.carousel); } else if(navClass.indexOf('prev') !== -1){ $(plugin.carousel).prepend($(plugin.carousel).find('li').last()); } return false; }
At this point, we currently have the plugin reordering our items; however, it doesn't appear to look much like a carousel as all the items are still visible. To make it appear more like a carousel, we need to look at adding some styles.
The first step is to define the styles for our container, which will provide a width for the view area of the carousel and will hide all those items not in the view area. We will define a width of
500px
and useoverflow:hidden
to hide those that are outside of the view area..carousel{ overflow: hidden; width: 500px; position: relative; }
We then need to look at the styles of the slider. The
UL
tag itself will need to override the default padding on aUL
tag by settingpadding: 0px
, and we will useoverflow:hidden
to clear the floats of the items inside theUL
tag..carousel ul{ padding: 0px; overflow: hidden; }
We will need to style the appearance of the individual items in the carousel. We need all of them to be floated in line with each other, have a width of
500px
to match the view area. Additionally, we need to override the defaultlist-style
parameter and set its value tonone
. This is done as follows:.carousel ul li{ float: left; width: 500px; list-style: none; }
Finally, we need to add some styling to both the arrows, which will be achieved by positioning them at the top of the carousel.
.carousel ul.controller li a.next,.carousel ul.controller li a.prev{ position: absolute; top: 140px; background: #fff; padding: 20px; } .carousel ul.controller li a.prev{ left: 0px; border-radius: 0 10px 10px 0; } .carousel ul.controller li a.next{ right: 0px; border-radius: 10px 0 0 10px; }
At this point, all our carousel is doing is reordering the elements; it is not doing any animation, as one would normally expect in a carousel. We will now add one animation; hopefully, with what you will learn here, you could go on to add your own effects:
When working on this extension to the carousel, we are extending our existing carousel plugin.
Our first step is to extend the
defaults
object literal to have an extra value for the effect; this will enable the plugin to support multiple effects if you wish.defaults = { effect: "none" };
We then need to add a new method to our
Plugin
prototype's object literal to handle the effect; we will name this aseffect
.Our first step is to move our existing transition with no effect to the
effect
method, and for this, we will need to write the first part of oureffect
method. To begin with, we will move theif
statement we previously had in ourcontroller
method to theeffect
method.effect: function($currentItem, direction){ if(direction === "next"){ $currentItem.appendTo(this.carousel); } else if(direction === "prev"){ $(this.carousel).prepend($(this.carousel).find('li').last()); } }
We then need to make some changes to our
controller
method so that it uses the neweffect
method for the transition. Where we previously had anif
statement for the transition, we now have anif
statement that will fire theeffect
method with the correct parameters, shown as follows:controller: function(e){ var $this = $(this), navClass = $this.attr('class'), plugin = $(e.delegateTarget).data("plugin_" + pluginName), $currentItem = $(plugin.element).find('li').first(); if(navClass.indexOf('next') !== -1){ plugin.effect($currentItem, "next"); } else if(navClass.indexOf('prev') !== -1){ plugin.effect($currentItem, "prev"); } return false; }
At this point, our carousel should work in the same way as before, cycling through each of the items in the carousel one-by-one with no animation. Now, we can extend our
effects
method to provide extra transitions. The first step is to create anif
statement that selects whicheffect
should be used. The idea is that, if the developer using the plugin has not indicated which effect they want, it will resort to the defaultno
effect.effect: function($currentItem, direction){ var itemWidth = $currentItem.width(); if(this.options.effect === "slide"){ //slide effect will go here } else if(this.options.effect === "none"){ if(direction === "next"){ $currentItem.appendTo(this.carousel); } else if(direction === "prev"){ $(this.carousel).prepend($(this.carousel).find('li').last()); } } }
Once we are happy with our
if
statement, we can start working on our slide effect. For the effect to work, we need to animate the sliding of theUL
containing the carousel's items to the left and right. The view area of the carousel is only set to the width of one item, so moving theUL
tag within this will show our desired animation.As per the
no
animation effect, we first need to add a simpleif
statement that reads thedirection
parameter of theeffect
method and allows us to handle both directions.effect: function($currentItem, direction){ var itemWidth = $currentItem.width(); if(this.options.effect === "slide"){ if(direction === "next"){ } else if(direction === "prev"){ } } else if(this.options.effect === "none"){ if(direction === "next"){ $currentItem.appendTo(this.carousel); } else if(direction === "prev"){ $(this.carousel).prepend($(this.carousel).find('li').last()); } } }
Once we have the
if
statement, we are able to start working on our logic inside it. Our first step is to work on the code required for when our first condition is met, which is when thedirection
variable is equal tonext
.For this, we are going to use the
jQuery.animate()
function to animate the shifting of theUL
containing the items on the left. The distance travelled will equal the width of one of the carousel items. As the complete function parameter, we move the item to the end of the list of items and then reset theleft
value to0px
.effect: function($currentItem, direction){ var itemWidth = $currentItem.width(); if(this.options.effect === "slide"){ if(direction === "next"){ $(this.carousel).animate({ left: "-="+itemWidth }, 500, function() { $(this).find('li').first().appendTo(this); $(this).css('left','0px'); }); } else if(direction === "prev"){ } } else if(this.options.effect === "none"){ if(direction === "next"){ $currentItem.appendTo(this.carousel); } else if(direction === "prev"){ $(this.carousel).prepend($(this.carousel).find('li').last()); } } }
Moving to the previous
direction
is not as simple. Firstly, we need to move the last carousel item to the beginning of theUL
tag containing the items; then we need to set the position of theUL
so that the current item is still visible. Finally, we need to use thejQuery.animate()
function to move to the previous item. The main problem with this is that this function does not allow us to provide it with functionality before the animation. Along with this, we need to ensure that, if the user clicks on the previous button X times, then we need to move an X number of items. This means we need to look at how we can manipulate the animation queue. jQuery provides us with a way to do this using.queue()
. In its simplest form, we only need to provide one parameter, which is our method that we wish to queue. This will add the method to be called as part of thefx
queue.effect: function($currentItem, direction){ var itemWidth = $currentItem.width(); if(this.options.effect === "slide"){ if(direction === "next"){ $(this.carousel).animate({ left: "-="+itemWidth }, 500, function() { $(this).find('li').first().appendTo(this); $(this).css('left','0px'); }); } else if(direction === "prev"){ $(this.carousel).queue(function () { }); } } else if(this.options.effect === "none"){ if(direction === "next"){ $currentItem.appendTo(this.carousel); } else if(direction === "prev"){ $(this.carousel).prepend($(this.carousel).find('li').last()); } } }
Now when we click on the previous button, we will be adding a new item to the
fx
queue. Next, though, we need to add the functionality that we previously discussed to the queue. (Rather than repeating the code, we will now only show the queue). The first thing we need to do is create a variable in which we will store the queue; we will call thiscurrentQueue
and will set the value tonull
. We then need to move the last item to the beginning of the list and set the left position of theUL
item list to be0
minus the width of 1 item.$(this.carousel).queue(function () { var currentQueue = null; $(this).prepend($(this).find('li').last()); $(this).css('left',-itemWidth); });
We can then look at adding our animation. We do this in the same way we used the
jQuery.animate()
function previously; however, this time we are moving theUL
item container in a positive direction. We will also not find the need to pass a callback method as, once the animation is complete, the items will be in the correct place. The code snippet to do this is as follows:$(this.carousel).queue(function () { var currentQueue = null, var $this = $(this); $this.prepend($(this).find('li').last()); $this.css('left',-itemWidth); $this.animate({ left: "+="+itemWidth }, 500) });
We have added our animation to the queue using
.animate()
; however, this will add it to the end of the queue; if the user has clicked on the button multiple times, the animation will be a bit strange. Thus, we now need to re-sort the order of thefx
queue so that it flows correctly. To start with, we will now use thecurrentQueue
variable we declared earlier to store our queue, which can be retrieved using$(this).queue()
. We then need to move the last item of the array to be the second item in the array (so that it is fired next). We do this by using.splice()
with the index of1
,howMany
set to0
(so that we do not remove any items) and we pass the last element of the array as the value to add into the array. We can then simply use.pop()
to remove the last item from the queue as we have already moved it to the new position. We then need to update thefx
queue array with the values of the updated queue that we have manipulated.$(this.carousel).queue(function () { var currentQueue = $(this).queue(), var $this = $(this); $this.prepend($(this).find('li').last()); $this.css('left',-itemWidth); $this.animate({ left: "+="+itemWidth }, 500); currentQueue = $(this).queue(); currentQueue.splice(1, 0, currentQueue[currentQueue.length-1]); currentQueue.pop(); $(this).queue(currentQueue); });
Finally we need to add
$(this).dequeue()
at the end of the method, which will then remove this function from the queue and trigger the next item to be fired. As we just made our next item to beanimation
, ouranimation
item will now trigger.$(this.carousel).queue(function () { var currentQueue = null; $(this).prepend($(this).find('li').last()); $(this).css('left',-itemWidth); $(this).animate({ left: "+="+itemWidth }, 500); currentQueue = $(this).queue(); currentQueue.splice(1, 0, currentQueue[currentQueue.length-1]); currentQueue.pop(); $(this).queue(currentQueue); $(this).dequeue(); });
Now that we have finished this, our previous button should work, so try clicking on it. You should find that you're able to click on it multiple times with the animation remaining smooth.