Previous Section:
Programming the Link Element
Programming the Image and Map Elements
Images and image maps are fully programmable in Internet Explorer 4.0.
You can now change the SRC attribute and size of an image and modify, add,
and remove Area elements from an image map. The object model also allows
new images to be asynchronously downloaded in the background while the
user interacts with the page. This section presents techniques for
downloading images and for manipulating the Image element and associated image maps.
Image Animation
One common technique for animating images is to change the image as
the mouse enters and exits the element. In Internet Explorer 4.0, this task
is trivial--you use the onmouseover and
onmouseout events on the Image element itself:
<IMG SRC="start.gif"
ONMOUSEOVER="this.src = `over.gif';"
ONMOUSEOUT="this.src = `start.gif';">
Netscape Navigator will ignore this code because it does not
currently support onmouseover and
onmouseout events on the Image element.
Netscape Navigator does support these events on the Anchor element, however.
Therefore, with a little forethought it is possible to re-create the preceding
scenario in a more compatible way. By wrapping the Image element in an Anchor element, both Netscape Navigator 3.0 or later and Internet Explorer 4.0
will properly change the image:
<A HREF=""
ONMOUSEOVER="document.myImage.src = `over.gif';"
ONMOUSEOUT="document.myImage.src = `start.gif';">
<IMG BORDER=0 NAME="myImage" SRC="start.gif">
</A>
The BORDER=0 attribute must be added so that the default anchor
border is not drawn around the image. And while this technique does provide
similar support in both Netscape Navigator and Internet Explorer, there is still
one key difference. Because no size is supplied to the image, in Internet Explorer the container of the image is automatically resized to match the image and the surrounding contents are reflowed. In Netscape
Navigator, the size of the image is fixed when the first image is loaded, so the next
image is scaled to fit. To work around this discrepancy, either ensure that
the images are the same size or provide
width and height attributes on the
Image element.
While the preceding code works, a noticeable delay might occur when
the second image is initially downloaded. Dynamic HTML supports the ability
to preload an image behind the page so that it is immediately available for use.
Image Sequencing
Run Sample
Timer events can be used instead of user-generated events to change an
image. Dynamic HTML makes it simple to create an image sequencer that
rotates images after a specified amount of time. Images can be preloaded using
a special image constructor, and the Image element's SRC attribute can be
dynamically changed.
The following code shows the application of this technique, a
client-side billboard that cycles through images after a specified amount of time.
This scenario uses unrecognized elements to define the list of advertisements.
The advantages of this model are that new ads can be added and outdated ads
can be removed without having to modify any code. Another technique used in
this example is to preload the images before assigning the SRC attribute to
ensure a smooth transition from image to image. An error recovery mechanism
is included to skip an image if it fails to download.
<HTML>
<HEAD>
<TITLE>Ad Sequencing</TITLE>
<!-- More ads can be added simply by extending this list. -->
<ADLIST src="ad1.gif" duration=3000>
<ADLIST src="ad2.gif" duration=5000>
<ADLIST src="ad3.gif">
<ADLIST src="ad4.gif" duration=1000>
<SCRIPT LANGUAGE="JavaScript">
var adSet = document.all.tags("ADLIST");
adSet.current = 0;
var nextImage = document.createElement("IMG");
function preLoad() {
// Get next image.
// If an error occurs, skip to the next image.
/* Always set up image event handlers before assigning the
SRC attribute to ensure that no events are missed. */
nextImage.onerror = preLoad;
nextImage.src =
adSet[adSet.current].getAttribute("src");
// The duration attribute specifies how long the image is
// displayed.
nextImage.duration =
adSet[adSet.current].getAttribute("duration");
if (null == nextImage.duration) // If not specified, use
nextImage.duration = 2000; // default 2 seconds.
if (++adSet.current == adSet.length)
adSet.current = 0; // Start over.
}
function skipImage() {
// Check whether next image has been downloaded.
if (nextImage.complete) {
document.all.ad.src = nextImage.src;
var duration = nextImage.duration;
preLoad();
window.tm = setTimeout(`skipImage()', duration);
}
else // Quickly iterate until image is available.
window.tm = setTimeout(`skipImage()', 10);
}
preLoad();
</SCRIPT>
</HEAD>
<BODY ONLOAD="window.tm = setTimeout(`skipImage()', 1);"
ONUNLOAD="clearTimeout(window.tm);">
<IMG ID="ad" SRC="ad4.gif" STYLE="border:2px solid navy">
</BODY>
</HTML>
Internet Explorer 4.0 also supports the construction of new images
for background downloading using the new operator in addition to the
createElement method. This operator is supported for compatibility with Netscape
Navigator's JavaScript implementation. The
new operator is a language-dependent technique for creating new elements. For example, in the preceding code, the line
nextImage = document.createElement("IMG");
can also be written as
nextImage = new Image();
However, because Netscape Navigator does not expose custom elements
to scripts, the code for sequencing advertisements requires further
modifications in order to run in Netscape Navigator: the information about the ad
graphics needs to be stored by the script, most likely in an array, rather than in
custom AdList elements.
Image Maps
Image maps specify different click regions on an image. The most common
use for image maps is to create visual navigation maps. When the user clicks in
a particular area of the image, the default action is to navigate the user to
a specified page. Using the event model, you can override the default action
with an alternative action.
Defining an Image Map
HTML provides two types of image maps: server-side and client-side. A
server-side image map is specified simply by adding an ISMAP attribute to the
image and creating an image map file on the server. When the user clicks on
the image, the xy-coordinates are submitted to the server. The server-side
image map has two inherent disadvantages: it generally requires a server
round-trip, and it is not easily accessible because the click regions are not known to
the browser or to scripts.
Client-side image maps use the Map element and have the advantage
of not requiring a round-trip to the server. They also allow browsers to
intelligently map and outline the click regions of the image. The Map element contains
a set of Area elements that define the coordinates for each click region.
Map elements must be named in order to be associated with an
image. Once the Map element is named, any number of images can be associated
with it through the images' USEMAP attribute. The value for USEMAP must
be specified as a link reference. For example, the following code associates
an image with an image map named diagram:
<IMG SRC="diagram.gif" USEMAP="#diagram">
Client-side image maps and their syntax are demonstrated in the
following examples. However, the complete syntax for defining a server-side or
client-side image map is beyond the scope of this book. For details about image map syntax, refer to an HTML reference book or the Microsoft Web
site (www.microsoft.com).
Image Maps and Events
You can place an image map anywhere in the document, independent of
the image the map is associated with. Because multiple images can share a
single image map, the Dynamic HTML object model maintains a special
relationship between the image and its image map when firing events.
When an event is fired on an image map, the Area element receives
the event, followed by the Map element, followed by the Image element the
user clicked on. After the image receives the event, the event continues to bubble
up through the image's parent elements. Thus, a single image map and events
can be shared, or depending on the circumstances, the image itself can
override or add its own behavior to the image map. Elements that contain the
image map in the HTML source may never receive the events that originate in
the image map.
Accessing the Image Map
An Image element's useMap property contains the name of the associated
image map, prefixed with a # character. By removing the leading # character
from the useMap property, you can access the image map. The
useMap property is read/write, so it allows image maps to be dynamically associated with the
image. The following code demonstrates a simple function for obtaining
the associated image map from an Image element:
function getMap(elImage) {
// Be sure that a map is specified for the image.
if (null != elImage.useMap) {
// Remove the leading # from the bookmark.
var strMap = elImage.useMap.substring(1);
// Return the element with the specified name.
return document.all[strMap];
}
else
return null;
}
A useful application of dynamically changing an image map is to
provide a different level of granularity in a complex image or geographic map.
Figure 9-4 shows how a set of items--in this case, cities and states--can be made
more manageable by letting the user first define a subset of items of interest.
This filtering technique becomes even more powerful when used to
distinguish between multiple overlapping regions.
Because the cities in this image overlap the states, the user might find
it difficult to make a selection. By allowing the user to decide between cities
and states, selection becomes much simpler. This filtering is easily implemented
by toggling between two image maps for the image, depending on the user's
selection, as shown in the following code.
Figure 9-4.
An image that can use two different image maps.
Run Sample
<HTML>
<HEAD>
<TITLE>Switching Image Maps</TITLE>
<SCRIPT LANGUAGE="JavaScript">
function setMap(mapName) {
document.all.mapImage.useMap = mapName;
}
</SCRIPT>
</HEAD>
<BODY>
<P>Select From:<BR>
<INPUT TYPE=RADIO NAME="feature" ID="States" Value="#States"
ONCLICK="setMap(this.value);" CHECKED>
<LABEL FOR="States">States</LABEL><BR>
<INPUT TYPE=RADIO NAME="feature" ID="Cities" Value="#Cities"
ONCLICK="setMap(this.value);">
<LABEL FOR="Cities">Cities</LABEL></P>
<P><IMG ID="mapImage" SRC="places.gif" BORDER=0
WIDTH=197 HEIGHT=448 USEMAP="#States"></P>
<MAP NAME="Cities">
<AREA SHAPE="POLYGON" HREF="la.htm"
COORDS="108, 408, 164, 407, 165, 388, 111, 387,
109, 361, 86, 361, 73, 394, 94, 411">
<AREA SHAPE="POLYGON" HREF="sanfran.htm"
COORDS="12, 301, 58, 275, 75, 305, 80, 301, 87, 314,
92, 326, 119, 329, 121, 340, 45, 341, 44, 328,
9, 328">
<AREA SHAPE="POLYGON" HREF="portland.htm"
COORDS="34, 120, 47, 120, 49, 115, 68, 115, 69, 123,
86, 127, 86, 131, 140, 131, 137, 144, 86, 145,
91, 162, 22, 160, 22, 148, 26, 144">
<AREA SHAPE="POLYGON" HREF="seattle.htm"
COORDS="73, 86, 93, 84, 92, 73, 125, 73, 123, 59,
92, 57, 87, 43, 93, 22, 82, 2, 71, 21, 79, 45">
</MAP>
<MAP NAME="States">
<AREA SHAPE="POLYGON" HREF="california.htm"
COORDS="14, 204, 18, 200, 83, 209, 79, 278, 166, 386,
171, 403, 167, 409, 166, 419, 163, 423, 164, 430,
166, 436, 161, 439, 115, 438, 112, 433, 110, 420,
97, 409, 92, 401, 82, 399, 77, 392, 56, 385, 54
369, 46, 357, 46, 352, 34, 338, 39, 327, 35, 322,
32, 309, 34, 297, 25, 297, 24, 288, 14, 273, 15,
255, 9, 235, 12, 224, 12, 221, 16, 216">
<AREA SHAPE="POLYGON" HREF="oregon.htm"
COORDS="16, 199, 136, 216, 140, 178, 143, 171,
138, 164, 153, 132, 147, 122, 103, 120, 80, 123,
72, 121, 55, 121, 51, 109, 37, 105, 22, 163,
23, 166, 18, 173, 14, 189">
<AREA SHAPE="POLYGON" HREF="washington.htm"
COORDS="33, 50, 64, 64, 57, 74, 57, 86, 63, 81,
70, 65, 66, 41, 152, 55, 147, 123, 100, 119,
86, 124, 74, 120, 56, 119, 51, 108, 40, 104,
36, 99, 43, 93, 37, 87, 41, 84, 36, 80">
</MAP>
</BODY>
</HTML>
NOTE
The coordinate lists in the Area elements cannot be
broken onto multiple lines or the code will not run correctly. The
lists are broken in the preceding code in order to fit them on the page;
artificial line break symbols (
)
indicate line breaks that
shouldn't appear in the actual code.
Accessing Area Elements
Dynamic HTML exposes the Area elements through the following collections:
- The links collection on the document
- The all collection on the document
- The areas collection on the Map element containing the Area elements
Scripts can access the attributes of the Area element in any of these three
ways in order to dynamically modify them. The Area element has an HREF
attribute that contains a URL, and it exposes the same properties containing parts
of that URL that the location and
anchor objects expose. The areas collection
provides the extra functionality of allowing new Area elements to be added
and removed from the image map.
Dynamically modifying the coordinates and shapes within an image
map is supported, but it is usually easier and more maintainable to define
multiple image maps in the document and switch between them. The exception is
when you can calculate the new click regions from the old by a simple
transformation. For example, if an image can be scaled, it is easier to scale both the image and the image map. If a zoom function is supported on an image,
any associated image map also needs to be zoomed with the imageRun Sample:
<HTML>
<HEAD>
<TITLE>Dynamically Scaling Image Maps</TITLE>
<SCRIPT LANGUAGE="JavaScript">
function getMap(elImage) {
// Be sure that a map is specified for the image.
if (null != elImage.useMap) {
// Remove the leading # from the bookmark.
var strMap = elImage.useMap.substring(1);
// Return the element with the specified name.
return document.all[strMap];
}
else
return null;
}
function zoomImage(elImage, amount) {
// Expand the image the specified amount.
var elMap = getMap(elImage);
elImage.width *= amount;
elImage.height *= amount;
// If an image map is available, scale it too.
if (null != elMap) {
for (var intLoop = 0; intLoop < elMap.areas.length;
intLoop++) {
var elArea = elMap.areas[intLoop];
// Break the coordinates string into an array.
var coords = elArea.coords.split(",");
var scaledCoords = "";
// Rebuild the new scaled string.
for (coord in coords) {
scaledCoords += (coords[coord] * amount) + ",";
}
// Put the scaled coordinates back into the map.
elArea.coords = scaledCoords;
}
}
}
function swapButtons(b1, b2) {
// Swap the enabled/disabled buttons.
document.all[b1].disabled = true;
document.all[b2].disabled = false;
}
</SCRIPT>
</HEAD>
<BODY>
<P>
<INPUT TYPE=BUTTON VALUE="Zoom In"
ONCLICK="zoomImage(document.all.img1, 2);
swapButtons(`zoomin', 'zoomout');"
ID="zoomin">
<INPUT TYPE=BUTTON VALUE="Zoom Out"
ONCLICK="zoomImage(document.all.img1, .5);
swapButtons(`zoomout', 'zoomin');"
ID="zoomout" DISABLED>
</P>
<P>
<IMG SRC="img001.gif" WIDTH=200 HEIGHT=200
ID="img1" USEMAP="#map1">
<MAP NAME="map1">
<AREA SHAPE="POLYGON"
COORDS="92, 140, 126, 114, 155, 139, 124, 163"
HREF="home.htm">
<AREA SHAPE="CIRCLE" COORDS="30, 105, 30" HREF="cool.htm">
<AREA SHAPE="RECT" COORDS="62, 28, 200, 79"
HREF="dhtml.htm">
</MAP>
</P>
</BODY>
</HTML>
Adding and Removing Area Elements
Using the areas collection, Dynamic HTML supports the ability to
dynamically add and remove Area elements from an image map. The technique for
creating a new Area element is the same as for creating a new image. The
primary difference is that this new Area element can be added directly to an
existing map's areas collection, whereas a new image object cannot be added to
the document.
The areas collection exposes add and
remove methods. The add method takes an Area element created with the
createElement method and adds it to the areas
collection. The remove method is used to remove an existing Area
element from the image map. The following example is a simple image map
editor written entirely in HTML:
<HTML>
<HEAD>
<TITLE>Image Map Editor</TITLE>
<SCRIPT LANGUAGE="JavaScript">
var curFocus = null;
function areaFocus() {
// Track the last Area element selected.
if ("AREA" == event.srcElement.tagName)
curFocus = event.srcElement;
}
function removeArea() {
// Remove an Area element.
var coll = document.all.dynaMap.areas;
if (null != curFocus) // Make sure one is selected.
// Loop over Area elements and find the one selected.
for (var intLoop = 0; intLoop < coll.length; intLoop++)
if (curFocus == coll[intLoop]) {
document.all.dynaMap.areas.remove(intLoop);
return;
}
alert("No Area element is selected.");
}
function addArea(f) {
/* Be sure that coordinates are specified. This code does
not perform any extra validation for the coordinates. */
if ("" != f.coordinates.value) {
var elArea = document.createElement("AREA");
elArea.coords = f.coordinates.value;
// Determine shape selected.
for (var intLoop = 0; intLoop < f.shape.length;
intLoop++)
if (f.shape[intLoop].checked)
elArea.shape = f.shape[intLoop].id;
document.all.dynaMap.areas.add(elArea);
}
else
alert("You need to enter a Coords value.");
event.returnValue = false;
}
</SCRIPT>
</HEAD>
<BODY>
<H1>Image Map Editor</H1>
<H2>Select a Shape</H2>
<FORM NAME="area">
<!-- The ID is used to determine the shape attribute. -->
<P>
<INPUT TYPE=RADIO NAME="shape" ID="rect" CHECKED>
<LABEL FOR="rect">Rect</LABEL>
<BR>
<INPUT TYPE=RADIO NAME="shape" ID="polygon">
<LABEL FOR="polygon">Polygon</LABEL>
<BR>
<INPUT TYPE=RADIO NAME="shape" ID="circle">
<LABEL FOR="circle">Circle</LABEL>
</P>
<P>
<LABEL FOR="coords">Coords</LABEL>
<INPUT TYPE=TEXT ID="coords" NAME="coordinates">
</P>
<P>
<INPUT TYPE=SUBMIT VALUE="Add Area"
ONCLICK="addArea(this.form)">
<INPUT TYPE=BUTTON VALUE="Remove Area"
ONCLICK="removeArea()">
</P>
</FORM>
<IMG SRC="img001.gif" WIDTH=200 HEIGHT=200 USEMAP="#dynaMap">
<MAP NAME="dynaMap" ONCLICK="areaFocus()">
</MAP>
</BODY>
</HTML>
Next Section:
Programming the Marquee Element