jQuery: creating a slideshow from scratch

The theory behind a jQuery slideshow is one of the most neglected topics in a million. Many web sites provide excellent tutorials on how to create specific slideshows for specific scenarios, but there are no tutorials covering the theory and the basics of a jQuery slideshow. In short, you're a developer who wants to create a slideshow for his/her client. You've tried to use an existing plugin but the client's needs are very specific and the chosen plugin doesn't fit the bill. So you need to understand how a jQuery slideshow works in order to create a custom one. Let's get to work.

Choosing the right markup

A slideshow is made up of solid and robust HTML markup. It doesn't matter if you choose to use HTML5, XHTML or HTML 4: it's the structure that makes the difference, not the preferred DTD.

There are several parts that make up a slideshow, which include:

  1. Outer container: this is the general wrapper of your slideshow that you can stylize with CSS to exactly position the whole slideshow on a specific section of a page.
  2. Inner container: this is the wrapper of your slide elements, which can either be images or other elements.
  3. Slides: slides can be either img or div elements, depending on the slideshow type.
  4. Controls: controls are the canonical "Next" and "Previous" buttons or a series of links containing thumbnails used for navigating the slides.

Here's the structure of a recent demo of mine:

<div id="slideshow-container">
<div id="slideshow">
 <div id="slideshow-wrapper">
  <img src="1.jpg" alt="" id="s1" />
  <img src="2.jpg" alt="" id="s2" />
  <img src="3.jpg" alt="" id="s3" />
  <img src="4.jpg" alt="" id="s4" />
  <img src="5.jpg" alt="" id="s5" />
  <img src="6.jpg" alt="" id="s6" />
  <img src="7.jpg" alt="" id="s7" />
  <img src="8.jpg" alt="" id="s8" />
  <img src="9.jpg" alt="" id="s9" />
  <img src="10.jpg" alt="" id="s10" />
  <img src="11.jpg" alt="" id="s11" />
  <img src="12.jpg" alt="" id="s12" />
  
  
 </div>
</div>
<div id="slideshow-thumbs">
 <a href="#" rel="s1"><img src="1.jpg" alt="" /></a>
 <a href="#" rel="s2"><img src="2.jpg" alt="" /></a>
 <a href="#" rel="s3"><img src="3.jpg" alt="" /></a>
 <a href="#" rel="s4"><img src="4.jpg" alt="" /></a>
 <a href="#" rel="s5"><img src="5.jpg" alt="" /></a>
 <a href="#" rel="s6"><img src="6.jpg" alt="" /></a>
 <a href="#" rel="s7"><img src="7.jpg" alt="" /></a>
 <a href="#" rel="s8"><img src="8.jpg" alt="" /></a>
 <a href="#" rel="s9"><img src="9.jpg" alt="" /></a>
 <a href="#" rel="s10"><img src="10.jpg" alt="" /></a>
 <a href="#" rel="s11"><img src="11.jpg" alt="" /></a>
 <a href="#" rel="s12"><img src="12.jpg" alt="" /></a>
</div>
<div id="controls">
   <a href="#" id="previous">Previous</a>
   <a href="#" id="next">Next</a>
  </div>
</div>

Let's make this code a little bit clearer. This is an image slideshow and is made up of the following parts:

  1. slideshow-container is the outermost wrapper of the whole slideshow
  2. slideshow is the outermost wrapper of the slides
  3. slideshow-wrapper is the innermost wrapper of the slides
  4. each slide has a progressive ID (s1, s2, etc.) which uniquely identifies each image
  5. slideshow-thumbs contains a series of linked thumbnails of the images; each link has a rel attribute set on the same value of the corresponding ID of each image so that now thumbnails and images are linked together
  6. controls contains the traditional "Previous" and "Next" buttons.

The above structure is surely one of the most widely used in the overwhelming majority of slideshows. In this case the controls and navigation sections come after the slide series, but you can revert this structure by inserting them just before the outermost slide wrapper.

Using CSS

CSS performs a preparatory task: it simply lays out the slideshow in the way you want it to appear. As you surely noticed from the code above, we have 12 images. Each image is approximately 650 pixels wide and 440 pixels tall. But we want to display only one large image at time, so how we can do?

We could use floating to align images on the same line, but our wrapper should be 7,800 pixels wide, so how this could be? It could be one way: we use the overflow property combined with floating so that only one image will be always visible:

#slideshow-container {
 width: 650px;
 margin: 2em auto;
 position: relative;
}

#slideshow {
 width: 100%;
 height: 440px;
 position: relative; 
 z-index: 1;
 overflow: hidden;
}

#slideshow-wrapper {
 width: 7800px;
 height: 440px;
 position: relative;
}

#slideshow-wrapper img {
 width: 650px;
 height: 440px;
 float: left;
}

Our innermost image wrapper is actually 7,800 pixels wide, but only a 650 x 440 box will be visible thanks to the hidden overflow. Now our images are all laid out on the same line, but we can see only one image at time when the slideshow slides (no pun intended).

Now you may ask: "Why position: relative?". This is due to two reasons: first, IE 6 and 7 have a problem with the overflowing content and this declaration fixes the bug; second, we need to create a contextual positioning for our next/previous buttons:

#controls a {
 display: block;
 width: 29px;
 height: 78px;
 text-indent: -1000em;
 background-repeat: no-repeat;
}

#controls #previous {
 background-image: url(arrow-left.png);
 float: left;
 position: relative;
 left: -29px;
}

#controls #previous:hover {
 background-position: 0 -78px;
}

#controls #next {
 background-image: url(arrow-right.png);
 float: right;
 position: relative;
 right: 0;
}

#controls #next:hover {
 background-position: 0 -78px;
}

We've simply vertically centered our controls and put them on the opposite edges of the main content area so that they will be shown each one on a different image side ("Previous" on the left, "Next" on the right).

Finally, our thumbnail navigation:

#slideshow-thumbs {
 margin-top: 1em;
 width: 100%;
 height: 50px;
}

#slideshow-thumbs a {
 float: left;
 width: 54px;
 height: 100%;
}

#slideshow-thumbs a img {
 display: block;
 width: 100%;
 height: 100%;
 border: none;
}

Adding jQuery

We have to implement three actions with jQuery:

  1. An automatic sliding
  2. Next/Previous navigation
  3. Thumbnail navigation

Let's focus on the sliding first. As you remember, we've a series of floated images. Each image is 650 x 440 pixels wide and its wrapper is globally 7,800 pixels wide. Since the wrapper is relatively positioned, each image has its own left offset calculated from the left edge of the container. So the first images has an offset of 0, the second has 650, the third 1300 and so on. So far so good. Each image has an offset, but what's the point with knowing this?

Here's the point: we want to make the slide wrapper move from right to left and from left to right by adjusting its left property. By doing so, we'll use the left offset of each image to make the current image appear. Very simple.

But first things first: let's wrap our code with an object:

var Slideshow = new function() {

 // code here

}();

Now we need to define some initial variables for later use:

  1. a reference to a JavaScript timer defined later and used for the automatic sliding
  2. a global index initially set to 0 to get the current image
  3. a reference to the image wrapper
  4. a reference to the images themselves
  5. a reference to the thumbnail links
  6. a reference to the previous/next buttons
var timer = null,
     index = 0,
     wrapper = $('#slideshow-wrapper', '#slideshow'),
     slides = $('img', wrapper),
     thumbs = $('a', '#slideshow-thumbs'),
     previous = $('#previous', '#controls'),
     next = $('#next', '#controls');

Next step is to store all the image offsets in an array that can be accessed through our global index:

var getSlidePositions = function() {
 
  var positions = [];
  slides.each(function(i) {
  
   var left = $(this).position().left;
   
   positions[i] = left;
  
  });
  
  return positions;
 
};

Thumbnail links are associated with the large images through the rel attribute but they're not aware of the offset of each image, so we need now to associate each image with its left offset through the jQuery's attr() method:

var addOffsetsToImages = function() {
 
  slides.each(function() {
  
   $(this).attr('position', $(this).position().left);
  
  });
 
};

The preparatory work is done. Now we can create our automatic sliding by incrementing and decrementing our global index to access the offsets array in a JavaScript timer:

var autoSlide = function() {
 
  var offsets = getSlidePositions();
  timer = setInterval(function() {
  
      index++;
      
      if(index == offsets.length) {
      
       index = 0;
      
      }
  
   wrapper.animate({
    left: - offsets[index]
   }, 1000, function() {
   
    thumbs.eq(index).animate({
     opacity: 0.5
    }, 500, function() {
    
     $(this).animate({
      opacity: 1
     }, 500);
    
    }); 
   
   });
  
  
  
  }, 2000);
  
 
 
};

Together with making the image wrapper slide, this timer also animates the current image thumbnail by adjusting its opacity. Thumbnails are our next step. We need to associate an action that makes the current image slide into view. In this case, our timer needs to be stopped when we click on a thumbnail and restarted when the sliding movement ends:

var handleThumbLinks = function() {
 
  thumbs.each(function() {
  
   var $a = $(this);
   var imgId = $('#' + $a.attr('rel'));
   var $offset = imgId.attr('position');
   
   $a.click(function(event) {
   
    clearInterval(timer);
    
    wrapper.animate({
     left: - $offset
    }, 1000, function() {
    
     autoSlide();
    
    });
   
    event.preventDefault();
   
   });
  
  });
 
};

The triggered action uses as left offset the current's image position attribute previously set. It also uses the link's rel attribute to target the current ID of the image.

The last action we need to implement is the handling of next/previous buttons:

var handlePrevNextButtons = function() {
 
     var offsets = getSlidePositions();
 
  previous.click(function(event) {
      
      clearInterval(timer);
  
      index--;
  
   if(index == 0) {
   
    index = 0;
   
   }
   
   wrapper.animate({
    left: - offsets[index]
   }, 1000, function() {
   
    autoSlide();
   
   });
   
  
   event.preventDefault();
  
  });
  
  next.click(function(event) {
      
      clearInterval(timer);
  
      index++;
  
   if(index == offsets.length) {
   
    index = 0;
   
   }
   
   wrapper.animate({
    left: - offsets[index]
   }, 1000, function() {
   
    autoSlide();
   
   });
   
  
   event.preventDefault();
  
  });

 
 
};

In this case we also need to check whether our global index is within the range given by the number of images and act accordingly. Finally, we have to define a public function to kicks things off:

this.init = function() {
 
  addOffsetsToImages();
 
  autoSlide();
  
  handleThumbLinks();
  
  handlePrevNextButtons();
 
};

Usage:

$(function() {

 Slideshow.init();

});

You can see the demo below.

Demo

Live demo

This entry was posted in by Gabriele Romanato. Bookmark the permalink.

One thought on “jQuery: creating a slideshow from scratch”

Leave a Reply

Note: Only a member of this blog may post a comment.