The first and still best way to create and maintain a thumbnail gallery is to create it on the backend. We grab the image material from a database, generate the thumbnails on the fly and allow the visitor to navigate from thumbnail pages to the big picture ones and back with a set of linked templates. This will guide the visitors through the whole image collection, and they can bookmark certain pictures. These galleries are fully accessible –- if we go through the sometimes cumbersome process of adding good alternative text.
These galleries do not necessarily need a database and scripting enabled on the server. Many tools generate static galleries that simply have to be transferred over to the server.
However, thumbnail galleries mean a lot of traffic and -- depending on your page templates and their page weight -- visitors might be annoyed by the reloads when navigating through the gallery. When your server is unable to serve dynamic pages, you also have to transfer a lot of files for each update: the thumbnails, the images, the thumbnail HTML documents and one HTML document for each image.
Basic Functionality of a Thumbnail Gallery
Let’s take a step back and look at what a thumbnail gallery should do for us. First, it should give us thumbnails -- small images that load fast and get cached to give us an overview of all the images available.
It should be common sense, but thumbnails should be small both in physical size and file size. Thumbnail galleries are there to give users with a slow connection a preview of the images, not to force them to load the full images alongside.
There are Javascript and CSS “no preload” galleries on the Web that violate exactly that idea. Resizing images via CSS, Javascript or deprecated HTML attributes is not a clever idea, no matter how you cut it. It simply means that a visitor needs to load the entire gallery if she wants to see only selected images.
The thumbnails should give a good impression of what the image is and provide proper alternative text for accessibility reasons; these texts act as a fallback should images not be available. We could also provide the visitor with information about the dimension and file size of the big picture.
The thumbnails should link to big images which get opened in the same browser, and -- when it comes to generated or dynamic galleries -- provide a link back to the thumbnails. Really usable galleries also have links to the next and previous images and tell the visitor how many more are in this album.
Old School Dynamic Galleries
When Javascript became more common, many developers with a lot of time and a lot of enthusiasm on their hands came up with hundreds of “DHTML galleries,” at times appallingly one-browser-centric and bloated ones. Nearly all of them used pop-up windows to display the images, and relied on a lot of inline event calls with a lot of parameters:
HTML:
<a href="javascript:showpic('images/archway.jpg',300,300) ">
<img src="images/tn_archway.jpg" alt="Archway with natural green roof" />
</a>
JavaScript:
function showpic(pic,x,y)
{
newwin=window.open(pic,'newwin','width='+x+',height='+y+'toolbar=no')
newwin.moveTo(screen.width/2-x/2,200);
}
This practice leads to completely dead galleries in browsers without JavaScript. A better way is to always ensure there is a link to the image and JavaScript that adds the pop-up functionality on top of that.
<a href="images/archway.jpg" onclick="showpic('images/archway.jpg',300,300);return false;"> <img src="images/tn_archway.jpg" alt="Archway with natural green roof" /> </a>
These pop-up window examples still did not allow for a link back or extra information to be displayed. Seeing that as a problem, developers started writing out HTML dynamically:
HTML:
<a href="images/archway.jpg" onclick="showpic('images/archway.jpg','An archway in Santorini',300,300);return false;"> <img src="images/tn_archway.jpg" alt="Archway with natural green roof" /></a>
Javascript:
function showpic(pic,message,x,y)
{
newwin=window.open(pic,'newwin','width='+x+',height='+y+'toolbar=no')
newwin.document.open();
newwin.document.write('<html><head><title>Big picture</title></head>');
newwin.document.write('<body bgcolor="#0000000">');
newwin.document.write('<a style="float:right;font-family:arial;color:#369;"
href="javascript:window.close()">Close</a>');
newwin.document.write('<h1
style="color:#fff;font-size:12px;font-family:arial;">'+message+'</h1>');
newwin.document.write('<img src="'+pic+'" alt="'+message+'">');
newwin.document.write('</body></html>');
newwin.moveTo(screen.width/2-x/2,200);
}
There was no end to their creativity: sliding windows, fading in images, galleries with templates using the window location to read image location and message, and lots more. All of them had several drawbacks:
- Bad maintainability - the idea of a dynamic gallery is that it enhances the basic HTML without any extra link event calls with a lot of parameters.
- Bad Accessibility - they relied on Javascript for the functionality.
- Bad performance – Instead of one browser, the machine needed to start a new window every time the user clicked a thumbnail.
- Pop-ups - These might be blocked by a lot of users nowadays.
Let’s try to keep things as simple as possible, and not listen to the "feature creature" on our shoulders telling us to add more, more and some more later.
Sketching our Gallery
The gallery we are about to create should have the following features:
- Clean, simple markup – The HTML developer should not need to add any extra information for our gallery to work.
- Full accessibility – Users without Javascript should simply get the linked picture displayed in the browser.
The HTML Markup
In order to make scripts work without inline event calls, we need to use the Document Object Model (DOM) to reach what we want to enhance. To avoid extensive searching in the document, we wrap our gallery in an element with a certain ID, for example “thumbs.” Each thumbnail will be contained in its own DIV, to allow for captions, should we want them, and is linked to the main image.
<div id=”thumbs”>
<div>
<a href="images/archway.jpg"><img src="images/tn_archway.jpg" alt="Archway with natural green roof" /></a>
</div>
</div>
You might wonder why we don’t use a table for a thumbnail gallery, as technically a gallery with captions could be seen as tabular data. Furthermore a table degrades as a grid layout in non-CSS capable browsers, and our example shows up as one thumb under the other in those. The reason is that it is easier to maintain, renders faster and could be designed as liquid, something you cannot do with a table.
The script we are about to create can take both, so if you feel more comfortable using tables, go for it.
In terms of accessibility, our example is perfectly acceptable. The nature of thumbnail galleries makes them not much use to blind or text browser users, but by giving the thumbnails a proper alternative text, even those users know what is linked. We could add a title to the links stating that it links to an image, though. Keyboard users have no problem with the gallery either –- they can tab from link to link (or press A in Opera), and hit enter to load the image. Pressing “Ctrl + Cursor Left” gets them back to the gallery.
The Javascript
Our script will follow the rules of unobtrusive Javascript. We use the Document Object Model to access our markup, create and destroy elements on the fly, and will not use any global variables. The whole script will be self-contained and should be easy to mix and match with other scripts. The only overlap is when we call it when the page has loaded. If you are not familiar with unobtrusive Javascript, you can become familiar with it by taking the self-training course to get your skills up-to-date: http://www.onlinetools.org/articles/unobtrusivejavascript/
To apply our functionality, all we need to do is write a function that loops through all the links inside the element with the ID “thumbs,” and, to avoid the use of a pop-up window, we will generate a DIV dynamically and insert the image in that one.
function dyngallery()
{
var picId='bigDynPic';
var d=document.getElementById('thumbs');
if(!d){return;}
var piclinks=d.getElementsByTagName('a');
for(var i=0;i<piclinks.length;i++)
{
// [..]
}
}
Once the user clicks on any of the links inside the “thumbs” element, we want to do the following:
- Check if there is already an element with the same ID as picId.
- If that is the case, remove this element – this is necessary to make the element “shrink-wrap” around our image.
- Create a new DIV element with the ID defined in picId.
- Create a new image with the same source the thumbnail-link points to.
- Set the alternative text of the image.
- Add a title to the image, telling the visitor that clicking the image will get her back to the thumbnails.
- Add a function to the image that removes the image when you click on it.
- Add a paragraph below displaying the alternative text of the thumbnail.
- Not return to the document, to avoid the real link being followed. If there is a Javascript error, the script will fail and the browser will load the big image instead.
function dyngallery()
{
var picId='bigDynPic';
var d=document.getElementById('thumbs');
if(!d){return;}
var piclinks=d.getElementsByTagName('a');
for(var i=0;i<piclinks.length;i++)
{
piclinks[i].onclick=function()
{
var oldp=document.getElementById(picId);
if(oldp)
{
oldp.parentNode.removeChild(oldp);
}
var nc=document.createElement('div');
d.parentNode.insertBefore(nc,d);
nc.style.display='none';
nc.id=picId;
var newpic=document.createElement('img');
newpic.src=this.href;
newpic.alt=this.getElementsByTagName('img')[0].alt;
newpic.title='Click to return to images';
newpic.onclick=function()
{
this.parentNode.parentNode.removeChild(this.parentNode);
}
nc.appendChild(newpic);
np=document.createElement('p');
np.appendChild(document.createTextNode(this.getElementsByTagName('img')[0].alt))
nc.appendChild(np);
nc.style.display='block';
return false;
}
}
}
Now all we need to do to make our gallery work is fire up our function once the page is loaded, after we checked that the browser does understand the DOM.
window.onload=function()
{
if(document.getElementById && document.createTextNode)
{
dyngallery();
}
}
Almost, that is. The functionality we developed depends on a mouse. Keyboard users can open the big image, but they cannot close it, as the newly generated DIV does not get the focus, and setting it via focus() doesn’t work in all browsers. Therefore we need to ensure that only mouse users get the functionality:
window.onload=function()
{
if(document.getElementById && document.createTextNode)
{
document.body.onmouseover=function()
{
dyngallery();
}
}
}
See the example with a proper style sheet in action here: http://icant.co.uk/articles/dyngallery/gallery.html
The Stylesheet
There are numerous ways to style an image gallery. The technique we use here is to give the thumbnail DIVs a certain size and float them left. Then we fix the width of the “thumbs” element, which causes the thumbnails to neatly sort themselves into rows. This also means that should we resize the “thumbs” element, we won’t need to rearrange the thumbnails; they’ll do that automatically.
#thumbs{
width:700px;
}
#thumbs div {
margin:5px;
width:120px;
height:110px;
float:left;
}
#thumbs div img{
border:none;
display:block;
margin:5px auto;
}
For the big image we need to define the bigDynPic DIV as absolutely positioned over the others. This is the DIV that our script generates.
#bigDynPic{
background:#369;
position:absolute;
top:1em;
left:80px;
padding:5px;
}
Adding more features
So now we have it, a thumbnail gallery that displays the big picture without reloading the page or opening a pop-up, fully accessible and only available to those who really can use it. We could add a “loading” message while the image is being loaded, to avoid confusion for users on slow connections.
function dyngallery()
{
var picId='bigDynPic';
var loadingId='loadingmessage';
var d=document.getElementById('thumbs');
if(!d){return;}
if(!document.getElementById(loadingId))
{
var lo=document.createElement('div');
lo.appendChild(document.createTextNode('Loading image'));
d.parentNode.insertBefore(lo,d);
lo.id=loadingId;
lo.style.display='none';
}
var piclinks=d.getElementsByTagName('a');
for(var i=0;i<piclinks.length;i++)
{
piclinks[i].onclick=function()
{
document.getElementById(loadingId).style.display='block';
var oldp=document.getElementById(picId);
if(oldp)
{
oldp.parentNode.removeChild(oldp);
}
var nc=document.createElement('div');
d.parentNode.insertBefore(nc,d);
nc.style.display='none';
nc.id=picId;
var newpic=document.createElement('img');
newpic.src=this.href;
newpic.alt=this.getElementsByTagName('img')[0].alt;
newpic.title='Click to return to images';
newpic.onload=function()
{
document.getElementById(loadingId).style.display='none';
}
newpic.onclick=function()
{
this.parentNode.parentNode.removeChild(this.parentNode);
}
nc.appendChild(newpic);
np=document.createElement('p');
np.appendChild(document.createTextNode(this.getElementsByTagName('img')[0].alt))
nc.appendChild(np);
nc.style.display='block';
return false;
}
}
}
Just like the big image, the loading message will need to be positioned absolutely.
#loadingmessage{
position:absolute;
top:200px;
left:150px;
padding:1em 5px;
background:#ffc;
font-family:Verdana,Sans-serif;
font-weight:bold;
width:20em;
text-align:center;
font-size:80%;
color:#000;
}
This allows us to display a range of images in a slick, nice way that does not force the visitor to reload the page for every image. But what if we are dealing with a sequence of images?
A Sequential Gallery
Images that follow a sequence need another approach. For the visitor, it would be very annoying to click each of the thumbnails separately and click the real size picture to get back. A sequential viewer is needed.
The functionality will include the following:
- The visitor sees all images as thumbnails; clicking on one takes her to the sequential viewer.
- The viewer displays the image in its real size and next to it the same image as a thumbnail. Below the thumbnail it displays the next one and above the previous one.
Our script needs to do the following:
- To avoid the need of two different style sheets, the script adds a class called “sequential” to the “thumbs” div. This means that in the style sheet we simply need to overrule the original settings with the new ones by increasing the selector’s specificity:
thumbs div { … }
#thumbs.sequential div {} - Loop through all the thumbnails and hide them. If the thumbnail is the one that was clicked on, it gets an extra class called “current,” and the previous and the next one do not get hidden but displayed as a block element rather than floated.
- Add a function that reloads the entire document when the user clicks on the big image.
Other than that our script stays the same, we generate a DIV which will contain the big image and display when the user clicks on a thumbnail.
function sequentialgallery()
{
var picId='bigDynPic';
var d=document.getElementById('thumbs');
if(!d){return;}
var piclinks=d.getElementsByTagName('a');
for(var i=0;i<piclinks.length;i++)
{
piclinks[i].i=i;
piclinks[i].onclick=function()
{
if(!/sequential/.test(d.className)){d.className='sequential';}
for(var j=0;j<piclinks.length;j++)
{
piclinks[j].parentNode.className=(j==this.i)?'current':'';
piclinks[j].parentNode.style.display=(j<this.i-1 || j>this.i+1)?'none':'block';
}
var oldp=document.getElementById(picId);
if(oldp)
{
oldp.parentNode.removeChild(oldp);
}
var nc=document.createElement('div');
d.parentNode.insertBefore(nc,d);
nc.style.display='none';
nc.id=picId;
var newpic=document.createElement('img');
newpic.src=this.href;
newpic.alt=this.getElementsByTagName('img')[0].alt;
newpic.title='Click to return to images';
newpic.onclick=function()
{
window.location=window.location;
}
nc.appendChild(newpic);
np=document.createElement('p');
np.appendChild(document.createTextNode(this.getElementsByTagName('img')[0].alt)) |
nc.appendChild(np);
nc.style.display='block';
return false;
}
}
}
See the example with a proper style sheet in action here: http://icant.co.uk/articles/dyngallery/gallery2.html
The only difference in the style sheet is that we need to add the selectors for the thumbnails in the sequential view:
#thumbs.sequential div{
float:left;clear:both;margin-bottom:0;
}
#thumbs.sequential div.current{
background:#036;
}
The script adds the class “sequential” to the “thumbs” element when the sequential view is needed, therefore we don’t float the thumbnails in this one, but clear all floats to display them above one another. Theoretically, setting their display value to block should have done the same, but some margin-collapsing bug in Mozilla forces us to go this route. The “current” class is added to the thumbnail of the currently visible image, and all we add here is a different background color.
Other options
The options we have with this script are endless, we could add more features and ideas as we get along, but sometimes keeping it simple and straight to the point gives the visitor a much better experience than confusing her with features.
However, possible enhancements include:
- Display the information of how many images are there in total and where we are in the sequence. We’ll need to send the index of the clicked thumbnail onclick and read the total number of images via piclinks.length.
- Offer links to the first and last image.
- Display the image data by reading its dimensions after the image was loaded.
Whatever we want to do, let’s make sure we check if it is possible first, and then display it, rather than making assumptions about what the visitor can or cannot do or have.
By Chris Heilmann
Source: devarticles.com
