All modifications are based on the original Shadowbox code by Michael Jackson which can be downloaded from the Shadowbox website.
In the past I'd been using Lightbox and later Litebox for my image viewing needs. They're both great but I'd never been too keen on the default navigation style, I always wanted thumbnail images in the lightbox itself which would allow me to switch the main image. I was also starting to get into JQuery in a big way, so when I went looking for a replacement I consulted the Lightbox clone comparison matrix and found Shadowbox (which was at version 2 at that point in time). While it doesn't offer the option of thumbnail navigation as standard I'd noticed this post on the support forum which seemed to suggest that it was possible (thanks to Micke04 and Wizzud for lighting the way). After a bit of hacking about with the code I got the thumbnail functionality working. I was aware that the code was a bit messy and I'd always intended to revisit it at some point and clean it up, but never quite got round to it. With the release of Shadowbox 3 with support for IE8 I thought it would be a good time to go back and take another look, only this time I'd try to keep the code clean and document my modifications as I went along, for my own benefit and the benefit of anyone else who's interested, hence the reason these pages are here.
This is my first attempt at putting thumbnails into the Shadowbox. At the moment I'm just trying to add the minimum code required to add the thumbnail feature without sacrificing any of the existing functionality. This is probably going to highlight a few problems but will give me a standard code base for further exploration.
Get the source code. This only contains the modified versions of shadowbox.js and shadowbox.css so you'll still need a full working copy of Shadowbox 3.
If you have any questions about the modified code please add them to this post on the Shadowbox support forum.
Original Shadowbox code appears first with the blue background, modified code follows with a yellow background and changes highlighted in red.
All changes shown here are based on Shadowbox 3.0rc1
This is basically just going to be a modified version of the skip style counter with thumbnail images instead of numbers. Since I want to maintain all the existing functionality of Shadowbox I'm going to add a new counterType called 'thumbskip' and set it as my default. I'm also dropping the counterLimit to 7 since that's what fits comfortably with the thumbnail size I'm using. Here's how the code looks to start with...
counterLimit: 10, // limit to the number of counter links that
// are displayed in a "skip" style counter
counterType: 'default', // counter type. May be either "default" or
// "skip". Skip counter displays a link for
// each item in gallery
...and this is what I'm going to change it to:
counterLimit: 7, // limit to the number of counter links that
// are displayed in a "skip" style counter
counterType: 'thumbskip', // counter type. May be either "default",
// "skip" or "thumbskip". Skip counter displays
// a link for each item in gallery. Thumbskip
// adds a thumbnail.
If you don't want to set these as your defaults they can just as easily be passed in as parameters for specific shadowbox instances.
I'm going to need to extract the thumbnails src attribute to use when building the counter so I'll start with the buildCacheObj function. Here's what it looks like as standard:
buildCacheObj: function(link, opts){
obj = {
el: link,
title: link.getAttribute('title'),
options: apply({}, opts || {}),
content: link.href // don't use getAttribute here
};
Changes to the code are highlighted in red. I'm assuming the thumbnail is the first child of the link and adding it's src attribute to the obj array.
buildCacheObj: function(link, opts){
obj = {
el: link,
title: link.getAttribute('title'),
options: apply({}, opts || {}),
content: link.href, // don't use getAttribute here
source: link.firstChild.src // add the thumbnail src
};
Next I'll look at how the counter is built.
// build the counter
var counter = '';
if(S.options.displayCounter && S.gallery.length > 1){
var len = S.gallery.length;
if(S.options.counterType == 'skip'){
// limit the counter?
var i = 0,
end = len,
limit = parseInt(S.options.counterLimit) || 0;
if(limit < len && limit > 2){ // support large galleries
var h = Math.floor(limit / 2);
i = S.current - h;
if(i < 0) i += len;
end = S.current + (limit - h);
if(end > len) end -= len;
}
while(i != end){
if(i == len) i = 0;
counter += '<a onclick="Shadowbox.change(' + i + ');"'
if(i == S.current) counter += ' class="sb-counter-current"';
counter += '>' + (i++) + '</a>';
}
} else
var counter = (S.current + 1) + ' ' + S.lang.of + ' ' + len;
}
This follows one of two paths depending on whether it's a skip style counter or the default style. The skip style counter also has some useful logic built in that creates a wraparound counter if the number of images exceeds the limit specified by the counterLimit variable. By branching the thumbnail counter code from the existing skip code I can retain that functionality.
// build the counter
var counter = '';
if(S.options.displayCounter && S.gallery.length > 1){
var len = S.gallery.length;
if(S.options.counterType == 'skip' || S.options.counterType == 'thumbskip'){
// limit the counter?
var i = 0,
end = len,
limit = parseInt(S.options.counterLimit) || 0;
if(limit < len && limit > 2){ // support large galleries
var h = Math.floor(limit / 2);
i = S.current - h;
if(i < 0) i += len;
end = S.current + (limit - h);
if(end > len) end -= len;
}
if(S.options.counterType == 'thumbskip') { // thumbskip counter
while(i != end){
if(i == len) i = 0;
counter += '<a onclick="Shadowbox.change(' + i + ');"'
if(i == S.current) counter += ' class="sb-counter-current"';
counter += '>' + '<img src="' + S.gallery[(i++)].source + '" />' + '</a>';
}
} else // skip counter
while(i != end){
if(i == len) i = 0;
counter += '<a onclick="Shadowbox.change(' + i + ');"'
if(i == S.current) counter += ' class="sb-counter-current"';
counter += '>' + (i++) + '</a>';
}
} else
var counter = (S.current + 1) + ' ' + S.lang.of + ' ' + len;
}
The standard skip counter is built by outputting a series of links with onclick handlers that call the change function. The modified version for the thumbskip counter just replaces the text in the links with the thumbnail images that were added to the obj array previously.
And that's all the changes to the javascript done.
As with the javascript I'm keeping the changes to the CSS to the minimum required to get the job done. The first step is to increase the height of the sb-info and sb-info-inner divs to accomodate the thumbnails. The exact height required will depend on the size of the thumbnail images and the amount of vertical space around them but in this case a value of 56px gives the desired result.
#sb-info, #sb-info-inner {
height: 56px;
}
Next I'll drop the width of the sb-nav div since I'm going to need the extra space to accomodate the thumbnail counter.
#sb-nav {
float: right;
height: 16px;
padding: 2px 0;
width: 19%;
}
And finally I'll increase the width of the sb-counter div and add borders and :hover classes to the thumbnails to improve things visually (Note: The :hover pseudo classes don't work in IE6 or IE7 for some reason, see this post on the Shadowbox support forum for further details).
#sb-counter {
float: left;
padding: 4px 0 0 0;
width: 80%;
}
#sb-counter a {
padding: 0 4px 0 0;
text-decoration: none;
cursor: pointer;
color: #fff;
}
#sb-counter a.sb-counter-current {
text-decoration: underline;
}
#sb-counter a img {
border: solid 1px #000;
}
#sb-counter a:hover img {
border-color: #0f0;
}
#sb-counter a.sb-counter-current img, #sb-counter a.sb-counter-current:hover img {
border-color: #fff;
cursor: default;
}
And that's it, thumbnail navigation in the Shadowbox. As I've mentioned, I don't think the navigation of large galleries seems very intuitive and could become an issue if the gallery contained narrow images that would cause the counter to get cut off, but that's something I'll look at later.
The first thumbnail image is hidden using CSS and the gallery is activated from an onclick handler on the bigger image.
This is just to show how the navigation can 'break' if the linked image isn't wide enough.
Just to check that the standard functionality is not affected by changes