Sunday, October 9, 2011

A fancier slideshow: thumbnails and a moving highlight

Two weeks ago I showed you how to create a simple image slideshow for your web pages. Last week, I showed you how to create an animated link highlight that responds to the mouse hovering over links. This week, I'm going to show you how to combine those two to create a fancier slideshow, like this example.

What we'll be working with
  • HTML to create the structure
  • CSS to define the style
  • jQuery to create the animation and interactivity
I'm going to try out a slightly different writing style today, to try to accommodate readers with different levels of web coding expertise. I'm not going to go on explaining details of syntax like I have in the last two posts. I will, however, create lots of links within the chunks of code that I show, which will link to relevant pages where you can learn more about that particular CSS property, jQuery function, etc. That way, those of you who already know the syntax won't be bored by my explanations, and those of you who need to be reminded what a particular thing does can go to a page that will do a much better job of explaining than I can. This should also allow me to focus on explaining the logic of my code as clearly as possible.

Let's get started
This tutorial builds on the basic slideshow that I showed you how to create two weeks ago. I am not going to repeat the content of that tutorial. If you still have the files you created then, good for you. If not, go back and work through that tutorial before you read this one.

So you should already have a basic slideshow that looks something like this when viewed in a web browser. Starting from there, let's get fancy.

In the HEAD tag of your HTML code for that slideshow, you should see something like this:

<script type="text/javascript">
$(document).ready(function(){
    $("#slideshow").cycle();
});
</script>
We're going to insert two short new scripts between the second and third lines of that chunk (between "$(document).ready(function(){" and "$("#slideshow").cycle();". Here they are:
$("#slideshow").after('<div id="pager">');
$("#pager").prepend('<span id="highlight"></span>');
The first script finds the element with the ID "slideshow" and creates a new DIV just after it with the ID "pager". The second script targets this new DIV with the ID "pager" and creates a SPAN with the ID "highlight" inside it. The full HTML syntax of the document would now look like this:
<div id="slideshow">
    <img src="slide1.jpg" />
    <img src="slide2.jpg" />
    <img src="slide3.jpg" />
</div>
<div id="pager">
    <span id="highlight"></span>
</div>
The DIV with the ID "pager" is where the thumbnails of the slides are going to go. But first, let's switch to CSS and define some styles.

Here's the whole block of CSS to put in your HEAD tag, just after the TITLE tag and before the first SCRIPT tag:

<style type="text/css">
#pager {
    width: 200px;
    position: relative;
}

#pager a {
    position: relative;
}

#pager a img {
    width: 50px;
    height: 50px;
}

#highlight {
    display: block;
    width: 44px;
    height: 44px;
    border: 3px solid black;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 10;
}
</style>
If you need a reminder of how absolute positioning works, especially related to the sliding highlight, please refer to my sliding highlight tutorial from last week.

There are a few things here that differ from my sliding highlight tutorial. First, the height and width of both the image thumbnails and the sliding highlight are defined in the CSS. The image thumbnails have their height and width defined because they are actually the same images that are in the slideshow, with their natural size being 200 pixels square -- the CSS just shrinks them down to 50 pixels square.

The sliding highlight has its height and width defined because all the thumbnails are the same size, so the highlight's size doesn't need to be dynamically changed by a script. You should notice that the highlight's height and width are each set to only 44 pixels, not 50, but if you take the extra 3-pixel border on all sides into account, it actually comes out to the same thing. This is due to the CSS box model, which you can read about here.

Second, the highlight's "left" property is set in the CSS. This is because we know the highlight's going to start out over the left-most thumbnail, and don't need to calculate where to have it start.

Third, there is no spacing between these thumbnails, unlike the links in last week's tutorial. This is just to make the code simpler.

Now, for the script that makes it all work. In your scripts, replace the line that reads "$("#slideshow").cycle();" with this:

$("#slideshow").cycle({
    speed: 500,
    pager: '#pager',
    pagerAnchorBuilder: function(idx,slide){
        return '<a href="#"><img src="' + slide.src + '" /></a>';
    },
    before: function(){
        $("#highlight").animate({
            left: $(this).index()*50
        },500);
    }
});
Quite a bit to take in, isn't it? Let's try to break it down. The first line just calls the Cycle plugin and applies it to the slideshow, like in my basic slideshow tutorial. The curly braces -- { } -- mean we're going to define some options for the Cycle plugin.

First, we define the speed of the transition as 500 milliseconds. You don't necessarily need to do this, but setting the speed allows you to synchronize exactly with the movement of the highlight.

Second, we set the pager to be the HTML element with the ID "pager", which we created with an earlier script. This tells the Cycle plugin to find this element and fill it with HTML elements that will allow someone viewing the slideshow to manually change the slide.

Third, we define what those HTML elements will be with the "pagerAnchorBuilder" option. If we didn't define this option, the Cycle plugin would automatically create A (link) tags with the numbers of the slides. As it is, we've basically told it to create a series of A tags, each containing an IMG tag that is a duplicate of its corresponding slide. These images will be shrunk by the CSS we defined earlier.

Fourth and finally, we define the "before" option as a function that will move the highlight. For an explanation of how the "animate" method in jQuery is being used here, see my sliding highlight tutorial. Basically, the Cycle plugin activates this function before it starts to transition from one slide to another.

First, the function finds the index of the slide that is being transitioned to -- "$(this).index()". That means it finds the place in the list of slides that the slide being transitioned to occupies. JavaScript and jQuery count from zero, so the first slide would have an index of 0, the second an index of 1, the third an index of 2, and so on if there were more slides.

The function then multiplies this value by 50 (the width of the thumbnails) and applies the result to the "left" property of the highlight element. Basically, if the slideshow is transitioning to the second slide, the function will move the highlight to be 50 pixels (index 1 x 50) to the left of the left edge of its container element, which is the DIV with the ID "pager". This will make the highlight line up over the second thumbnail. If transitioning to the third slide, the function will move the highlight to be 100 pixels (index 2 x 50) to the left of the left edge of its container, lining up over the third thumbnail. You should get the idea by now.

And that's it! If you've followed along carefully, you should have something resembling my example. If there's anything I haven't made sufficiently clear, or if there's any visual gimmicks you'd like me to explain in future posts, please let me know in the comments!

3 comments:

  1. I appreciate the way you have your initial blog page formatted with a link to read more.

    Thanks for the great explanation of how to format this slideshow. Your writing makes it easy to follow and understand what's going on.

    FYI . . . when viewed in Firefox, your images have the ugly, blue link border around them. Also, the black boxes don't exactly match the images they appear over. I'm sure both of these are minor adjustments.

    ReplyDelete
  2. Both those problems should be solved by adding a property to the CSS rule "#pager a img". Simply add the "border" property with a value of "none". So that rule would now look like this:

    #pager a img {
     width: 50px;
     height: 50px;
     border: none;
    }

    Newer versions of Firefox no longer display blue link borders around images by default, which is why I forgot to include that property in the example in the first place. It has now been updated.

    ReplyDelete