Share!

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.

Advertise with us

Author

Cedric Ruiz is a freelance graphic and web designer/developer with a passion for computers and technology. He actually studied 3D animation but took a totally different path. He needs to learn something new everyday and he loves challenges and teaching people new skills.

18 Comments

Say Something
  1. July 1, 2011 at 9:26 pm

    Wow, impressed by the post. Everything is clear enough.
    I've used such plug-ins doing http://www.websitetemplates.bz/oscommerce-templates/decor-oscommerce-template_1161.html

  2. July 2, 2011 at 4:50 am

    You should check out my lightweight and fully customizable content fader/slider on github (http://github.com/jensscherbl/jQuery-Billboard).

    Markup is similar to what you suggest in your article, plus it's clean coded following the jQuery plugin guidelines.

    Oh, and it works fine in IE, by the way... ;-)

  3. July 2, 2011 at 5:20 am

    I think this website just became my #1 website for new tools on the web 2.0 era, very nice slideshow came out of this code, thank you very much.

  4. Anna Blume
    July 2, 2011 at 7:41 pm

    Please, how do I get Links in the captions or photos?

  5. July 3, 2011 at 1:27 am

    Really solid and well written tutorial - thanks for sharing.

  6. July 3, 2011 at 7:22 am

    Nice run through a plugin development, just what I was looking for!
    Thanks

  7. July 3, 2011 at 8:27 pm

    nice tutorial! is it possible to use the picture thumbnails instead of numbers?

    • Cedric Ruiz
      July 6, 2011 at 3:02 pm

      This tutorial covers how to create numbers as well as thumbnails. You have to pass the value when you call the plugin.
      I created a theme switcher with all the different options and themes available at the powerSlide website.
      http://jqpowerslide.com

      • July 6, 2011 at 4:02 pm

        cool.. thanks for pointing me to the right direction. cheers mate!

  8. John
    July 3, 2011 at 8:59 pm

    While "awesome post!" or "Thanks :)" would definitely sum things up..

    The main reason I exceedingly enjoyed this article is that it doesn't have a microscopic scroll bar and a harry potter book worth of text walking you through each step. I much prefer the clear straight forward approach.

    Easy to understand and great end result.

  9. July 6, 2011 at 12:14 am

    Hi there.
    For of all, congrats. The slideshow it selfs is looking nice and works well
    But a found a issue with the setInterval function.
    I didnt manage to find what it was. the problem is that after some time the images star to pass to fast and the clearInterval function stops clearing the intervals...and in the end get gets kind of bugged.
    Havent tested in other browser (im using chrome)
    Nice Work

    • Cedric Ruiz
      July 6, 2011 at 2:53 am

      Yes, I noticed that bug too. It happens with chrome when you leave the window and then come back after a while.

      • nei
        August 28, 2011 at 5:50 pm

        Same here

  10. July 8, 2011 at 10:06 pm

    Great tutorial! Do you think it would be possible to trigger the navNext and navPrev by a swipe on a tablet such as the iPad?

  11. Ben Bruner
    December 8, 2011 at 4:59 am

    Excellent tutorial, this is exactly what I was looking for!!

    ** I believe I found the error that was occuring in IE. It appears that IE when the div containing the image has the opacity applied, it is not applying the opacity to the image within the div. Therefore, I just added a short bit of code within the 'this.go' function to apply the fadeIn and fadeOut to the two images affected as well. After doing that it seemed to work fine in all browsers I tested.

    Thanks again for the simple and easy to follow tutorial.

  12. December 22, 2011 at 10:02 pm

    Great tutorial.
    This was exactly what I was looking for.

    Only thing is that I have the same bug in IE as described in the comments.
    I have tried to change the code in this.go but Im not satisfied with the result.

    Do you have an idea how to solved the bug?

    Thanks!

  13. Sid
    February 10, 2012 at 9:12 pm

    Awesome tutorial
    Can you tell me the code to change theme and type
    Means how can I make the option to change theme and to change type...
    Please

  14. Sid
    February 10, 2012 at 10:53 pm

    Hey
    And also can you tell me that how to move it in center
    Please

Please note that comments are moderated - and rel="nofollow" is in use. Please no link dropping, no keywords or domains as names. Kindly do not spam, and do not advertise!