Friday, November 30, 2012

Javascript Explained: Transparent Image Captioning with jQuery and CSS

In this post I'll step through and explain the Javascript code I recently wrote for easily adding transparent captions to images. (Later I may turn this into a jQuery plugin, but for the moment it's structured as a short script you can cut and paste for use on your page.) Follow the directions from the previous post to use it. In this post I'm just going to talk about how the Javascript works. First, here's the entire script for reference:

/*
* CssBakery.com Captions v0.1
*
* Copyright (c) 2009 Cssbakery.com
* http://www.cssbakery.com
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*/
$(document).ready(function(){
var cssbakery_colors = { medblue: '#4084e1', tealblue: '#3fbff4', marineblue: '#3f3ff4',
twig: '#855434',  eggplant: '#5d2f4d', violet: '#c261e2', darkpink: '#e261a4' };

$("img.cssbakery_caption").each(function() {
var theid = $(this).attr('id');
var saveclass = $(this).attr('class').replace('cssbakery_caption','');
var theclass = ' '+saveclass+' ';
theclass = theclass.replace(' cc ','');
theclass = theclass.replace(/ /g,''); // note use of global replace
if (theclass === '') theclass='black';
if (!(typeof cssbakery_colors[theclass]=="undefined")) {
theclass = cssbakery_colors[theclass];
}
var src = $(this).attr('src');
var caption = $(this).attr('alt');
var idx = caption.indexOf(':');
if (idx >= 0) {
caption = "<strong>"+caption.substr(0,idx+1)+"</strong>"+
caption.substr(idx+1);
}
var w = $(this).width();
var pb = $(this).css('padding-bottom');
var pl = $(this).css('padding-left');
if (theid !== '') {
theid = "id='"+theid+"' ";
}
var markup = "<div "+theid+" class='cssbakery_image_wrapper "+saveclass+"'
style='width:"+w+"px'>\n<img src='"+src+"' />\n<div class='cssbakery_transbox'
style='background-color: "+theclass+"; left:"+pl+"; bottom:"+pb+";'></div><p
style='left:"+pl+"; bottom:"+pb+";'>"+caption+"</p></div>";
$(this).before(markup).remove();
$('.cssbakery_image_wrapper').children('p').each( function() {
var h = $(this).height();
$(this).siblings('div').height(h+20);
});

});
});


Now we'll break it down line by line:

$(document).ready(function(){


The first line of the script is the typical jQuery pattern for executing some code only after the Browser has completely loaded the document object model (DOM) for the page. If you aren't familiar with this, the idea is that you don't want your Javascript to run too early, before all the HTML and CSS has been loaded by the browser. If it does run too early, the HTML elements you are trying to operate on may not yet be available. jQuery solves this by providing the ready() method which registers a callback function to be executed when the DOM is fully loaded.

var cssbakery_colors = { medblue: '#4084e1', tealblue: '#3fbff4', marineblue: '#3f3ff4', twig: '#855434',
eggplant: '#5d2f4d', violet: '#c261e2', darkpink: '#e261a4' };



The second and third lines of the script initialize an array of objects that represent different color codes. You can use the names of these objects to select a color for the transparent background underneath the caption text. To make this easy to use, you can specify the color name as a class of the img tag. For example, you might write <img class="cssbakery_caption violet" src="images/whatever.jpg">, and you'll end up with hex code #c261e2 as the color of the background div. You can also use any standard CSS color name such as "blue", or even directly specify a hex code, like this: class="cssbakery_caption #afafaf".

$("img.cssbakery_caption").each(function() {
var theid = $(this).attr('id');
var saveclass = $(this).attr('class').replace('cssbakery_caption','');



Now we use jQuery to apply a function to each img tag with class of cssbakery_caption. The first part of the function is all about pulling out information from the id and class attributes of the img tag. First, the tag's id attribute is saved in variable theid, then the class string is copied into variable saveclass, but with any occurrence of cssbakery_caption removed. The idea is that we want a string that contains any other class names (other than cssbakery_caption) stored into saveclass. For example if the img tag's class attribute is "cssbakery_caption cc blue", then we end up with "cc blue" in the saveclass variable.


var theclass = ' '+saveclass+' ';
theclass = theclass.replace(' cc ','');
theclass = theclass.replace(/ /g,''); // note use of global replace
if (theclass === '') theclass='black';
if (!(typeof cssbakery_colors[theclass]=="undefined")) {
theclass = cssbakery_colors[theclass];
}


The next several lines are aimed at getting the color code stored into the theclass variable. First we remove the cc part of the class string (if present), then remove all spaces from the string. At this point if the theclass variable is empty, we default the color to black. Next we check to see if the value in theclass matches any of the objects we have stored in the cssbakery_colors array that we setup before. If it does match, then we overwrite theclass with the hex code value from the array.

var src = $(this).attr('src');
var caption = $(this).attr('alt');
var idx = caption.indexOf(':');
if (idx >= 0) {
caption = "<strong>"+caption.substr(0,idx+1)+"</strong>"+caption.substr(idx+1);
}
var w = $(this).width();
var pb = $(this).css('padding-bottom');
var pl = $(this).css('padding-left');
if (theid !== '') {
theid = "id='"+theid+"' ";
}


Now we pull some more attributes from the image: the src attribute is stored in variable src, the alt attribute is stored in variable caption. We then look for the colon character in the caption string, and if present we add a <strong> markup around the portion before the colon.

Next we pull out the width of the image into variable w, and grab padding-bottom and padding-left CSS attributes, storing them into variable pb and pl respectively. We then check to see if theid variable is not empty. If it's not we reformat it slightly to form an id attribute string such as: id='myId'.

At this point we have all the variables that we need to construct the new markup for the captioned image. So the next step is to build that markup and and put it into the document:

var markup = "<div "+theid+" class='cssbakery_image_wrapper "+saveclass+"' style='width:"+w+"px'>
<img src='"+src+"' />
<div class='cssbakery_transbox' style='background-color: "+theclass+";
left:"+pl+"; bottom:"+pb+";'></div><p style='left:"+pl+";
bottom:"+pb+";'>"+caption+"</p></div>";
$(this).before(markup).remove();



Using all the variables we've previously set, we construct a string of markup and assign it to the variable markup. We then need to replace the original image tag with the new markup. This is achieved by the line $(this).before(markup).remove().

$('.cssbakery_image_wrapper').children('p').each( function() {
var h = $(this).height();
$(this).siblings('div').height(h+20);
});




Finally, we want to set the height of the semi-transparent background div based on the height of the caption text paragraph. This code finds the paragraph, gets its height and then sets it's sibling div to that same height+20 (the extra 20 allows for some padding above and below the text). jQuery make it easy to find things like the caption paragraph. In this case, we first select all elements of class cssbackery_image_wrapper using $('.cssbakery_image_wrapper). We then find any children p elements by tacking on .children('p'). The each() method then loops over everything we found and calls the supplied function on those elements. Inside the function we grab the height of the element (so that would be the height of the paragraph). We then use that value in setting the height of the sibling div, but making use of the jQuery statement $(this).siblings() function which finds any sibling elements to the current element.

And that's the end of the script. Remember that you also need the CSS file that goes along with this for it to work (see the previous post on this subject for further instructions).

Post a Comment