How To Create Simple, Stylish and Swappable Image Captions

Published December 3, 2009 by CSS Newbies.

Image Captions

While they say a picture is worth a thousand words, not every image is self-explanatory. Sometimes a few words of description or context can make the difference between a confusing image and a clarifying one. That’s why image captions are often a good idea.

I’ve always liked the idea of giving images captions, but I rarely do. And why not? It seems to me that most image caption solutions out there:

  • Require a lot of excess HTML,
  • Make it difficult to redesign (because of the rigidity, usually),
  • Don’t make the relationship between image and caption clear, or
  • All of the above.

Today, I propose a solution that addresses many — if not all — of these concerns.

You can view the demo here, or read on for the why- and how-to.

How Not To Do Captions

An example of an old-school way to deal with image captions might look like this:

<table class="image-caption">
		<td><img src="myimage.jpg" alt="" /></td>
		<td>This is a caption for the image above.</td>

One thing this solution does well is make the relationship between the image and the caption clear: items in the same column of a table are (or at least should be) related in some way.

However, it requires a ton of markup for something as simple as a line or two of related text. And let’s pretend you have a blog with 300 posts that contain images with this style of caption. What happens when, a year down the line, you decide the caption would look better above the image in the new design? There goes your weekend.

Here’s another example that’s much cleaner, but has problems of its own:

<img src="myimage.jpg" alt="" />
<p>This image is swiped from Flickr.</p>

This solution is nice and clean, but it makes a lot of assumptions. Your readers should assume, for example, that paragraphs following your images describe that image. Which may be true most of the time, but probably isn’t true all of the time.

And while you could give that paragraph a certain class or ID to style it more like a caption, code-wise they’re still semantically unrelated.

So what’s the solution?

The Goal

My goal was to create something that would:

  • Retain some resemblance to good, semantic code,
  • Keep my HTML as clean as possible, and
  • Be easy to style and restyle at will.

But in order to do any fancy CSS work, I’d need some hooks in my HTML. And adding more HTML makes it less clean, harder to revise in the future, and reduces my odds at keeping things semantically meaningful.

To solve this thorny catch-22, I turned (yet again) to our good friend jQuery.

The Solution

In order to keep my HTML clean, I decided to limit myself to a traditional image tag. Nothing simpler, right?

<img src="my_image.jpg" alt="" class="hascaption" />

I also allowed myself the luxury of a special “hascaption’ class, but if all the images were going to have captions, this wouldn’t even be necessary. But then where does the caption come from? Well, why not the image tag itself?

Image tags have a “title” attribute which is supposed to contain extra information about the image. Extra, related information? That sounds like a caption to me! So let’s use it as such:

<img src="my_image.jpg" alt="" class="hascaption" title="This is going to be my caption!" />

Now our caption is contained within our image tag, which is about as semantically meaningful as you can get. And we’re off to a good start: all modern browsers display image titles as a “tooltip,” which is sort of a poor man’s caption. But we want ours to be fancier, and styleable (you can’t style tooltips).

Here’s some jQuery that provides us a nice structure:

$(document).ready(function() {
	$("img.hascaption").each(function() {
		$(this).wrap('<div class="figure"></div>')
		.after('<p class="caption">'+$(this).attr("title")+'</p>')

This code loops through our document and finds all the images with a class of “hascaption” and wraps them in a div called “figure”. Then it adds a paragraph after the image (inside the div) with a class of “caption”. It then fills the paragraph with the contents of the image’s title tag. Then it removes the title attribute to prevent the tooltip from competing with our caption (a bit extreme, but it works).

And the last line of code is a little trick that makes the “figure” div the exact same width as our image, so it only takes up as much room as necessary. If you chose to float your div or specify a predetermined width in your CSS, you wouldn’t need this line.

So now we have a rendered structure that looks more like this:

<div class="figure">
	<img src="my_image.jpg" alt="" class="hascaption" />
	<p class="caption">This is going to be my caption!</p>

This gives us plenty of hooks for some CSS/JS fun, but it didn’t require us to write any extra code by hand, and keeps our HTML looking clean. Anyone viewing the source will just see the original image tag, not the code above.

And best of all, if a year from now we want the caption to go above the image, it’ll only take changing one line of jQuery, versus changing hundreds of articles manually.

Adding Some Flair

I wanted these captions to have a bit of flair: the caption should appear directly on top of the image when the user mouses over (close visual proximity providing relevance), but disappear when the mouse goes away (so as not to clutter or obscure the image). For that, I needed a couple more lines of jQuery:

$(document).ready(function() {
	$("img.hascaption").each(function() {
		$(this).wrap('<div class="figure"></div>')
		.after('<p class="caption">'+$(this).attr("title")+'</p>')

And some quick CSS:

.figure {
	position: relative; }
.figure p.caption {
	display: none;
	position: absolute;
	bottom: 0px;
	left: 0px;
	margin: 0;
	width: 96%;
	padding: 5px 3%;
	background-color: #555;
	color: #fff;
	font-weight: bold; }

Note: The CSS above has been modified slightly from the original to account for an IE7 bug.

The jQuery is making use of the “mouseenter” and “mouseleave” functions built into jQuery. Whenever the mouse enters or leaves the space occupied by the “figure” div, the “slideToggle” function fires.

I think slideToggle is pretty cool: it “slides” the content in if it’s not currently there, and slides the content out if it is already present: it toggles between the two states.

The CSS is doing two primary things: setting a relative position on the figure div, and an absolute position on the caption paragraph. Then we just position the caption at the bottom-left of the figure using the power of absolute-inside-relative positioning (one of my favorite positioning tricks!). I gave the caption a nice dark background and made the text white and bold to provide plenty of contrast, but yours could be styled however you like. I’m also using a percentage width, plus a percentage padding on the left and right sides, to ensure the caption always stretches the entire width of the image.

View the whole thing in action.

Admittedly, this is really just a proof-of-concept and could probably stand to be cleaned up a bit. If there’s any interest, I might post a follow-up article that makes this a little prettier.

76 Responses

  1. Pingback: 40 Useful CSS Tutorials, Techniques And Resources | Free and Useful Online Resources for Designers and Developers

  2. Pingback: 40 Useful layout by CSS Tutorials including Techniques And Resources « 68Design – ( Creative Design Transmitter

  3. Pingback: 45 Powerful CSS/JavaScript-Techniques « Downgraf – Design weblog for designers

  4. Pingback: How To Create Simple, Stylish, and Swappable Image Captions |

  5. Pingback: 15 Best CSS Tutorials to Enhance Web Design Skills | Sky Tech Geek

  6. Pingback: Five Useful Design Techniques and Coding Solutions For Web Designers - Goodfav Howto

  7. Pingback: 45 Powerful CSS/JavaScript-Techniques - Goodfav Howto

  8. Pingback: 45 Powerful CSS/JavaScript-Techniques - Internet Business

  9. you (reply)

    When I initially left a comment I seem to have clicked the -Notify me when new comments are added- checkbox and from now on every time a comment is added I get 4 emails with
    the exact same comment. There has to be a way you can remove me from that service?


  10. Ecar Insurance Customer services contact phone number (reply)

    Wonderful website you have here but I wwas curious about if you knew
    of any forums tgat cover the same topics talked about here?
    I’d really like to bee a part of community where I can get feedback from other knowledgeable individuals that share the same interest.

    If you have any suggestions, please let me know. Thasnk you!

    Feel free to surf to myy weblog :: Ecar Insurance Customer services contact phone number

  11. homes for sale des moines (reply)

    This paragraph will assist the internet viewers for building up new
    webpage or even a blog from start to end.

    For an awesome response please check out this page :
    : homes for sale des moines

  12. (reply)

    Thank you a bunch for sharing this with all folks you actually recognise what you’re talking approximately!

    Bookmarked. Please additionally discuss with my
    site =). We can have a link trade agreement among us

    For a better informative review please view this site – Testibate reviews (

  13. free movies online (reply)

    Earlier many people were not aware of the concept of watching free movies online.

    Include some digital swag in your entertainment promotions campaign if you are sponsoring a special event.
    Some videos will have subtitles or will be dubbed in another language; if these are
    present the viewer will not be able to turn it off so they need
    to know that they are there and be comfortable with them prior to download.

  14. Fleta (reply)

    Good way of explaining, aand pleasant piece of writing too
    take facts on tthe topic oof my presentation subjnect matter,
    which i am going to convey in institution of higher education.

    My webpage: social media Bedford (Fleta)

  15. Pattaya Holiday (reply)

    Have you ever considered about adding a little bit more than just your
    articles? I mean, what you say is valuable and everything.
    But just imagine if you added some great images or videos to give your posts
    more, “pop”! Your content is excellent but with images and
    clips, this blog could definitely be one of the most beneficial in its niche.
    Fantastic blog!

    Look at my site :: Pattaya Holiday

  16. Nell (reply)

    I leav a respone whenever I especially enjoy a post onn a site or if I have something too contribute to thee conversation.
    Usually iit is a result of the fire displayed in thhe post I read.
    And after thios article How To Creat Simple, Stylish annd Swappable Iage Captions.

    I was moved enough to post a comment :-P I actually doo have a couplle off quesstions for you
    if it’s allright. Could iit bbe simnply
    mme or ddo a feww of hese responses come across as if they are coming frtom brain dea folks?
    :-P And, iff you aree writiing aat addtional socil sites, I would like to keep uup witrh
    you. Couyld yyou list all oof yoour communmity pages like your linkedin profile, Faceook pqge oor
    twitter feed?

    Feeel frre tto suff too my wweb page: gsme (Nell)

  17. Bursa Emlakçı (reply)

    Bursa Emlakçı ile her birey istediği bir günde ve istediği bir zamanda internet sitesini ziyaret ederek birbirinden güzel, lüks ve kullanışlı olan daireler hakkında bilgi sahibi olabileceği gibi, Türkiye’nin her yerinden iletişime geçerek istediği daireyi satın dahi alabilecektir.

  18. payday loan (reply)

    Fantastic blog! Do you have any suggestions for aspiring writers? I’m planning to start my own website soon but I’m a little lost on everything. Would you propose starting with a free platform like WordPress or go for a paid option? There are so many choices out there that I’m completely overwhelmed .. Any suggestions? Thank you!

  19. happy wheels (reply)

    Thank you for your blog post.I admire the valuable information you offer in your articles. Thanks for posting it, again! Really looking forward to read more. Awesome.

Leave a Reply

Your email address will not be published. Required fields are marked *