PREVIOUS PAGE

This article was published in PC Plus issue 150 (Apr 99), and is reproduced here for information purposes only.

This is the original copy which was sent to the magazine, not the subbed version which appeared on the page. 


paul@paulspages.co.uk
www.paulspages.co.uk

Web Workshop, PC Plus issue 150 (April 99)

This is the copy for Web Workshop in issue 150 of PC Plus, including captions and images (resized to make them download-friendly). Accompanying it were HTML pages and ScreenCam movies for the magazine's cover-mounted CD-ROM (see link on previous page).

 

[BEGINS]

<head>

Web Workshop

<strap>

Image file substitution is one of the few 'Dynamic HTML' tricks you can do with the same code in both Netscape and Microsoft browsers. What's more it's powerful stuff and great fun, as Paul Stephens finds out.

<boxout for opening area>

More on the SuperCD

For example image substitution pages, JavaScript source code and ScreenCam movies, see this month's SuperCD, or my website at http://www.bigfoot.com/~pstephens.

<body>

Issue 148's Internet Explorer-only animated pushbuttons brought howls of anguish from Netscape fans, so this month I'll try to make amends by looking at an image substitution technique which works in both IE and Netscape Navigator.

'Image substitution' means replacing the .gif or .jpg file displayed by an <IMG> tag with a different file, so that the on-screen image changes - a good example of 'Dynamic HTML', and one of the all too few instances where DHTML code can be made to work with both major browsers. Substitution is often used in conjunction with mouse events, for example creating icons which appear to light up or move when the mouse pointer passes over them (the infamous 'rollover' effect) - or, indeed, pushbuttons which animate when clicked. You can also use it to create multi-image slide shows, controlled either by button clicks or timer events.

Image substitution is fairly straightforward if you're writing purely for IE4. Here are an IE4-only image tag and JavaScript routine:

<script>
function changepic(imgObj, imgFileName) {
imjObj.src = imgFileName}
</script>

<IMG ID="MyPic" SRC="PicA.gif"
   onMouseOver="changepic(MyPic, 'PicB.gif')"
   onMouseOut  ="changepic(MyPic, 'PicA.gif')">

When the mouse pointer moves over the image, its .src property is changed to 'PicB.gif', causing the browser to update the screen display. When the mouse pointer moves off the image, the image's source is changed back to 'PicA.gif'. The function changepic() accepts two parameters, an object variable pointing to an IMG object, and a string representing an image file name. It simply sets the object's .src property equal to the string value, and the job is done.

                Making the code Netscape-compatible involves quite a few changes, although the results are , at least, still compatible with IE. Netscape doesn't allow you to address IMG objects directly by name (as in 'MyPic.src='), and doesn't allow <IMG> tags to trigger onMouseOver, onMouseOut or onClick events. To solve these problems, you have to access the IMG object via the document.images collection (an array of object variables pointing to all the <IMG> tags in the document), and embed the <IMG> tag inside a hyperlink (<A>...</A>) element, which Netscape allows to trigger the necessary mouse events.

Here's the Netscape (and IE) compatible version:

<script>
function changepic(imgTagName, imgFileName) {
document.images[imgTagName].src = imgFileName}
</script>

<A HREF="dummypage.htm"
  onClick="return false"
  onMouseOver="changepic('MyPic', 'PicB.gif')"
  onMouseOut  ="changepic('MyPic', 'PicA.gif')">
<IMG NAME="MyPic" SRC="PicA.gif"></A>

The <IMG> tag no longer has an ID= attribute, but has a NAME= attribute instead, while the <A> (hyperlink) tag now contains the event-handler calls. Because the image represents the entire contents of the hyperlink, clicking on the link and the image are effectively the same thing. (A useful tip is to place the </A> hyperlink terminator hard up against the end of the <IMG> tag (for example ... SRC="PicA.gif"></A>) - if you allow any spaces or newline characters between the two tags, then Navigator inserts them, in blue-underlined format, on the screen). In this example, the hyperlink has been rendered inactive by an onClick="return false" event handler statement, but you can make it active in a variety of ways - see 'the Versatile Link' for details.

                The function changepic() works differently too; it can't address the IMG object directly, so has to accept the object's name as a string value, and use that to index the document.images collection (' document.images[imgTagName]....'). The basic principle of updating the object's .src property remains unchanged, however, as does the end result - when the .src property's value is altered, the browser updates the screen display, rendering the new image file at the <IMG> tag's location.

                As an alternative to onmouseover and onmouseout, you can trap the onmousedown and onmouseup events. This results in an image which changes when the mouse button is pressed, then reverts to its original form when the button is released. Oddly enough, Netscape supports these events directly on <IMG> tags, so in theory you don't need an enveloping <A>...</A> hyperlink. However to be completely safe with mouse down/up event handling you also need to trap the onmouseout event (see 'The Main Event' for details). Navigator doesn't, of course, support onmouseout events on <IMG> tags, so it's back to the hyperlinks.

Be prepared
The theory of image substitution is simple enough, but in practice there's another factor to consider - pre-loading the alternative image files. In the code versions above, the first time a user accesses the page from a web server and moves the mouse pointer over the image/hyperlink, there will be a delay while the browser downloads the second image file ('PicB.gif'). This is because the browser had no idea that PicB.gif was going to be needed until the first time the function changepic() was executed. After that first execution, the browser will have a copy of PicB.gif in its local disk cache, so there won't be a delay - but the copy may be dropped if the cache gets full, and then the delay will re-occur.

To avoid this problem, you can pre-download PicB.gif when the page loads, so that it's (hopefully) already in the browser's cache when changepic() is first executed (this still may not be the case if the user moves the pointer over the image immediately after the page appears). The Navigator/IE compatible way of pre-loading images involves creating dummy Image objects via a script in the document's <HEAD> section, like this:

<SCRIPT>
MyPicA = new Image(10, 10); MyPicA.src = "PicA.gif"
MyPicB  = new Image(10, 10); MyPicB.src = "PicB.gif"
</SCRIPT>

These Image objects don't appear on screen, but updating their .src properties (' MyPicA.src = "PicA.gif"') makes the browser download the specified image files and place them in its cache. Note that, as well as the object for the alternative image file (PicB.gif), there's also one for the <IMG> tag's default image (PicA.gif) - this isn't for pre-loading purposes, but so that the original image can be restored using the modified version of changepic() described below.

                In theory our original <A> tags and changepic() routine will work unmodified with these pre-loading Image objects - as long as the filenames you supply to changepic() are the same as the ones you specify for the Image object .srcs. To eliminate the risk of errors, however, we can change the code slightly, like this:

<script>
function changepic(imgTagName, imgObj) {
document.images[imgTagName].src = imgObj.src}
</script>

<A HREF="dummypage.htm"
  onClick="return false"
  onMouseOver="changepic('MyPic', MyPicB)"
  onMouseOut  ="changepic('MyPic', MyPicA)">
<IMG NAME="MyPic" SRC="PicA.gif">
</A>

The second parameter to changepic() is now an object variable (imjObj), and must point to one of the 'new Image' objects created in the <HEAD> section (so the parameter value is an object name, e.g. MyPicB , not a string in quotes, such as "PicB.gif"). Instead of updating the <IMG> tag's .src with a string value, the function now sets it equal to the .src property of the specified Image object (imgObj.src). You only have to type the image file once in the source code, so there's no danger of a mismatch between function call and object declaration.

                This system is automatically backward-compatible with browsers that don't support JavaScript, since they'll simply ignore the <A> tag's event handlers, and the initial images will remain static. You can take this a stage further, and protect against browsers which support JavaScript but don't support the document.images collection, by adding an if() statement to changepic(), like this:

<script>
function changepic(imgTagName, imgObj) {
  if (document.images) {
     document.images[imgTagName].src = imgObj.src}
}
</script>

Now the function will only attempt to manipulate the image tag's .src property if the document.images collection exists.

A wide choice.
Image substitution is clever stuff, but what can you actually use it for? The answer is a surprisingly wide range of things.

                At its simplest, image substitution can produce two-state animations, such as 'winking' eyes and 'illuminating' light bulbs, which liven up your page as the mouse pointer rolls over them. The visual effect depends entirely on the contents of the image files - tips are not to make objects appear to move too far between frames (it doesn't look realistic), and to use transparent backgrounds (or the same colour as the page background) to make objects appear to 'float' over the page (drop shadows enhance this effect).

                An animated pushbutton is really just an animated image contained inside a hyperlink which has an onclick= event handler or an active HREF= attribute (see 'The Versatile Link' for details). A popular technique is to make the default button image 'flat', then substitute a 3D-effect popup version when the onmouseover event occurs (this is the effect used on the toolbars in Windows applications such as Microsoft Office, so you're in good company). If you want to go the whole hog you can add a third image, of the button indented, and show that in response to the onmousedown event, reverting to the popup version in response to onmouseup or onclick (see 'The Main Event' for details of the sequence in which mouse events are fired).

                With the addition of a few JavaScript routines, you can create 'checkbox' buttons which toggle between on and off ('lit' and 'unlit') states, as well as radio button groups, where selecting one button deselects whichever one was previous 'lit'. I've written JavaScript functions for these buttons and - terrible confession time - I think that, although not quite as flexible, they're a better bet than the IE4-only buttons in issue 148, partly because they're easier to set up, but mainly because they're cross-browser compatible. That'll teach me to take too IE-centric a view of things!

                Examples of rollovers buttons and slide shows, as well as those button-handling JavaScript libraries and explanatory ScreenCams, are all on this month's SuperCD and my web site. Until next month, happy authoring.

<END OF BODY COPY>

<CAPTION FOR MAIN DIAGRAM - ROUGH LAYOUT IN " web workshop 150 diagram rough.gif",  SUPPLIED WITH THIS COPY>

An image rollover in action. The <IMG> tag places the default image file "kiss1.gif" on screen when the page is first loaded. The <IMG> is contained inside a hyperlink (<A>...</A>) container, and when the mouse pointer moves over the container, the <A> tag's onmouseover event-handler code substitutes the image file "kiss2.gif" on screen. When the pointer moves off, the <A> tag's onmouseout handler restores the default image file "kiss1.gif2.

<OTHER VISUALS - DROP FROM LAST IF OVER>

<3-PIC MONTAGE - WW1502A.GIF, WW1502B.GIF & WW1502C.GIF>

 

 

A transparent (or page-equivalent) background and drop shadow makes an image 'float' above the surface. The neon-light effect is simply a soft-edged drop shadow in colour rather than black.

 

<WW1503.GIF>

Yes, you can even create Netscape-compatible animated pushbuttons, which - to my shame - I have to admit are more useful than the IE4-only ones in issue 148. The necessary JavaScript functions are on the SuperCD.

 

<WW1504.GIF>

Image substitution isn't just about rollovers and buttons - this slideshow page uses event timers to auto-advance the images in the viewing panel. You can see it in action in the Altered Images Guide on the SuperCD.

<BOXOUT>

The Versatile Link.

<BOXOUT PIC - WW1501.GIF>

<CAPTION>

If Netscape supported mouse events in <IMG> tags, then this hyperlink wouldn't be necessary - but at least it makes page-loading buttons easy to create.

<box body>

It seems a chore having to wrap <IMG> tags in hyperlink (<A>...</A> containers just so that Navigator can trigger mouse events (see main text). However the hyperlink is a versatile tag, able to load a new page into the browser, call JavaScript routines, or just do nothing in response to mouse clicks. It can also trigger all five major mouse events (onclick, onmousedown, onmouseup, onmouseover and onmouseout) in Netscape browsers, so it's easily capable of earning its living.

                At its simplest, you might just want the visual effect of an image animation, for example a bulb that 'lights up' when the mouse rolls over it. In this case you can disable the hyperlink altogether, like this:

<A HREF="nojs.htm"
     onclick="return false"
     onmouseover="lightup()"
     onmouseout="lightoff()">

The onclick handler will make JavaScript-enabled browsers ignore mouse clicks on the link. You can deal with non-JavaScript browsers (which will execute the link) by making the link's HREF= target ('nojs.htm' in this case) a 'sorry, you need JavaScript to view this site' page.

                Alternatively, you can make the <A> tag a functional hyperlink - just omit the onclick= handler, point its HREF= attribute at a valid URL, and it will work as standard (remember to add a TARGET= attribute if you want the page loaded into a different frame). You can still make the image animate as the pointer moves over it, by trapping the hyperlink's onmouseover event (don't forget to 'un-animate' it again in response to the hyperlink's onmouseout event, in case the user moves the pointer back off without clicking). For a really smart effect on pushbutton images, you can use the hyperlink's onmousedown event to call a function which substitutes a third image, simulating the button being pushed 'in'.

                Here's an example of functional, animated-image hyperlink tag:

<A HREF="newpage.htm"
     onmouseover="lightup()"
     onmouseout="lightoff()"
     onmousedown="pressin()">

Instead of loading a new page, you may want a click on the link/image to do something on the current page, such as advancing a slideshow. In this case reinstate the onclick= event handler, and make it a composite statement, like this:

<A HREF="nojs.htm"
   onmouseover="lightup()"
   onmouseout="lightoff()"
   onmousedown="pressin()"
   onclick="advance_show();return false">

The onclick handler now contains two JavaScript statements, separated by the semi-colon. When the user clicks, the browser first executes the function 'advance_show()', then the statement 'return false', which disables the hyperlink. You can call image substitution routines from the hyperlink's other mouse-handling events, as in the 'normal' hyperlink example above. Action routine calls (such as 'advance_show()" aren't restricted to the onclick event, so if you want to advance your slideshow in response to the pointer moving over the link, you can say:

<A HREF="nojs.htm"
     onmouseover="lightup();advance_show()"
     onmouseout="lightoff()"
     onclick=" return false">

If you really want to work your hyperlink hard, you can combine an action routine call and a normal hyperlink action, like this:

<A HREF="newpage.htm"
     onmouseover etc...
     onclick="say_goodbye()">

When the link is clicked, the browser will execute the function "say_goodbye()", then, because there's no "return false" in the onclick= string, it will load the new page.

                Working examples of all these hyperlink techniques are on this month's SuperCD and my web site.

<boxout - no pic>

The Main Events

These are the key mouse events which you can trap on <A> (hyperlink) tags in Netscape Navigator. They also work in Internet Explorer, but in IE you can trap them directly on <IMG> tags as well. Navigator only supports onmousedown and onmouseup on <IMGs>.

Clicking on a hyperlink generates an onclick event, as well as onmousedown and onmouseup. Reading from the top down, the table shows the sequence in which the events are triggered, assuming that the user moves the pointer over an object, clicks (presses and releases the mouse button) while on it, then moves the pointer away. 

Step No.

Event

Meaning

1.

onmouseover

Mouse pointer moved over object

2.

onmousedown

Left-hand mouse button pressed

3.

onmouseup

Left-hand mouse button released

4.

onclick

Left-hand mouse button press and release (happens as well as previous two events)

5.

onmouseout

Mouse pointer moved off object.

If, however, the user clicks down on an object, holds the button in place, then drags the pointer off the object before releasing the button, the onmouseup and onclick events aren't fired. The sequence is now: 

onmouseover

onmousedown

onmouseout

This click-drag sequence often occurs in real life, so it's important to anticipate it. Here are some tips:

1. If you're animating an image via onmousedown and onmouseup event handlers (for example a button that lights up when clicked and stays lit until the mouse is released), then add an onmouseout handler with the same action as onmouseup, for example:

  <A HREF="nopage.htm"
  onclick="return false"
  onmousedown="lightup()"
  onmouseup = "lightoff()"
  onmouseout = "lightoff()">

2. If you want a button or other clickable image's action to be 'abandonable', so that, having clicked down on it, the user can drag the pointer off before releasing the button, then attach your function call to its onclick event.

3. If you want a button or other image's action to be non-abandonable (once the user has clicked down they're committed), then attach your function call to its onmousedown event.

4. If you want a button to light up when clicked, and to stay lit until its action has completed (or been abadoned), then call its 'un-light' routine from the end of its onclick event handler string, and also from its onmouseout event, like this:

<A HREF="nopage.htm"
 onmousedown="lightup()"
 onclick="do_something();lightoff();return false"
 onmouseout = "lightoff()">

<END OF BOXOUT>

[ENDS]

(C) Paul Stephens 1999. All rights reserved.