Snapshots have reappeared on Abouthalf after some hefty tinkering.
If you’re a serious minded web dork, then you have no doubt heard all the buzz about this so-called Ajax thing (the melding of client-side scripting and JavaScript to make highly responsive web-based applications like Google Maps.)
I think this is part of a JavaScript renaissance.
Web developers and client alike have come to the realization that 99% of web browsers in use have enough common standards support that we can use neat-o stuff like CSS without worrying to much about being on the bleeding edge.
With CSS fully entrenched, JavaScript DOM methods are the next logical step. Most browsers support these methods pretty well and pretty consistantly, so it’s only natural to see it coming back to the stage in a loud spectacular way.
So.
If you think about a web page as an application, and not as “just” a document, you could think of the HTML as the data, JavaScript as the model, and CSS as the view. You know. If you wanted to apply software development metaphors to web content.
I was thinking along those lines when I was worrying about how to do a snapshot gallery that worked in the context of my xml based site management tool.
I didn’t want to make 80 bazillion HTML documents (again) to hold one picture a piece, in sequence with a bunch of other documents.
I also didn’t want to hack and crack a PHP script into making HTML docs for my CMS to parse on the fly. That would be kind of crufty.
I decided that a client-side solution makes the most sense.
Think about it.
• Why should one gallery be made of many HTML pages? It’s one “thing” why not keep it on one page? It should be simpler to maintain one page than several
• One could write a server-side script to create a photo gallery, but it would be hard to customize an individual gallery, or the script would become overly complex.
• A client side gallery should work faster, because it’s all the same document, only the images are changing.
So I decided to write a JavaScript gallery.
There are good examples out there which use DOM techniques to show and hide images. But none of the techniques I looked into resolved one simple problem.
The back button.
Most DHTML or DOM scripts which show and hide page elements work within one document. So the URI never changes. So you can’t click Back.
That sucks.
I want users to click a picture, see a detail, then hit back to get back to the thumbnails. Of course I’ll have a link to the thumbnails…but people love the back button. Let them have it.
My first idea was to create a gallery which relied on named anchors to show and the thumbnail pictures and full sized images.
I started by making a gallery page with a long vertical column of full sized images, in DIV tags. Each DIV had an id, img1, img2, img3, etc.
Each image was within an anchor which linked to the next image. So image #1 linked to image #2. The last image linked back to the first image. Under each image would be a handy “back” link which linked to the top of the page.
Each anchor simply linked to the id of the DIV I wanted to show. So if the anchor’s href was “#img1″ I’d show image #1, hide all the other. If I didn’t have an image#1, then I’d show the thumbnails.
But this plan was not to be. While the JavaScript worked in theory, and in practice in FireFox, I fell victim to a strange behavioral quirk of most browsers. If you navigate between named anchors within a single HTML document, the JavaScript window.location object does not get updated to reflect the current location. So while the address bar would change, JavaScript had no idea what was in the address bar.
The short version? No back button. Without the window.location object getting updated, you can’t detect the new address (with the new anchor) and update the page. Well. You can in FireFox. Yay team.
This almost broke my heart. Using anchors would have meant no trips back to the server, and it would be super fast.
But it was not to be.
So I reworked the whole thing to use dynamically generated query strings instead. Now we’ve got some back button.
So how does it work?
Each photo gallery has the following basic structure:
The div with id “gallery” is a container for the thumbnails and images. This is basically an area of isolation for use with CSS styling.
The div “intro” holds introductory text, if needed.
The “images” div holds all of the thumbnail images, each linked to it’s large counterpart.
This provides a good default view which is accessible in browsers without scripting or CSS, and makes the gallery indexable by search engines (Hi Google).
Auto Gallery
First a formal ‘thumbnail view’ is created from the content of the “images” div. The achors are cloned, and the href attribute is updated to link to the corresponding div’s id.
function createThumbs() {
// create thumbnail link container
var theThumbs = document.createElement(“DIV”);
theThumbs.id = “thumbs”;
theThumbs.style.display = “none”;
var theImages = document.getElementById(“images”);
var imgDivs = theImages.getElementsByTagName(“DIV”);
var l = imgDivs.length;
for(i=0;i
var myId = d.id;
var myLink = d.getElementsByTagName(“A”)[0].cloneNode(true);
myLink.href = “?”+ myId;
myLink.title = “”;
theThumbs.appendChild(myLink);
}
theImages.parentNode.appendChild(theThumbs);
}
Next there’s a generic “hide all” function which gets called each time the gallery is formatted.
function galleryHideAll() {
// hide thumbs
document.getElementById(“thumbs”).style.display = “none”;
document.getElementById(“intro”).style.display = “none”;
// hide images
var imgDivs = document.getElementById(“images”).getElementsByTagName(“DIV”);
var d = imgDivs.length;
for(i=0;i
}
}
The gallery is formatted by a show/hide function which is called when the page loads, and consequently each time a link is clicked in the gallery.
function galleryShowHide(trig) {
// hide all by default
galleryHideAll();
// get item from search string
if (!trig) {trig = String(window.location.search);}
var loc = (trig.split(“?”))[1];
// show selected item, default to thumbs
var show = document.getElementById(loc); // container for target image
if (show && loc!=”thumbs”) {
show.style.display = “block”;
show.getElementsByTagName(“IMG”)[0].src = show.getElementsByTagName(“IMG”)[0].src.replace(/thumbnails/,”img”);
document.getElementById(“backLink”).style.display = “block”;
document.getElementById(“intro”).style.display = “none”;
} else {
document.getElementById(“thumbs”).style.display = “block”;
document.getElementById(“backLink”).style.display = “none”;
document.getElementById(“intro”).style.display = “block”;
}
}
The last step is to update all the images to link to the next image in sequence.
function applyGallery() {
// link boxes
var lbx = document.getElementById(“images”).getElementsByTagName(“DIV”);
var l = lbx.length;
for (var i=0;i
var theLink = theBox.getElementsByTagName(“A”)[0];
// get next or first link
var nxtBox = lbx[i+1]; if (!nxtBox) {nxtBox = lbx[0];}
var linkTo = nxtBox.id;
theLink.href = window.location.pathname + “?” + linkTo;
}
// target back link to thumbnails
document.getElementById(“backLink”).href = window.location.pathname + “?thumbs”;
}
To apply this functionality to any gallery in my web site, I have an initialization function which checks JavaScript DOM methods, checks for required elements (the gallery, intro, and images), constructs the thumbnails, then shows the appropriate image. The initialization function is added to the window.onload event.
function initGallery() {
// filter non-DOM browsers
if ((!document.getElementById && !document.createElement)) {return}
// check for required elements
if (!document.getElementById(“gallery”) && !document.getElementById(“images”) && !document.getElementById(“intro”)) {return}
// do createThumbs
if (!document.getElementById(“thumbs”)) {createThumbs();}
// hide all by default
galleryHideAll();
// show or hide
galleryShowHide(false);
// set onclick for gallery anchors
applyGallery();
}
var galleryOldOnLoad = (window.onload) ? window.onload : function() {};
window.onload = function() {galleryOldOnLoad(); initGallery();}
The CSS used to format the gallery is pretty straight forward:
div#gallery div#thumbs a {
display: block;
width: 77px;
height:77px;
float: left;
margin: .25em;
text-align: center;
text-decoration: none;
}
div#gallery div#thumbs img {
border: 1px solid #f93;
}
div#gallery div#images div {
text-align: center;
padding: 1em;
display: none; /* display managed by scripting */
}
div#gallery div#images div.galleryImg img {
border: 1px solid #f93;
}
div#gallery a.return {
display: block;
clear: left;
text-align: center;
margin: .5em;
display: none; /* display managed by scripting */
}
The entire JavaScript can be found here.
And there you go. Instant gallery. One HTML document refactored via JavaScript to display as several pages. And the back button works.
