Animated Image Captions

I'd like to start a little series of tutorials here on the ol' blog. Both as a resource for other web pros out there and to act as a sort of skills archive for my personal purposes. I figured I'd start with something I did right here on tysonwilley.com.

In the Work section, and on the homepage you'll notice all the thumbnails have a snazzy little roll over effect that reveals the picture caption. I'd like to show you how to do it.

Markup

First things first, as with anything with front end development, we start with the markup. It's the foundation of everything you do and it's elementally important that it's strong and well formed. Below is an example of the finished markup which we'll dissect further.

<a href="somePage.html" class="thumb">
	<img src="someImg.png" alt="Image Title" />
	<div class="caption">
		Caption Text Goes Here
	</div>
</a>

Essentially what we've got here is an anchor tag with nested image and div tags inside. If you look at your page with just this basic markup, it should look something like this:

Caption Example

Not the most beautiful thing in the world, but definitely something any user could understand, it's a photo with a caption.

Styling

Okay, step 2, bringing the pretty.

We're going to be taking advantage of the overflow property and some absolute positioning, both of which I've seen cause some trouble for lots of people. So again, below is the CSS and we'll dissect it.

a.thumb {
	border: 1px solid #eaeaea;
	display: block;
	height: 123px;
	overflow: hidden;
	position: relative;
	text-decoration: none;
	width: 288px;
}

 a.thumb .caption {
	background: #555;
	color: #eaeaea;
	display: block;
	font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
	font-size: 18px;
	font-weight: 300;
	line-height: 27px;
	padding: 8px;
	position: absolute;
	width: 274px;
}

a.thumb img {
	border: none;
}

a.thumb:hover .caption {
	bottom: 0;
}

You'll notice we've got 4 separate CSS definitions here. The first is to set up the containing anchor tag.

So what we're essentially saying is any <a> with a class of thumb will look like this… most the properties here are pretty self explanatory, but I want to focus on the overflow and position properties for a moment.

Firstly, we've set overflow to hidden. What this does is tell the browser that any content within this element that exceeds the bounds we have set to match the dimensions of our thumbnail (width of 288px and height of 123px) is to be cut off. It's just not shown. This allows us to push the caption out of the way when we're not rolled over.

Secondly, we've set the position property to relative. This is to tell the browser that anything within this element that is positioned absolutely will be based on this elements position and not the browser window.

Next we're styling the <div class="caption"> tag inside our containing anchor. The main property to take away from this is the position property again. It's set to absolute so we can have more control over placement on the fly.

Finally we're defining the hover styling of our <div class="caption"> tag. All we've done is set the bottom property to 0 which aligns the bottom of the div tag with the bottom of the containing anchor tag, or 0 pixels from the bottom of the container. Simple, right?

You may have noticed that the finished product utilized a smooth, animated sliding effect when rolled over and all we've got is this pop in/out thing… Well you're right, but we're using jQuery to accomplish that and with anything on the web, accessibility is a major factor. Not everyone has Javascript enabled in their browser. What about them? By styling the hover state, we can ensure that any user can see the caption of the photo regardless of their browser configuration. We'll add the animation in the next step.

Javascript/jQuery

The last step is adding the smooth sliding animation. To do that, we're going to use Javascript. Well, more specifically we'll use the wonder framework that John Resig and crew have provided the world, jQuery. If you're not familiar with jQuery, not to fear, it's pretty self explanatory if you understand CSS Selectors and there's a ton of documentation on it over at jquery.com.

Essentially, what we want to do is capture the mouseover/mouseout event and animate the motion of the div.caption element. Here's the code and we'll break it apart below.

<script 
	type="text/javascript" 
	src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js">
</script>
<script type="text/javascript">

	function initThumbs(){
		var thumbs = $(".thumb");		
		thumbs.hover(thumbOver, thumbOut);
	}

	function thumbOver (){
		var	caption = $('.caption', this),
			height = $(caption).outerHeight();
		
		$(caption).css({"bottom": "-"+height+"px"});				
		$(caption).animate({"bottom": "0"}, "fast");
	}

	function thumbOut (){
		var	caption = $('.caption', this),
	       		height = $(caption).outerHeight();
		
		$(caption).animate({"bottom": "-"+height+"px"}, "fast");
	}

	$(initThumbs);

</script>

As I mentioned briefly, jQuery uses CSS style selectors to navigate the DOM. So, to return an array of all the elements in the document with a class name of "thumb", we would structure our jQuery method call like so:

var thumbs = $('.thumb');

Pretty easy, right?

The initThumbs function is pretty simple, all we're doing is filling an array with all the elements in the DOM with the class name of "thumb" then assigning them all actions to take when they are hovered over.

To capture the roll over and roll out events, we'll use the built in jQuery method hover(). This method takes two functions as arguments, the first being what to do when rolled over, the second being what to do when rolled out. In this case, when rolled over, execute the thumbOver function, when rolled out, execute the thumbOut function.

thumbs.hover(thumbOver, thumbOut);

Once an event is executed, the scope of the called function becomes that of the element that the event is fired on, meaning we can use the this keyword in our thumbOver and thumbOut functions to reference the specific thumbnail we're rolled over. This is useful because it allows us to write generic functions that will work on any element that is structured basically the same, but not necessarily exactly the same.

jQuery is pretty cool in that you can set a base parent element for your selector to be limited to. This is where we're going to use the this keyword. We want to get the div.caption element from within the thumbnail being rolled over and only that element.

var caption = $('.caption', this);

Okay, so now we need to some variables before we can start animating. In short, we need to get the height of the caption so we know how far to push it down to hide it and where to place it before it's revealed so we can ensure the animation works the same every time. To do that, all we need is another jQuery native method: outerHeight().

outerHeight calculates the overall height, in pixels, of an element with padding and borders thrown in. Once we have the height of the caption element, we then know that by setting the caption's bottom CSS property to -height, it will be hidden just below the viewing threshold of the thumbnail.

Now all we have to do is set up our animations.

jQuery's animate() method does all the work for us. All we have to do is pass it an object describing the CSS properties we want to animate, and tell it how fast to do it, like so:

$(caption).animate({"bottom": "0"}, "fast");

to bring the caption into view, and

$(caption).animate({"bottom": "-"+height+"px"}, "fast");

to hide it.

Lastley, we don't want anything to happen until the DOM is fully loaded, so we'll take advantage of jQuery's ready() method to call our initThumbs function once everything is there. This line does that:

$(initThumbs);

which is shorthand for

$(document).ready(initThumbs);

In Conclusion

Hopefully this has been enough to get you started with some intermediate CSS and jQuery animation. Nothing too difficult here, just make sure to keep the docs handy for stuff you've never done before and keep an eye on the details.