• Feed RSS

How to Create a Slideshow Plugin with jQuery

How to Create a Slideshow Plugin with jQuery: "
There are hundreds of slideshow plugins out there, from all time favorites like Lightbox to the most advanced like Galleria. You might think, why make another one?. Well, most of us are using plugins like these on our websites, so why not use our own?
How to Create a Slideshow Plugin with jQuery

Introduction

If you use slideshow plugins frequently you may realize that there are basically two different types, the ones that slide and the ones with effects that don't slide. Choosing one or the other depends on the approach you take when coding it.
In this tutorial I'll discuss the creation process of Powerslide, a plugin I just released that follows the “effects” approach. You should have a basic knowledge of HTML, CSS and jQuery to follow this tutorial.

Project Setup

I suggest you download this project setup template. It'll be easier than having to create all the files and link them. It has all you need to get started with this tutorial. If you decide to use your own project template keep in mind that the relative paths might change.
This is how the project template looks:
Setup
960gs and modernizr are not necessary but they're really useful. We're not going to use a separate IE stylesheet but you might need it if you want to mess with IE later.

The Structure

We're going to start with the bare bones of the slider.
First, let's set the base HTML for the slideshow in index.html:
<div id="slider">
 <img src="img/img1.jpg" title="caption" />
 <img src="img/img2.jpg" title="caption" />
 <img src="img/img3.jpg" title="caption" />
</div>
Let's also call the jQuery plugin on document load in js/scripts.js:
$(function(){
 $('#slider').powerSlide();
});
Now, we have everything we need to start coding the plugin.
Let's take a look at the structure we want to achieve:
Structure
The following code serves only as preview. This is what we will be generating with jQuery.
<div class="powerSlide">
 <div class="wrapper">
  <a href="" class="prev"></a>
  <a href="" class="next"></a>
  <div class="image">
   <img src="" alt="" />
   <p></p>
  </div>
 </div>
 <div class="nav"></div>
</div>
We need to generate all these elements in the DOM from the original HTML. So, let's begin with a basic jQuery plugin template.
Open js/powerSlide.js.
(function($){
 $.fn.powerSlide = function(options { 

  var opt = {
   // Options
  };

  return this.each(function() {
   if (options) {
    $.extend(opt, options);
   }
   /* Code goes here */
  });
 };
})(jQuery);
Options are the variables that we can specify when calling the plugin on an element. These are the options we want to be able to change with their default values.
'width': 908, // Width and height of the images
'height': 340,
'position': 'bottom', // Position of the navigation
'bullets': false, // Show numbering navigation
'thumbs':  true, // Show thumbnail navigation
'row': 10, // Thumbnails per row
'auto': true, // Auto rotate
'autoSpeed': 4000,
'fadeSpeed': 1000
Now we can use the options with opt.option. For example, to use the width value we would write opt.width.
Inside the “each loop” and with the structure already in mind, we need to create the elements and assign them to variables so it's easier to refer to them later. Using firebug or the webkit developer tools we can track the elements as they're being created in the DOM.
/* Container and wrapper
-----------------------------------------------*/
$(this).children().wrapAll('<div class="powerSlide" />');
var container = $(this).find('.powerSlide');
container.find('img').wrapAll('<div class="wrapper" />');
var wrapper = container.find('.wrapper');

/* Previous & next buttons
-----------------------------------------------*/
wrapper.append('<a href="#" class="prev">Prev</a><a href="#" class="next">Next</a>');

/* Navigation & captions
-----------------------------------------------*/
switch (opt.position) { // Navigation position
  case 'top': container.prepend('<div class="nav" />'); break;
  case 'bottom': container.append('<div class="nav" />'); break;
}

var nav = container.find('.nav');

wrapper.find('img').each(function(i){

 i += 1; // Start numbers at 1

 if (opt.bullets === true) { // Bullet navigation
   nav.append('<a href="#">'+ i +'</a>');
 }

 if (opt.thumbs === true) { // Thumbnail navigation
   nav.addClass('thumbs').append(
    '<img class="thumb" src="'+
    $(this).attr('src') +'" alt=""/>');
 }

 // Captions
 var title = $(this).attr('title');
 $(this).wrapAll('<div class="image" />');
 if (title !== undefined) {
   $(this).attr('title', '');
   $(this).after('<p>'+ title +'</p>');
 }
});
You can now try changing the plugin options values to test different situations. Once the elements are created we can dive into the css.
This is just for the layout, but we will create a separate theme file for all the color customization and CSS3 goodness. This will help to keep the code clean and it'll be easier to create new themes later on.
Let's open css/powerSlide.css.
/* Wrapper
-------------------------------------------*/
.powerSlide .wrapper {
 overflow: hidden;
 padding: 15px;
 position: relative;
}

/* Image
-------------------------------------------*/
.powerSlide .wrapper img {
 position: absolute; /* They key to the “effects” approach */
}

/* Prev & Next buttons
-------------------------------------------*/
.powerSlide a.prev,
.powerSlide a.next {
 display: none;
 margin-top: -1em; /* Same as padding-top */
 padding: 1em 2em;
 position: absolute;
 text-decoration: none;
 top: 50%;
}
.powerSlide a.next {
 right: 0;
}

/* Caption
-------------------------------------------*/
.powerSlide .wrapper p {
 bottom: 0;
 display: none;
 padding: 1.5em;
 position: absolute;
}

/* Navigation
-------------------------------------------*/
.powerSlide .nav {
 margin: .5em 0;
 overflow: hidden;
}
.powerSlide .nav.thumbs {
 padding: 15px; /* Same as wrapper padding */
}
.powerSlide .nav img.thumb {
 cursor: pointer;
 float: left;
 margin: 2px;
 position: relative;
}
.powerSlide .nav img.thumb.current { /* Current thumbnail */
 z-index: 999;
}
.powerSlide .nav a {
 float:left;
 margin: .2em;
 min-width: 1em;
 padding: .2em .7em;
 text-align: center;
 text-decoration: none;
}
.powerSlide .nav a.current {} /* Current bullet */
As you can see the CSS is pretty straightforward. Elements that are inside the container have to be absolute positioned. Also be careful with margins and padding. We will generate the dimensions later on with jQuery.

The Slider Object

To store all the information and actions of the slider we're going to use an object. The most important concept of a slider is the index. We need to know the index of any given image at any time. The index will allow us to have control over which image needs to be shown when we trigger an event.
Here's a list of all the variables and a short description of what each one does.
  • imgs: the selector for all images.
  • imgCount: the number of images in the selection.
  • navNext: the next button.
  • navPrev: the previous button.
  • bullets: selector for all bullets in numbered navigation.
  • thumbs: selector for all thumbnails in thumbnail navigation.
  • captions: selector for all captions.
  • getCurrentIndex(): get the index of the current image at any given time.
  • go(index): go to an image of any given index.
  • next(): go to the next image.
  • prev(): go to the previous image.
  • init(): set width and height of the slideshow and calculte dimensions of dynamic elements.
/* Slider Object
-----------------------------------------------*/
var Slider = function(){

this.imgs = wrapper.find('div.image');
this.imgCount = (this.imgs.length) - 1; // Match index
this.navPrev = wrapper.find('a.prev');
this.navNext = wrapper.find('a.next');
this.bullets = container.find('.nav a');
this.thumbs = container.find('.nav img.thumb');
this.captions = this.imgs.find('p');

this.getCurrentIndex = function(){ // Index
 return this.imgs.filter('.current').index();
};

this.go = function(index){ // Rotate images
 this.imgs
  .removeClass('current')
  .fadeOut(opt.fadeSpeed)
  .eq(index)
  .fadeIn(opt.fadeSpeed)
  .addClass('current');
 this.bullets
  .removeClass('current')
  .eq(index)
  .addClass('current');
 this.thumbs
  .removeClass('current')
  .eq(index)
  .addClass('current');
};

this.next = function(){
 var index = this.getCurrentIndex();
 if (index < this.imgCount) {
  this.go(index + 1); // Go next
 } else {
  this.go(0); // If last go first
 }
};

this.prev = function(){
 var index = this.getCurrentIndex();
 if (index > 0) {
  this.go(index - 1); // Go previous
 } else {
  this.go(this.imgCount); // If first go last
 }
}; 

this.init = function(){ // Init
 wrapper
  .width(opt.width)
  .height(opt.height); /* Set width and height */

 this.imgs.hide().first().addClass('current').show(); /* Set current image */
 this.bullets.first().addClass('current');
 this.thumbs.first().addClass('current');

 // Dimensions for thumbnails and captions
 var padding = wrapper.css('padding-left').replace('px', '');
 var captionsPadding = this.captions.css('padding-left').replace('px', '');
 nav.width(opt.width);
 if (opt.thumbs === true) { // thumbs
  var thumbBorder = this.thumbs.css('border-left-width').replace('px', '');
  var thumbMargin = this.thumbs.css('margin-right').replace('px', '');
  var thumbMaxWidth = opt.width/opt.row;
  this.thumbs.width( (thumbMaxWidth - (thumbMargin * 2)) - (thumbBorder * 2) );
 }
 this.captions // captions
  .width(opt.width - (captionsPadding * 2) + 'px')
  .css('margin-bottom', padding + 'px');
 this.navNext.css('margin-right', padding + 'px');
   };

};
Now that the object is defined we have to create a new instance and load it with the init() function.
var slider = new Slider();
slider.init();

Events

Mouse Events

We want to trigger the following events:
  • Click next and go to next image
  • Click prev and go to previous image
  • Click on a numbered bullet and go to that image
  • Click on thumbnail and go to that image
  • Hover image and show caption and buttons
We have to disable the default behavior of the link items with e.preventDefault() to avoid jumping to the top of the page when clicking on empty links.
Also make sure you hide the captions when not hovering over the image. I think the code for this part could be DRYer but it's good for readability.
wrapper.hover(function(){ // Hover image wrapper
 slider.captions.stop(true, true).fadeToggle();
 slider.navNext.stop(true, true).fadeToggle();
 slider.navPrev.stop(true, true).fadeToggle();
});
slider.navNext.click(function(e){ // Click next button
 e.preventDefault();
 slider.next();
});
slider.navPrev.click(function(e){ // Click previous button
 e.preventDefault();
 slider.prev();
});
slider.bullets.click(function(e){  // Click numbered bullet
 e.preventDefault(); slider.captions.hide();
 slider.go($(this).index());
});
slider.thumbs.click(function(){ // Click thumbnail
 slider.captions.hide();
 slider.go($(this).index());
});

Auto Rotate Images

This part is kind of boring, just make sure the caption is hidden when not hovering over the image.
if (opt.auto === true) {
 var timer = function(){
   slider.next();
   slider.captions.hide();
 };
 var interval = setInterval(timer, opt.autoSpeed);

 // Pause when hovering image
 wrapper.hover(function(){clearInterval(interval);}, function(){interval=setInterval(timer, opt.autoSpeed);});

 // Reset timer when clicking thumbnail or bullet
 slider.thumbs.click(function(){clearInterval(interval); interval=setInterval(timer, opt.autoSpeed);});
 slider.bullets.click(function(){clearInterval(interval); interval=setInterval(timer, opt.autoSpeed);});
}

Creating a Theme

Everything should be working at this point. Let's make it pretty then.
Create the file css/powerSlide_theme.css and include it in index.html.
<link href="css/powerSlide_theme.css" rel="stylesheet" type="text/css" media="screen"/>
And with the help of Colorzilla CSS3 gradient generator let's add some styles.
/* Wrapper
-------------------------------------------*/
.powerSlide .wrapper {
 background:#fff;
 border:1px solid #999;
}

/* Prev & Next buttons
-------------------------------------------*/
.powerSlide a.prev,
.powerSlide a.next {
 background:#fff;
 box-shadow:2px 0 2px rgba(0,0,0,.3);
 color:#000;
 font:bold 10px Arial;
}
.powerSlide a.next {
 box-shadow:-2px 0 2px rgba(0,0,0,.3);
}

/* Caption
-------------------------------------------*/
.powerSlide .wrapper p {
 background:#000;
 background:rgba(0,0,0,.7);
 color:#fff;
}

/* Navigation
-------------------------------------------*/
.powerSlide .nav.thumbs {
 background:#b5bdc8;
 border:1px solid #999;
}
.powerSlide .nav img.thumb {
margin: 3px;
 box-shadow:0 0 2px #666;
 border: 4px solid transparent;
 filter:alpha(opacity=40);
 opacity:.4;
}
.powerSlide .nav a {
 background:#7d7e7d;
 background:-moz-linear-gradient(top, #7d7e7d 0%, #0e0e0e 100%);
 background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#7d7e7d), color-stop(100%,#0e0e0e));
 background:-webkit-linear-gradient(top,#7d7e7d0%,#0e0e0e100%);
 background:-o-linear-gradient(top,#7d7e7d0%,#0e0e0e100%);
 background:-ms-linear-gradient(top,#7d7e7d0%,#0e0e0e100%);
 background:linear-gradient(top,#7d7e7d0%,#0e0e0e100%);
 border-radius:3px;
 color:#fff;
 filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7d7e7d',endColorstr='#0e0e0e',GradientType=0);
 font:bold 12px Arial;
}
.powerSlide .nav img.thumb.current {
 box-shadow:0 0 10px #fff;
 border: 4px solid #fff;
 filter:alpha(opacity=100);
 opacity:1;
}
.powerSlide .nav a.current {
 background:#1e5799;
 background:-moz-linear-gradient(top, #1e5799 0%, #2989d8 50%, #207cca 51%, #7db9e8 100%);
 background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#1e5799), color-stop(50%,#2989d8), color-stop(51%,#207cca), color-stop(100%,#7db9e8));
 background:-webkit-linear-gradient(top,#1e57990%,#2989d850%,#207cca51%,#7db9e8100%);
 background:-o-linear-gradient(top,#1e57990%,#2989d850%,#207cca51%,#7db9e8100%);
 background:-ms-linear-gradient(top,#1e57990%,#2989d850%,#207cca51%,#7db9e8100%);
 background:linear-gradient(top,#1e57990%,#2989d850%,#207cca51%,#7db9e8100%);
 filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#1E5799',endColorstr='#7db9e8',GradientType=0);
}

Notes

The plugin works in IE7+ and all other modern browsers. For some reason, even in IE9, the images don't fade. This problem must be related to the fadeIn and fadeOut jQuery functions.
Bullets and thumbnails can't be displayed at the same time but who wants to have them both anyway?
I will be glad to answer any questions. I encourage you to create your own theme and post it here. It'll be great to have a community theme repository.

Written by: Cedric Ruiz
"