Part 4: Custom Objects, in code
Didn't I promise there would be code?
I expect that you are already familiar with the JavaScript syntax used below, so I won't go into too much detail. You will notice the regular use of the reference this. When you have used it previously it may have refererred to the document or a form element when working with event handlers like onchange. In its use here it is referring to the object that we are creating.
If you peeked at the source code of the finished example you will notice that I've put each object that was created into its own source file, this is also how I broke it down here. This is done not only because it results in a clean final document, but because it encourages reuse of the objects in other projects. All I have to do is link the object I want to use from another document and it is available.
csnPhotoNavObject.js
// object constructor function csnPhotoNavObject(csnPhoto) { // define object's properties this.photo = csnPhoto; // attach object's methods this.handleMouseOver = csnPhotoNavObjectShowThumb; this.handleMouseOut = csnPhotoNavObjectHideThumb; this.handleClick = csnPhotoNavObjectHandleClick; } // define object's methods function csnPhotoNavObjectShowThumb() { // simply hand off this off to the photo object this.photo.showThumb(); } function csnPhotoNavObjectHideThumb() { // simply hand off this off to the photo object this.photo.hideThumb(); } function csnPhotoNavObjectHandleClick() { // our other events were a bit simpler, here we want to return // false in addition to showing the full photo this.photo.showFull(); // first hand the display of the image off return false; // then return false }
csnPhotoObject.js
// object constructor function csnPhotoObject(thumbID,thumbOffURI,thumbOnURI,fullID,fullURI) { // define object's properties this.thumbDOMRef = document.getElementById(thumbID); this.fullDOMRef = document.getElementById(fullID); this.thumbOffImg = new csnSmartImageObject(thumbOffURI,true); this.thumbOnImg = new csnSmartImageObject(thumbOnURI,true); // we don't want to load the full image until its needed this.fullImg = new csnSmartImageObject(fullURI,false); // attach object's methods this.showThumb = csnPhotoObjectShowThumb; this.hideThumb = csnPhotoObjectHideThumb; this.showFull = csnPhotoObjectShowFull; } // define object's methods function csnPhotoObjectShowThumb() { this.thumbDOMRef.src = this.thumbOnImg.getImage(); } function csnPhotoObjectHideThumb() { this.thumbDOMRef.src = this.thumbOffImg.getImage(); } function csnPhotoObjectShowFull() { this.fullDOMRef.src = this.fullImg.getImage(); }
csnSmartImageObject.js
// object constructor function csnSmartImageObject(imgURI,preload) { // define local properties this.URI = imgURI; // we don't need this image until we are ready to load this.imageobj = null; // attach object's methods this.load = csnSmartImageObjectLoad; // force the loading of the image this.getImage = csnSmartImageObjectGetImage; /* this function will retrieve the source of the image from this object for use by other javscript objects */ // complete constuctor if (preload) { // if preload is true load the image up right away this.load(); } } // define object's methods function csnSmartImageObjectLoad() { this.imageobj = new Image(); // create the image object this.imageobj.src = this.URI; } function csnSmartImageObjectGetImage() { if (this.imageobj) { // if we already have it, send the src along return this.imageobj.src; } else { // if not send the URI to the source back /* note, it may be better form to load() and then return the full source when its done loading, but this is a much easier way to code it and works because the image.src is "overloaded" and can handle either case */ this.load(); return this.URI; } }
prototype.html
I have always found it easier to work first with prototypes. It allows me to work on the graphic production issues separately from the interactive and scripting issues. It is also very convienient for completely scrapping if you find that you've gone down the wrong direction. For this particular project I worked up a small functional prototype to help in building the above javascript objects.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="en"> <head> <title>Object Oriented JavaScript - Photo Interface Example</title> <script type="text/javascript" src="csnSmartImageObject.js"></script> <script type="text/javascript" src="csnPhotoObject.js"></script> <script type="text/javascript" src="csnPhotoNavObject.js"></script> <script type="text/javascript"> <!-- var elements = new Array(); var thumbnailID = "thumbnail"; // this is universal for the page var emptyimg = "empty.gif"; // this is universal for the page var photoID = "bigimage"; // this is universal for the page function initpage() { elements[0] = new csnPhotoNavObject(new csnPhotoObject(thumbnailID,emptyimg, "photo1_tn.gif",photoID,"photo1.gif")) } // --> </script> <style type="text/css"> <!-- body { color:black; background-color:white; } img { border:1px red solid; } a { color:blue; font-size:20px; } a:hover { color:red; } a:active { color:purple; } --> </style> </head> <body onload="initpage()"> <div> <a href="#" id="navthing" onmouseover="elements[0].handleMouseOver()" onmouseout="elements[0].handleMouseOut()" onclick="return elements[0].handleClick()">1</a><br> <img id="thumbnail" src="empty.gif" height="60" width="40" alt="thumbnail"> <img id="bigimage" src="empty.gif" height="300" width="200" alt="photo"> </div> </body> </html>
finishedgallery.html
Once all the scripting above was finished and working in the prototype I went ahead and put together the finished gallery. I've done a small amount of cleanup in the HTML code (e.g. adding other content; accounting for lack of javascript). I've also added a good deal of CSS to achieve the layout I wanted. The important thing, is that the JavaScript portions of the gallery are, aside from some variable assignments, identical to the prototype.