Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning JavaScript With DOM Scripting And Ajax - From Novice To Professional (2006)

.pdf
Скачиваний:
81
Добавлен:
17.08.2013
Размер:
17.27 Mб
Скачать
Note

C H A P T E R 1 0

■ ■ ■

Modern JavaScript Case Study:

A Dynamic Gallery

In this chapter, you will learn how to develop a JavaScript-enhanced thumbnail gallery backed up by a PHP script. You’ll start with techniques of static galleries and how to enhance them and move on to a gallery that uses PHP and Ajax to pull images dynamically from the server.

You can download the demo code of this chapter or see the results online at http:// www.beginningjavascript.com. As the chapter contains image galleries, the download is on the larger side, but it allows you to see all the code—including the server-side PHP—on your local server.

Basics of Thumbnail Galleries

Let’s start at the basics first and plan our thumbnail gallery. I pondered for a long time whether I should include one in this book, as it has become almost cliché for JavaScript and CSS books to have galleries as examples. However, I wrote this chapter to give an example of how you can spice up a very common solution like a thumbnail gallery with modern scripting and CSS and stay independent of the both of them. Many examples—especially CSS-only galleries—look great and work in modern browsers; however, they don’t degrade well and don’t really deliver what a thumbnail gallery is supposed to deliver.

387

388

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

What Is a Thumbnail Gallery and

What Should It Do?

The idea of a thumbnail gallery goes back to the times when browsers started to support images, and connection speeds on the Web could be measured in the single kilobits. The job of such a gallery was and still is to give an overview of what images are available by providing a smaller preview of each image in the gallery. “Smaller” means smaller in dimensions but also—and most importantly—smaller in file size. This means that a visitor who is only interested in one picture in your gallery does not need to download all images, only the one he is interested in—saving both him time and you server traffic. A lot of CSS-only or JavaScript/HTML thumbnail galleries fail to do this and assume that every user wants to download the lot to see one image. You can offer downloading of all the images, but it should be an option rather than a requirement. The worst thumbnail galleries resize photos via HTML attributes or CSS to thumbnails, thus forcing visitors to download the large images to see them as bad-quality thumbnails. Resizing images by altering their dimensions in CSS, via JavaScript, or by (ab)using HTML attributes does not result in goodquality thumbnails; it is simply laziness and a bad idea.

If you want to offer thumbnail galleries in their original sense, you need to generate smaller thumbnail pictures of the large images you want to show. You can do that either as a batch process before you upload the gallery or on the fly via scripting on the server.

Tip There are a lot of thumbnail-generation and batch-generation tools available. Good—and most importantly free—ones are Google’s Picasa (available at http://picasa.google.com/) and IrfanView (available at http://www.irfanview.com/). Generating thumbnails on the server can be easily achieved with PHP and the GD library. I’ve written an article how to do that, available at http://icant.co.uk/ articles/phpthumbnails/, and there is a great premade PHP class called phpThumb() available at http://phpthumb.sourceforge.net/. As this is a book about JavaScript, I will not get into the details of image generation via PHP, although it is amazingly handy for online galleries.

Static Thumbnail Galleries

Traditional thumbnail galleries offer the small thumbnail images inside a table or a list. Each of the thumbnails links to a page with the large image that in return links back to the thumbnail gallery or offers previous and next image links.

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

389

If there are a lot of images, the thumbnail pages may be paginated, showing a certain number of thumbnails at a time and offering navigation forward and backward through the whole collection. With purely static galleries, this means you have to generate all the thumbnail pages, and one for each photo, which is a lot of work initially and a lot of files to transfer to the server on every update of the gallery.

Faking Dynamic Galleries with JavaScript

You can use JavaScript to turn a static thumbnail gallery into a seemingly dynamic gallery by applying event handlers to all the thumbnails. When a thumbnail is clicked, you cover the thumbnails with a new element containing the large image. Keep the gallery accessible to non-JavaScript users by linking the thumbnail to the large picture and simply showing this in the browser:

exampleFakeDynamic.html (excerpt)

<ul id="thumbs"> <li>

<a href="galleries/animals/dog2.jpg">

<img src="galleries/animals/tn_dog2.jpg" alt="tn_dog2.jpg" /> </a>

</li>

<li>

<a href="galleries/animals/dog3.jpg">

<img src="galleries/animals/tn_dog3.jpg" alt="tn_dog3.jpg" /> </a>

</li>

<li>

<a href="galleries/animals/dog4.jpg">

<img src="galleries/animals/tn_dog4.jpg" alt="tn_dog4.jpg" /> </a>

</li>

[... more thumbnails ...] </ul>

Tip You might as well use tables or definition lists for thumbnail galleries, as tables degrade better because they remain multicolumn constructs even in non-CSS browsers and definition lists are semantically correct, too. For the examples in this chapter, I used a simple list to keep things easy and allow the thumbnails to take up as much space as is available on the screen.

390

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

You can test the effect by opening the demo exampleFakeDynamic.html. Let’s go through the functionality step by step, starting with a skeleton for the script:

fakeDynamic.js (skeleton)

fakegal = { // IDs

thumbsListID : 'thumbs', largeContainerID : 'photo',

//CSS classes closeClass : 'close', nextClass : 'next', prevClass : 'prev', hideClass : 'hide', showClass : 'show',

//Labels

closeLabel : 'close',

prevContent : '<img src="last.gif" alt="previous photo" />', nextContent : '<img src="next.gif" alt="next photo" />',

init : function(){ }, createContainer : function(){},

showPic : function(e){

},

setPic

: function(pic){

},

navPic

: function(e){

}

DOMhelp.addEvent( window,

'load', fakegal.init, false );

You need

The ID of the element containing all the thumbnails

An ID to assign to the large picture container

CSS classes for the link to remove the large picture

The links to navigate through the large pictures

Classes to show and hide elements

A label to tell the user that the link hides the large picture

Labels for the next and previous picture links

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

391

In terms of methods, you need

A method to initialize the functionality

A utility method that initially creates the image container

A method to show the picture

A method to set the picture to be shown

A method to navigate to the next or previous picture

The method setting the picture to be shown, setPic(), is necessary because both the showing method, showPic(), and the navigation method, navPic(), change the image in the container.

fakeDynamic.js

fakegal = { // IDs

thumbsListID : 'thumbs', largeContainerID : 'photo',

//CSS classes closeClass : 'close', nextClass : 'next', prevClass : 'prev', hideClass : 'hide', showClass : 'show',

//Labels

closeLabel : 'close',

prevContent : '<img src="last.gif" alt="previous photo" />', nextContent : '<img src="next.gif" alt="next photo" />', 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();

},

392

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 init() method tests whether DOM is supported and retrieves the element that contains the thumbnails. It then loops through all the links after storing the number of all links in a property called all (this is necessary later on to avoid a next link on the last image). It applies an event handler pointing to showPic() to each link in the thumbnail list and stores its index number in a new property called i before calling createContainer() to add the necessary image container element to the document.

fakeDynamic.js (continued)

createContainer : function() {

fakegal.c = document.createElement( 'div' ); fakegal.c.id = fakegal.largeContainerID;

You start the createContainer() method by creating a new DIV element, storing it in a property called c, and assigning the large image container ID to it.

fakeDynamic.js (continued)

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);

Create a new paragraph and insert a link into it with the closeLabel as text content. Assign an event handler pointing to setPic() to the link, apply the Safari fix, and add the paragraph to the container element.

fakeDynamic.js (continued)

var il = DOMhelp.createLink( '#', '' ); DOMhelp.addEvent( il, 'click', fakegal.setPic, false ); il.onclick = DOMhelp.safariClickFix; fakegal.c.appendChild( il );

Now add another—empty—link to the container with an event handler calling setPic(). This link will later surround the large picture, making it clickable and thus possible for keyboard users to get rid of it.

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

393

fakeDynamic.js (continued)

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 );

Two more links need to be added to show the previous and the next image, respectively, both with event handlers pointing to navPic().

fakeDynamic.js (continued)

fakegal.tlist.parentNode.appendChild( fakegal.c );

}

Add the new container to the parent node of the thumbnail list, and the show can begin.

fakeDynamic.js (continued)

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' ); fakegal.setPic( largePic ); DOMhelp.cancelClick( e );

},

In the event listener method showPic(), retrieve the target and determine whether it really is a link by testing the nodeName. Then store the i property that was assigned to each thumbnail link in the init() method as the value of a new property, current, of the main object to tell all other methods which picture is currently shown. Retrieve the link’s href attribute and call the setPic() method with the href as a parameter before stopping the browser from following the link via cancelClick().

394

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

fakeDynamic.js (continued)

setPic : function( pic ) { var a;

var picLink = fakegal.c.getElementsByTagName('a')[1]; picLink.innerHTML = '';

The setPic() method takes the second link inside the image container (which is the link after the closing link) and removes any content that this link might have by setting its

innerHTML property to an empty string. This is necessary to avoid more than one picture showing at a time.

fakeDynamic.js (continued)

if( typeof pic == 'string' ) { fakegal.c.className = fakegal.showClass; var i = document.createElement( 'img' ); i.setAttribute( 'src' , pic ); picLink.appendChild( i );

You compare the type of the parameter pic with string, since the method may be called with a URL as the parameter or without it. If there is a parameter that is a valid string, you add the show class to the container to show it to the user and add a new image to it with the pic parameter as its source.

fakeDynamic.js (continued)

} else { fakegal.c.className = '';

}

If there isn’t a parameter of the type string, you remove any class from the picture container, thus hiding it.

fakeDynamic.js (continued)

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 );

},

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

395

Test to see whether the current property of the main object is equal to 0, and hide the previous picture link if this is the case. Do the same with the next picture link and compare current with the number of all thumbnails (stored in all). Hide or show each link by adding or removing the hide class.

fakeDynamic.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]; fakegal.setPic( pic.getAttribute( 'href' ) ); DOMhelp.cancelClick( e );

}

}

DOMhelp.addEvent( window, 'load', fakegal.init, false );

Retrieve a reference to the link that was clicked (by getting the event target via DOMhelp’s getTarget() and ensuring the nodeName is A), and determine whether the link is the previous link by comparing this node to the one stored in the prev property. Increment or decrement current depending on which link was activated. Then call setPic() with the href attribute of the new current link and prevent the browser from following the activated link by calling cancelClick().

All that is left is to add a style sheet; the result might appear as shown in Figure 10-1.