reptile7's JavaScript blog
Monday, September 12, 2011
The Krewe of Image
Blog Entry #226

For the next several entries we will work through a series of animation-related tutorials in the HTML Goodies Beyond HTML : JavaScript sector. Today we'll get into "Web Developer Tutorial: Build Your Own Image Scrollbar", which is yet another Curtis Dicken production. Perhaps you are wondering, "Just what are image scrollbars?" I myself had never heard of them. The author describes an image scrollbar thusly:
It's a collection of thumbnail size images, usually in a table, with buttons on either side of the images that allow you to scroll through the collection. It's a great space saver when Web site real estate is hard to come by.
Associated with the "Build Your Own Image Scrollbar" tutorial is a strange demo that is nonfunctional with the browsers on my computer - several people in the tutorial comment thread also report having trouble with it - and that we may devote a separate post to. When working on the desktop and with the scrollbar images in hand, however, I find that the tutorial code works fine as far as it goes.


The tutorial works with a series of ten images with ordinalized file names: image1.png, image2.png, etc. The image file names are organized as an imagePath array:

var imagePath = new Array(10);
imagePath[0] = "image1.png";
imagePath[1] = "image2.png";
imagePath[9] = "image10.png";

The last and first imagePath index numbers are respectively assigned to maxIndex and minIndex variables that will later be used to bound the scrolling process.

var maxIndex = 9;
var minIndex = 0;

The image series is "scrolled" forward and backward through a row of four 100px-by-100px ("thumbnail") img placeholders that are initially loaded with the image1.png, image2.png, image3.png, and image4.png images and whose ids are scrollThumb1, scrollThumb2, scrollThumb3, and scrollThumb4, respectively.

<img id="scrollThumb1" height="100" src="image1.png" style="border-right:1px solid; border-top:1px solid; border-left:1px solid; border-bottom:1px solid;" width="100" />

(What's with the lengthy border specification? Yes, we should be using a border:1px solid; style declaration instead.)

An imageIndexFirst variable is used to index the image held by the scrollThumb1 placeholder and an imageIndexLast variable is used to index the image held by the scrollThumb4 placeholder; imageIndexFirst and imageIndexLast are initialized to 0 and 3, respectively.

var imageIndexFirst = 0;
var imageIndexLast = 3;

The author puts the img placeholders in separate cells in the second row of a

<table border="0" cellpadding="5" cellspacing="0" width="700px"> ... </table>

table. (In HTML, width attribute values do not include unit identifiers; the table's width attribute should be replaced by a width:700px; style declaration.) The table's first row contains a 400px-by-400px img placeholder loaded with the image1.png image; at no point in the code is the first-row image replaced by another image.

Forward scrolling is effected via a Next >> td element "button" to the right of the placeholders whereas backward scrolling is effected via a corresponding << Previous td element button to the left of the placeholders.

<td id="scrollPreviousCell" style="color:silver;" onmouseover="scrollPrevious( );" onmouseout="scrollStop( );"> << Previous</td>
...img placeholder cells...
<td id="scrollNextCell" style="color:black;" onmouseover="scrollNext( );" onmouseout="scrollStop( );"> Next >></td>

If the aforementioned demo worked as advertised, here's what you'd see initially:

[The pre-scroll tutorial demo table]

Scroll it

When the page loads we are ready to begin forward scrolling. The Next >> button label color is black, signaling that the Next >> button is active; the << Previous button label color is silver ('grayed out'), signaling that the << Previous button is inactive. (Nothing happens if you 'push' the << Previous button because imageIndexFirst and minIndex are equal - see the else clause discussion at the end of the post.) Forward scrolling is initiated by mousing over the Next >> button, which triggers a scrollNext( ) function:

var continueScroll = 0;
function scrollNext( ) {
    continueScroll = 1;
    scrollImages("up"); }

A continueScroll variable serves as an on/off switch for the image scrollbar; continueScroll is initialized to 0 (scrolling is off) and is toggled to 1 (scrolling is on) by the scrollNext( ) function's first statement. The scrollNext( ) function subsequently calls a scrollImages( ) function and passes thereto an up string, meaning that the thumbnail placeholders will be sequentially loaded with imagePath images in order of increasing imagePath index number, thereby giving a right-to-left scroll - we'll roll out a demo in due course.

When the scrollImages( ) function is called, the up string is given a scrollDirection identifier. The first scrollImages( ) statement declares but does not initialize a currentIndex variable; currentIndex will be a sort of roving index for the images that are loaded into the thumbnail placeholders.

function scrollImages(scrollDirection) {
    var currentIndex;

The remainder of the scrollImages( ) function comprises one big if...else statement whose if and else clauses respectively handle forward and backward scrolling. The if clause first tests if scrollDirection is equal to up: check. It next tests if imageIndexLast (3) is not equal to maxIndex (9): check.

if (scrollDirection == "up") {
    // Only do work if we are not to the last image
    if (imageIndexLast != maxIndex) {

The if clause then switches the << Previous button label color to black, even though we haven't done any scrolling yet, and unnecessarily resets the Next >> button label color to black:

document.getElementById("scrollPreviousCell").setAttribute("style", "color:black;");
document.getElementById("scrollNextCell").setAttribute("style", "color:black;");

The setAttribute( ) method of the DOM Element interface goes back to the DOM Level 1 Core; its support began on the Microsoft side with IE 4 and on the Netscape side with Netscape 6. However, I find that setAttribute( ) has a "doesn't throw an error but isn't implemented in practice" status with IE 5.2.3 for Mac OS X. Users without setAttribute( ) support can be brought into the loop by trading in the first setAttribute( ) command for a corresponding style.color assignment

document.getElementById("scrollPreviousCell").style.color = "black";

and then ditching the unneeded second setAttribute( ) command.

Subsequently the if clause increments both imageIndexLast and imageIndexFirst; these operations are followed by a conditional that would switch the Next >> button label color to silver if imageIndexLast and maxIndex were equal (the placement of this statement also seems odd to me - I would have formulated it as an else clause to complement the if (imageIndexLast != maxIndex) { ... } clause we are currently dissecting):

imageIndexLast = imageIndexLast + 1;
imageIndexFirst = imageIndexFirst + 1;
if (imageIndexLast == maxIndex) {
    document.getElementById("scrollNextCell").setAttribute("style", "color:silver;"); }

We assign the imageIndexLast value (4) to currentIndex, and then call a changeImage( ) function and pass thereto scrollThumb4 and imagePath[currentIndex] (image5.png).

currentIndex = imageIndexLast;
changeImage("scrollThumb4", imagePath[currentIndex]);

Upon calling the changeImage( ) function

function changeImage(ImageToChange, MyImagePath) {
    document.getElementById(ImageToChange).setAttribute("src", MyImagePath); }

scrollThumb4 and imagePath[currentIndex] are respectively given ImageToChange and MyImagePath identifiers; these arguments are plugged into a command that sets the ImageToChange's src property to MyImagePath, that is, the command changes the scrollThumb4 placeholder's image from image4.png to image5.png.

currentIndex is decremented to 3; after a 25-millisecond delay, changeImage( ) is re-called to change the scrollThumb3 placeholder's image from image3.png to image4.png.

currentIndex = imageIndexLast - 1;
window.setTimeout("changeImage('scrollThumb3', imagePath[" + currentIndex + "]);", 25);

currentIndex is decremented to 2; after a 50-millisecond delay, changeImage( ) is re-called to change the scrollThumb2 placeholder's image from image2.png to image3.png. currentIndex is decremented to 1; after a 75-millisecond delay, changeImage( ) is re-called to change the scrollThumb1 placeholder's image from image1.png to image2.png.

currentIndex = imageIndexLast - 2;
window.setTimeout("changeImage('scrollThumb2', imagePath[" + currentIndex + "]);", 50);
currentIndex = imageIndexLast - 3;
window.setTimeout("changeImage('scrollThumb1', imagePath[" + currentIndex + "]);", 75);

The cumulative visual effect of the preceding currentIndex/changeImage( ) commands is one of right-to-left image movement (cf. the table below):
• The image1.png image is now 'off-screen', having scrolled beyond the left edge of the bar.
• The image2.png, image3.png, and image4.png images have each scrolled leftward by one position.
• The image5.png image has now scrolled into view at the right side of the bar.

The changeImage( ) call timeouts would seem to be off by a factor of 10:
We also use setTimeout( ) when we call our changeImage( ) function. This causes a specific delay before the function is called. We stagger the delays by a quarter of a second (25 milliseconds) so that we can create a pseudo-animation effect.
That would be 250 milliseconds, bro.

The if clause concludes by calling a scrollAgain( ) function after a 1-second delay:

window.setTimeout("scrollAgain('" + scrollDirection + "');", 1000);

The scrollAgain( ) function checks if continueScroll is still equal to 1 (it is) and then re-calls the scrollImages( ) function; scrollDirection is passed to and from the scrollAgain( ) function so as to continue up/forward scrolling.

function scrollAgain(scrollDirection) {
    if (continueScroll == 1) {
        scrollImages(scrollDirection); } }

In our second run through the scrollImages( ) function, imageIndexLast is incremented to 5 and image6.png, image5.png, image4.png, and image3.png are respectively loaded into the thumbnail placeholders; scrollImages( ) is re-called, imageIndexLast is incremented to 6, and image7.png, image6.png, image5.png, and image4.png are respectively loaded into the thumbnail placeholders; and so on until imageIndexLast hits 9 (maxIndex), at which point we have run out of imagePath images.

imageIndexLastscrollThumb1 imagescrollThumb2 imagescrollThumb3 imagescrollThumb4 image

We can stop the scrolling action at any time by mousing out from the Next >> button: doing so calls a scrollStop( ) function

function scrollStop( ) {
    continueScroll = 0; }

that toggles continueScroll to 0, thereby shutting down the scrollImages( ) call in the scrollAgain( ) function.

The scrollImages( ) else clause runs it all in reverse for backward scrolling. Mousing over the << Previous button calls a scrollPrevious( ) function

function scrollPrevious( ) {
    continueScroll = 1;
    scrollImages("down"); }

that switches continueScroll to 1, calls the scrollImages( ) function, and feeds to scrollImages( ) a down argument, whose assignment to scrollDirection effectively directs control to the else clause. The else code is conditioned by an if (imageIndexFirst != minIndex) { ... } statement that is inoperative when the page loads (recall that imageIndexFirst starts out at 0) but is executed if we've done any forward scrolling (recall that forward scrolling increments imageIndexFirst as well as imageIndexLast).

We'll put the tutorial demo under the microscope and (depending on how long that takes) perhaps also retool the code a bit in the following entry.


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)