
Beginning JavaScript With DOM Scripting And Ajax - From Novice To Professional (2006)
.pdf
396 |
C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
Figure 10-1. Using JavaScript to simulate a server-controlled dynamic image gallery
Displaying Captions
Thumbnail galleries are visual constructs, but it is still a good idea to think about alternative text and image captioning. Not only will these make your gallery accessible to blind users, but they also allow for searching the thumbnail data and indexing it through search engines.
Many tools like Google’s Picasa allow for dynamic captioning and addition of alternative text. You could use XHR to create something similar, but as this is a book about JavaScript, and how to store the entered data on the server would need some explanation, it is not a relevant example. Instead, let’s modify the “fake” gallery so that it displays captions.
You’ll use the image’s title attribute as a caption for the image; this means that the static HTML needs proper alternative text and title data.
C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
397 |
exampleFakeDynamicAlt.html (excerpt)
<ul id="thumbs"> <li>
<a href="galleries/animals/dog2.jpg">
<img src="galleries/animals/tn_dog2.jpg" title="This square is mine"
alt="Dog in a shady square" /> </a>
</li>
<li>
<a href="galleries/animals/dog3.jpg">
<img src="galleries/animals/tn_dog3.jpg" title="Sleepy bouncer"
alt="Dog on the steps of a shop" /> </a>
</li>
[... More thumbnails ...] </ul>
The script itself does not have to change much; basically all it needs is an extra paragraph in the generated image container and alterations of the methods to send the caption and alternative text data to the large image container.
fakeDynamicAlt.js
fakegal = { // IDs
thumbsListID : 'thumbs', largeContainerID : 'photo',
//CSS classes closeClass : 'close', nextClass : 'next', prevClass : 'prev', hideClass : 'hide', closeLabel : 'close',
captionClass : 'caption',
//Labels
showClass : 'show',
prevContent : '<img src="last.gif" alt="previous photo" />', nextContent : '<img src="next.gif" alt="next photo" />',
398 |
C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
The first change is a cosmetic one: you add a new CSS class that will be applied to the caption.
fakeDynamicAlt.js (continued)
init : function() {
if( !document.getElementById || !document.createTextNode ) { return;
}
fakegal.tlist = document.getElementById( fakegal.thumbsListID ); if( !fakegal.tlist ) { return; }
var thumbsLinks = fakegal.tlist.getElementsByTagName( 'a' ); fakegal.all = thumbsLinks.length;
for( var i = 0; i < thumbsLinks.length; i++ ) { DOMhelp.addEvent( thumbsLinks[i], 'click', fakegal.showPic, false );
thumbsLinks[i].onclick = DOMhelp.safariClickFix; thumbsLinks[i].i = i;
}
fakegal.createContainer();
},
showPic : function( e ) {
var t = DOMhelp.getTarget( e );
if( t.nodeName.toLowerCase() != 'a' ) { t = t.parentNode;
}
fakegal.current = t.i;
var largePic = t.getAttribute( 'href' );
var img = t.getElementsByTagName( 'img' )[0]; var alternative = img.getAttribute( 'alt' ); var caption = img.getAttribute('title');
fakegal.setPic( largePic, caption, alternative );
DOMhelp.cancelClick( e );
},
C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
399 |
The init() method remains unchanged, but the showPic() method needs to read the alternative text and the title attribute of the image, in addition to the href attribute of the link, and send all three of them as parameters to setPic().
fakeDynamicAlt.js (continued)
setPic : function( pic, caption, alternative ) { var a;
var picLink = fakegal.c.getElementsByTagName( 'a' )[1]; picLink.innerHTML = '';
fakegal.caption.innerHTML = ''; if( typeof pic == 'string' ) {
fakegal.c.className = fakegal.showClass; var i = document.createElement( 'img' ); i.setAttribute( 'src', pic ); i.setAttribute( 'alt' ,alternative ); picLink.appendChild( I );
} else { fakegal.c.className = '';
}
a = fakegal.current == 0 ? 'add' : 'remove'; DOMhelp.cssjs( a, fakegal.prev, fakegal.hideClass );
a = fakegal.current == fakegal.all-1 ? 'add' : 'remove'; DOMhelp.cssjs( a, fakegal.next, fakegal.hideClass );
if( caption != '' ) {
var ctext = document.createTextNode( caption ); fakegal.caption.appendChild( ctext );
}
},
The setPic() method now takes three parameters instead of one—the source of the large picture, the caption, and the alternative text. The method needs to delete any caption that may already be visible, set the alternative text attribute of the large picture, and display the new caption.
400 C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y
fakeDynamicAlt.js (continued)
navPic : function( e ) {
var t = DOMhelp.getTarget( e );
if( t.nodeName.toLowerCase() != 'a' ) { t = t.parentNode;
}
var c = fakegal.current; if( t == fakegal.prev ) {
c -= 1;
}else { C += 1;
}
fakegal.current = c;
var pic = fakegal.tlist.getElementsByTagName( 'a' )[c]; var img = pic.getElementsByTagName( 'img' )[0];
var caption = img.getAttribute( 'title' ); var alternative = img.getAttribute( 'alt' );
fakegal.setPic( pic.getAttribute('href'), caption, alternative );
DOMhelp.cancelClick( e );
},
The navPic() method, just like the init() method, needs to retrieve the alternative text, the caption, and the source of the large picture and send them to setPic().
fakeDynamicAlt.js (continued)
createContainer : function() {
fakegal.c = document.createElement( 'div' ); fakegal.c.id = fakegal.largeContainerID;
var p = document.createElement( 'p' );
var cl = DOMhelp.createLink( '#', fakegal.closeLabel ); cl.className = fakegal.closeClass;
p.appendChild( cl );
DOMhelp.addEvent( cl, 'click', fakegal.setPic, false ); cl.onclick = DOMhelp.safariClickFix; fakegal.c.appendChild( p );
var il = DOMhelp.createLink( '#', '' ); DOMhelp.addEvent( il, 'click', fakegal.setPic, false ); il.onclick = DOMhelp.safariClickFix; fakegal.c.appendChild( il );
C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
401 |
fakegal.next = DOMhelp.createLink( '#', '' ); fakegal.next.innerHTML = fakegal.nextContent; fakegal.next.className = fakegal.nextClass;
DOMhelp.addEvent( fakegal.next, 'click', fakegal.navPic, false ); fakegal.next.onclick = DOMhelp.safariClickFix; fakegal.c.appendChild( fakegal.next );
fakegal.prev = DOMhelp.createLink( '#', '' ); fakegal.prev.innerHTML = fakegal.prevContent; fakegal.prev.className = fakegal.prevClass;
DOMhelp.addEvent( fakegal.prev, 'click', fakegal.navPic, false ); fakegal.prev.onclick = DOMhelp.safariClickFix; fakegal.c.appendChild( fakegal.prev );
fakegal.caption = document.createElement( 'p' ); fakegal.caption.className = fakegal.captionClass; fakegal.c.appendChild( fakegal.caption );
fakegal.tlist.parentNode.appendChild( fakegal.c );
}
}
DOMhelp.addEvent( window, 'load', fakegal.init, false );
The createContainer() method needs only one small change, which is creating a new paragraph in the container to host the caption.
As you can see, creating dynamic galleries with JavaScript means generating a lot of HTML elements and reading and writing of attributes. It is up to you to decide whether it is worth the hassle or not. This gets even worse when you want to offer pagination of the thumbnails.
Instead of doing all of this in JavaScript, you could do it on the back end (for example, with PHP or ASP.NET) and offer a fully functional gallery to all users, and only improve it via XHR and JavaScript.
Dynamic Thumbnail Galleries
Real dynamic thumbnail galleries use URL parameters, instead of lots of static pages, and create the pagination and show the thumbs or the large images depending on these parameters.
The demo examplePHPgallery.php works that way, and Figure 10-2 shows what this might look like.

402 |
C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
Figure 10-2. A dynamic PHP-driven thumbnail gallery example with thumbnail pagination and previous and next image preview on the large picture page
This gallery is fully functional and accessible without JavaScript, but you may not want to reload the whole page every time the user clicks a thumbnail. Using XHR, you can offer both. Instead of using the original PHP document, you use a cut-down version that only generates the content you need, in this case gallerytools.php. I won’t go into details of the PHP script; suffice to say that it does the following for you:
•It reads the contents of a folder the link in the main menu points to, checks it for images, and returns ten at a time as a HTML list with thumbnails linked to the large images.
•It adds a pagination menu that shows which ten of how many images in total are displayed and offers previous and next links.
•If any of the thumbnails is clicked, it returns the HTML of the large image and a menu showing the next and the previous thumbnail.

C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
403 |
You use this output to overwrite the HTML output of the original PHP script as shown in the demo examplePHPXHRgallery.php. Without JavaScript, it does the same as examplePHPgallery.php; however, when JavaScript is available, it will not reload the whole document, but only refresh the gallery itself. You achieve this by replacing the links in the content section with links to gallerytools.php and XHR calls instead of reloading the whole page.
dyngal_xhr.js
dyngal = {
contentID : 'content',
originalPHP : 'examplePHPXHRgallery.php', dynamicPHP : 'gallerytools.php',
init : function() {
if( !document.getElementById || !document.createTextNode ) { return;
}
dyngal.assignHandlers( dyngal.contentID );
},
You start by defining your properties:
•The ID of the element that contains the content that should be replaced with the HTML returned from gallerytools.php
•The file name of the original script
•The file name of the script that returns the data you call via XHR
The init() method tests for DOM support and calls the assignHandlers() method with the content element ID as a parameter.
■Note In this case, you only replace one content element; however, as there may be situations where you want to replace numerous different sections of the page, it is a good idea to create separate methods for tasks like these.
404 C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y
dyngal_xhr.js (continued)
assignHandlers : function( o ) {
if( !document.getElementById( o ) ){ return; } o = document.getElementById( o );
var gLinks = o.getElementsByTagName( 'a' ); for(var i = 0; i < gLinks.length; i++ ) {
DOMhelp.addEvent( gLinks[i], 'click', dyngal.load, false ); gLinks[i].onclick = DOMhelp.safariClickFix;
}
},
The assignHandlers() method tests whether the element with the ID that was sent as a parameter exists, and then loops through all the links in the element. Next, it adds an event handler pointing to the load method and applies the Safari fix to prevent that browser from following the original link (remember—the preventDefault() method used in cancelClick() is supported by Safari, but does not stop the link from being followed due to a bug in Safari).
dyngal_xhr.js (continued)
load : function( e ) {
var t = DOMhelp.getTarget( e );
if( t.nodeName.toLowerCase() != 'a' ) { t = t.parentNode;
}
var h = t.getAttribute( 'href' );
h = h.replace( dyngal.originalPHP, dyngal.dynamicPHP ); dyngal.doxhr( h, dyngal.contentID ); DOMhelp.cancelClick( e );
},
In the load method, you retrieve the event target and make sure it is a link. You then read the href attribute of the link and replace the original PHP script name with the dynamic one that only returns the content you need. You call the doxhr() method with the href value and the content element ID as parameters and stop the link propagation by calling cancelClick().
dyngal_xhr.js (continued)
doxhr : function( url, container ) { var request;
try{
request = new XMLHttpRequest();
}catch( error ) { try {
request = new ActiveXObject("Microsoft.XMLHTTP");
}catch ( error ) { return true;

C H A P T E R 1 0 ■ M O D E R N J A V A S C R I P T C A S E S T U D Y : A D Y N A M I C G A L L E R Y |
405 |
}
}
request.open( 'get', url, true ); request.onreadystatechange = function() {
if( request.readyState == 1 ) { container.innerHTML = 'Loading...';
}
if( request.readyState == 4 ) {
if( request.status && /200|304/.test( request.status ) ) { dyngal.retrieved( request, container );
}else {
dyngal.failed( request );
}
}
}
request.setRequestHeader( 'If-Modified-Since', 'Wed, 05 Apr 2006 00:00:00 GMT');
request.send( null ); return false;
},
retrieved : function( request, container ) { var data = request.responseText;
document.getElementById(container).innerHTML = data; dyngal.assignHandlers( container );
},
failed : function( request ) {
alert( 'The XMLHttpRequest failed. Status: ' + requester.status ); return true;
}
}
DOMhelp.addEvent( window, 'load', dyngal.init, false );
The XHR methods are the same as those you used in Chapter 8. The only difference is that you need to call the assignHandlers() method again, as you replaced the original content and as a result lost the event handlers on the links.
■Note PHP and JavaScript are a powerful combination. Once you have mastered JavaScript, it might be a good idea to look into PHP, as without knowledge of a server-side language Ajax is less fun. PHP can do what JavaScript cannot—reach files on the server and read their names and properties, and even reach content from third-party servers. PHP syntax is similar to that of JavaScript in some respects, and the nature of the language makes it easy to take the first steps without having to deal with compilers or command-line interfaces to build your scripts.