How to Auto Caption Images Using MooTools

Jan 22 2009 by Jacob Gube | 19 Comments

MooTools is a powerful open-source JavaScript framework that can greatly enhance the way you build your web interfaces. Using MooTools will allow you to write succinct and elegant JavaScript within an intuitive framework. In this tutorial, we’re going to auto caption images on-the-fly using a bit of MooTools magic.

How to Auto Caption Images Using MooTools tutorial - leading image.

Together, we’re going to write a simple script that will:

  • Select all images with the class of captioned
  • Take its title attribute or alt attribute
  • Display it right below the image

You can see a live demo here. View the source code to explore for yourself how it works. You can download the source file package here.

HTML and CSS

The first thing we want to do is mark up our images. The HTML markup is simple:

<img class="captioned" src="red_rocks.jpg"
title="Photo of Red Rock Mountain." width="300" height="201" />

A few important points here:

  • If our image has both a title and an alt attribute, we’ll take the value of the title attribute over the alt attribute.
  • If our <img> doesn’t have a title or alt attribute, we’re going to alert the user that it needs one.
  • Declaring the value of the width property is required or our script will break.

CSS

Nothing fancy here, just a few basic style rules to make our caption pretty:

img {
padding:5px 0;
}
div.figure {
width:310px;
text-align:center;
border:1px solid #ccc;
background-color:#f2f2f2;
margin:5px;
float:left;
}
p.caption { padding:0; margin:0 0 5px 0; font:italic 12px Georgia, "Times New Roman", Times, serif; color:#333333; }

div.figure will be the container of our image and caption. p.caption will be our actual caption. We’re going to use MooTools to insert these two things in our Document Object Model (DOM).

The code logic: manipulating the DOM

Before we get into any code-authoring, let’s step through the logic of what we’re trying to accomplish.

First, we’re going to select all of the images in our structure that has a class of captioned. Then for each image that matches our criteria, we’re going to:

  1. Determine whether it has an alt or title attribute.
  2. If it has neither an alt or title attribute, we don’t do anything to the image and warn the user that the image needs at least one for our script to work.
  3. If we find both a alt and title attribute, then we will use the title attribute value for our caption.

Once we get the text for our caption, we will create a new DOM element, a paragraph element (<p>) called caption which will contain our caption text.

Once caption is created, we’ll create another DOM element (a div) to contain our image and caption. We will call this new DOM element figure.

Through DOM manipulation, we’re going to end up with an HTML structure like this:

<div class="figure">
  <img class="captioned" src="red_rocks.jpg"
    title="Photo of Red Rock Mountain." width="300" height="201" />
  <p class="caption">Photo of Red Rock Mountain.</p>
</div>

Of course, this will happen behind the scenes, but that’s basically what our HTML will look like after our MooTools script runs.

Initializing MooTools

Now that we have our HTML and CSS set up and we have a rough overview of what we’re going to do – we’re ready to start scripting!

First, we’ll initialize MooTools by wrapping all of our code inside the ‘domready’ event listener.

window.addEvent('domready', function() {
     ... Our code goes in here ...
});

What this method does is it tells MooTools that as soon as the DOM is ready, we’ll run our code. This way, we’re not waiting for the page to load before we execute our JavaScript.

Selecting and iterating through our images

We’ll select our images to be captioned by using the $$() function which is often called the "dollars" function. $$() is very intuitive and works almost exactly like CSS selectors. To select all images that have the class captioned, we write:

$$('img.captioned')

Tip: we can also just use $$('.captioned') assuming that you only use this class for images. To be safe – I’ve chosen to increase the specificity of our selector.

Next,we will iterate through each image that matches our criteria using the .each() method.

window.addEvent('domready', function() {
  $$('img.captioned').each(function(el) {
    ...Everything we write will go in here...
  });
});

Getting the text for our caption

We’re going to use the getProperty() method to pull out the title attribute value.

We’ll put this value inside a string variable called captionText.

To get the value, we write:

var captionText = el.getProperty('title')

In our HTML markup, the value of captionText should now be:

"Photo of Red Rock Mountain."

But we have to consider the situation that the image might have both an alt and a title property declared.

So, we’re going to use a ternary control structure to check if there’s a title and if we find it, then we’ll use that for our caption; if not, we’ll try to see if there’s an alt property and use that instead. If they both don’t exist, then captionText gets assigned a null value.

All together, our ternary operator is:

var captionText = ( el.getProperty('title')!=null )
                  ? el.getProperty('title') : el.getProperty('alt');

Note: ternary operators are just shorthand for if statements. You can easily convert the above code into nested if statements and it will work just fine. Have a go at it, convert this bit to an if structure. If you need help ask in the comments.

In plain english, this all translates to "If the current object’s title attribute value is not null, then captionText will take on that value; otherwise, captionText will take on the value of the alt attribute."

Explore: Here’s a good spot to explore by yourself. Try to modify our ternary operator so that the alt attribute takes precedence over the title attribute.

Making sure we have a caption

Before we do anything else, we have to make sure that our object will have a caption.

So we make sure the captionText doesn’t contain a null value. We’ll do this with a basic if control structure

if ( captionText!=null ) {
  //code to execute if we have a caption
} else {
  //code to execute if we encounter an error (Error-handling code goes here).
}

Error-handling

Let’s jump ahead to our error-handling and reporting. The method we’ll use is very rudimentary: if we find that the current object doesn’t have a caption (i.e. captionText != null evaluates to false), then we’re going to tell the user that this image needs either a title or an alt attribute.

alert('The image: "'+el.getProperty('src')+'" needs a title or alt value.');

To help identify the image, we take the src value, which outputs the location of our image.

IMPORTANT: This type of error-handling and reporting isn’t good practice; we used this in the example to make things easier on ourselves. But in production, you would want to replace this code with a better way to handle errors.

Messing around with the DOM

Now, we’re going to get into the thick of things – DOM manipulation. We’re going to insert two new elements in our Document Object Model; they are figure and caption.

The first element we’re going to make is the figure element. This element will be a div element that will contain our image and our other new element (caption).

Declaring our figure prototype

In order to create a new element in MooTools, we use the following structure:

var elementName = new Element('type of element', {its  properties})

We want figure to be a div with a class of figure and 10 pixels wider than our image (so that we have a 5-pixel margin on the left and the right).

Our prototype for figure then becomes:

var figure = new Element('div', {
  'class' : 'figure',
  'styles' : {
    'width' : el.get('width').toInt() + 10
  }
});

To get the width, we use the get() method, and then we convert the value to an integer with the .toInt() method. We then add 10 pixels (+ 10) to the width so that we get the 5-pixel margin on either sides of the image.

Are you with me so far? Keep up, we're almost there!

Declaring our caption prototype

So we just built our first new element, figure. Now we have to build our second element, caption. caption will be a paragraph element (<p>) and will contain the caption's text we took from our image (remember: it's inside captionText).

Let's get right to it, here's our caption element prototype:

var caption = new Element('p', {
  'class' : 'caption',
  'html' : captionText
});

Review: Go to the MooTools Doc entry on the Element method: constructor to learn more about creating new elements.

In the above construction, we created a new element called caption. We told MooTools that we want it to be a paragraph (<p>) element that has a class of caption and the HTML that should be inside it is equal to captionText.

Manipulating the DOM

Wrapping our object inside figure

The first DOM manipulation that we'll pull off is wrapping our object inside our figureelement. We'll use the wraps() method which (as the name implies) wraps the target object of the method with the argument you pass into it.

with this in mind, all we have to do is write:

figure.wraps(el);

Inserting caption into the DOM

After that, we want to insert caption after our image. We'll use the inject() method for this.

caption.inject(el,'after');

Summing up

Let's bring everything together in one big chunk. Here's the entire MooTools script.

window.addEvent('domready', function() {
  $$('img.captioned').each(function(el) {
    var captionText = ( el.getProperty('title')!=null )
                    ? el.getProperty('title') : el.getProperty('alt');
    if ( captionText!=null ) {
      var figure = new Element('div', {
        'class' : 'figure',
        'styles' : {
          'width' : el.get('width').toInt() + 10
        }
      });
      var caption = new Element('p', {
        'class' : 'caption',
        'html' : captionText
      });
      figure.wraps(el);
      caption.inject(el,'after');
    } else {
      alert('The image: "'+el.getProperty('src')+'" needs a title or alt value.');
    }
  });
});

What did we just do?

In this tutorial, we used the MooTools JavaScript framework to automatically display captions below images that have the class of captioned. We did this by selecting all the elements that matched our criteria, iterated through the matched set, and for each object in the matched set, we:

  1. Determined our caption's text by getting the value of the object's title attribute, and if it didn't have one, then we defaulted to its alt attribute.
  2. Constructed the figure element that wraps around our image and caption.
  3. Constructed the caption element, a paragraph that has our image's caption.
  4. Wrapped our image inside the figure element using the wraps() method.
  5. Injected caption right after our image using the inject() method.

We also made a very basic error-handling system that warns the user via an alert message that a title or alt value is required.

Review questions for beginners

This script is far from perfect. I chose to leave it that way to encourage you to explore MooTools and analyze the code we wrote together.

Here are some questions to try and answer yourself. I will post the answer in the comments section at a later date.

  1. Our script will break if the image doesn't have a width attribute; what are some error-handling operations we can perform to prevent our script from breaking?
  2. What are other MooTools methods that could work in setting the width of the figure element?
  3. Is the .toInt() method a MooTools method or a native JavaScript method?

Download

You can download the source file package here. Please read README.txt for instructions on how to use this package.

19 Comments

Timothy

January 23rd, 2009

Awesome! Very good work. Much appreciated

Matt

January 23rd, 2009

Is this a preview of your upcoming book?

Scott Petrovic

January 23rd, 2009

thanks for this MooTools/DOM tutorial. This will be useful to standardize how I can make captions for my websites.

Jacob Gube

January 23rd, 2009

@Matt: I won’t be commenting cannot comment on that at this moment. Let’s just say I’m very interested in what people have to say with regards to this style of tutorials. :)

Sean McArthur

January 23rd, 2009

Very well written. Easy to follow. You could consider turning this block of code into a class, as that is the Mootools pattern, give it portability.

One other thing: your use of the ternary operator, can be shortened in Javascript, cause Javascript is cool like that.

var captionText = el.getProperty(‘title’) || el.getProperty(‘alt’);

Basically means the first value unless it is falsy (includes null), then get the second value. If that one is null too, then you just get null.

Jacob Gube

January 23rd, 2009

@Sean McArthur: Woops, good point about falsy. I think I was trying too hard to try and incorporate a discussion on ternary operators, though I could have easily switched it up to a discussion on falsy/truthy. I’ll revise this with your operation at a later date.

Creating an auto-caption class: I was kind of saving this topic on a subsequent tutorial; but you’re completely right, MooTools is very classy :) and I should make a note somewhere that it’s good practice to construct a class for this to keep it as OOP as we can and so we can set options and extend the auto-captioning script (i.e. float: right and float: none, be able to change the target class, etc.).

Thank you for the feedback, Sean – this is exactly the type of feedback I’m looking for.

Tonamel

January 23rd, 2009

As a bit of a JS noob, I don’t get where “el” came from, and how the code understands it to be “this”. Does the dollars function automatically set el, or is something else going on that I’m missing?

Jacob Gube

January 23rd, 2009

@Tonamel:
el does work exactly like this.

el did not come from the dollars function, it came from the .each() method. The structure of .each is:

.each(function(current element, index){
//code to run on each object here
});

So if we had 3 images, and we were on the 2nd iteration (of 3 iterations) – el would be referring to the current object which is the 2nd image, and index would be 1 (because we count starting from 0).

I could have left the function argument blank, and then use this instead. I was hoping el would make it easier to understand. By the way, a lot of MooTool’ers use el as abbreviation for “element” in case you were wondering.

From our script above:

$$('img.captioned').each(function(el) {
//perform operation on current item, el.
}

Tonamel – please tell me if this helped or if it muddied it up further.

To the rest, ask questions please – tell me what wasn’t clear to you in the tutorial.

Daniel

January 23rd, 2009

Well written (and useful) post, Jacob. Thanks for sharing! :)

Chris Wallace

January 23rd, 2009

If anyone is interested in seeing this inside a class, I’ve posted the code here. Great tutorial, Jacob, you inspired me to take it one step further!

Jacob Gube

January 24th, 2009

@Chris Wallace: Chris, you geek – this is what you did on a Friday night? :) Thanks for this, and you tweaked the script to look cleaner too ( if ( captionText ).

Suggestion (I should really post this on your blog, but another option can be the margins. I didn’t like how I set the div margins to be static at 5 pixels on each side.

I wasn’t planning on releasing this as a plugin, but if enough people find use for this, Chris and I can work together to release a more robust auto-captioning plugin.

Chris Wallace

January 24th, 2009

Yes, I AM a geek and it was only PART of what I did on a Friday night. My wife forced me to watch Apollo 13, so while she was crying about the astronaut’s wives, I did more interesting things :)

Tonamel

January 25th, 2009

Thanks, that totally cleared it up :)

It isn’t necessarily that you phrased anything poorly, it’s just that in my noviceness (in both js and mootools) I’d never seen a function that had “current element” as an argument.

Just another hurdle to get over whilst learning the conventions of languages and frameworks. I had similar issues figuring out how event handling works in actionscript 3.

Eddie

January 26th, 2009

Thanks for this great tutorial.

This might be a very stupid question: what if I need the img class for positioning the image?

Jacob Gube

January 26th, 2009

@Tonamel: AS2 and AS3 is similar to JS, especially with this. Thanks for the feedback, I appreciate it. I’m glad I cleared that up.

@Eddie: Good question actually. If you need to add another class, you can double-declare the classes.

For example, say your class for floating left is alignLeft. Your markup would then be

  <img class="captioned alignLeft" />

Just separate the classes with spaces, this way you can assign multiple classes to a particular page object.

Let me know if this works or not. I’m assuming your image would align to the left of the container div (<div class="figure">) though.

Dean Marshall

February 7th, 2009

What can be done about images that are already inside paragraphs. Auto wrapping these images inside divs that also contain paragraphs seems to add up to a heap of invalid html – p > div > p

Perhaps the DIV and P could/should be substituted with spans or some other inline element. If these were given classes and set to display:block through css then the generated code would stay valid.

Just my tuppence.

Jacob Gube

February 8th, 2009

@Dean Marshall: I debated for a long time how to write this to keep it as semantic as possible; it still works even if the image is inside a p tag, but it’s not good markup because in the situation you mentioned, the caption-text paragraph will sit inside another p tag. I know because I also put images inside paragraphs when I write HTML.

One way to solve it, as you’ve noted, is to replace p with a span tag.

For people interested in substituting the markup used to generate the auto-captioning, it’s easy enough – just tweak the figure and caption objects (change ‘div’ and ‘p’ to whatever element you’d like to use):

var figure = new Element('div', {
  'class' : 'figure',
  'styles' : {
    'width' : el.get('width').toInt() + 10
  }
});

var caption = new Element('p', {
  'class' : 'caption',
  'html' : captionText
});

Note that if you do that, you will also need to tweak the CSS style rules to reflect the changes you’ve made. Hope that helps.

Daniel

March 2nd, 2009

Thanks! This was very helpful.

Daniel North

June 16th, 2010

Hi, this is Daniel North.This post is really very appreciable.
your post is very advantageous for me and very good.If you need to add another class, you can double-declare the classes.If these were given classes and set to display:block through css then the generated code would stay valid.I’ve posted the code here. Great tutorial, Jacob, you inspired me to take it one step further!
==================================
Daniel North

Leave a Comment

Subscribe to the comments on this article.