Image Map Information in JavaScript
It's also possible to place all of your image map information in JavaScript, with none of it being HTML. It turns out this creates much longer code, but it also allows you to alter an individual area's coordinates, href, target, and shape on the fly.
Since all of the image map information lives in the JavaScript, our HTML is pretty simple, as shown in Example 3-6.
Did you notice that the map tag has both a name and an id? It seems redundant, and in fact IE works fine with only id="submenuMap", but Netscape 6 also needs the name="submenuMap" in order to work.
Example 3-6 Submenu layer
<div id="submenu"> <img src="images/white_block.gif" name="submenuImg" border="0" usemap="#subMenuMap"> <map name="submenuMap" id="submenuMap"></map> </div>
To get this to work, we'll be using five separate functions:
-
showSubmenu
-
createproductsSubmenuMap
-
createresearchSubmenuMap
-
createstoreSubmenuMap
-
clearImageMap
The showSubmenu function does two things: It swaps out the submenu image and calls the appropriate function that will create the actual image map. The next three functions all create the <area> tags and populate them with hrefs, shapes, and coordinates. The clearImageMap function clears the image map in place in order to make room for the new one. For example, when a user clicks “Products,” an image for the Products submenu is created and put in place. If the user then clicks “Research,” the Products image map is erased, and the Research image map is created. Let's take a look at these functions in Example 3-7.
Example 3-7 The five image map functions
function showSubmenu (menuName) { document.images['submenuImg'].src = eval(menuName + "Sub.src") eval("create" + menuName + "SubmenuMap()") } function createproductsSubmenuMap () { replicator = document.createElement("AREA") replicator.href = "prod/replicator.html" replicator.shape = "rect" replicator.coords = "286,44,422,101" creature = document.createElement("AREA") creature.href = "prod/easycreature.html" creature.shape = "rect" creature.coords = "139,126,282,175" biohomelab = document.createElement("AREA") biohomelab.href = "prod/biohomelab.html" biohomelab.shape = "rect" biohomelab.coords = "88,48,196,75" // clear out existing image map clearImageMap() mapObj = document.getElementById("submenuMap") mapObj.appendChild(replicator) mapObj.appendChild(creature) mapObj.appendChild(biohomelab) } !function createresearchSubmenuMap () { capping = document.createElement("AREA") capping.href = "prod/capping.html" capping.shape = "rect" capping.coords = "142,134,432,161" cancer = document.createElement("AREA") cancer.href = "prod/cancer.html" cancer.shape = "rect" cancer.coords = "258,78,431,110" downs = document.createElement("AREA") downs.href = "prod/downs.html" downs.shape = "rect" downs.coords = "81,34,479,67" // clear out existing image map clearImageMap() mapObj = document.getElementById("submenuMap") mapObj.appendChild(capping) mapObj.appendChild(cancer) mapObj.appendChild(downs) } function createstoreSubmenuMap () { fish = document.createElement("AREA") fish.href = "store/capping.html" fish.shape = "rect" fish.coords = "346,106,461,153" mugs = document.createElement("AREA") mugs.href = "store/mugs.html" mugs.shape = "rect" mugs.coords = "347,34,464,60" vegas = document.createElement("AREA") vegas.href = "store/vegas.html" vegas.shape = "rect" vegas.coords = "85,130,272,177" sucks = document.createElement("AREA") sucks.href = "store/sucks.html" sucks.shape = "rect" sucks.coords = "85,100,228,121" selfish = document.createElement("AREA") selfish.href = "store/selfish.html" selfish.shape = "rect" selfish.coords = "86,55,252,92" shirts = document.createElement("AREA") shirts.href = "store/shirts.html" shirts.shape = "rect" shirts.coords = "88,18,211,46" // clear out existing image map clearImageMap() mapObj = document.getElementById("submenuMap") mapObj.appendChild(fish) mapObj.appendChild(mugs) mapObj.appendChild(vegas) mapObj.appendChild(sucks) mapObj.appendChild(selfish) mapObj.appendChild(shirts) } function clearImageMap () { mapObj = document.getElementById("submenuMap") while (mapObj.childNodes.length) { mapObj.removeChild(mapObj.firstChild) } }
♦ HOW THE CODE WORKS
-
We begin by swapping out images, as you've seen before. This method isn't available for Netscape 4, so we don't worry about that browser's need for additional layer information.
-
We then call the appropriate function. For example, if the menuName is “Research,” then the createresearchSubmenuMap function is called.
-
If the user clicked “Products,” then the createproductsSubmenuMap function is called.
-
We start creating the image map by creating all the AREA elements first. Start by creating an empty AREA element.
-
Adding the href information, shape, and coordinates is pretty straightforward.
-
Now we create the AREA element for the EasyCreature kit link.
-
…and the AREA element for the BioHomeLab link.
-
As a precaution, we call the clearImageMap function to remove any image map information that may exist. This information would only exist if the user has already clicked on a navigation link.
-
To make the code easier, we create a little container called mapObj that holds a reference to the subMenu image map (that is, the <map> tag in the HTML).
-
In order for the AREA elements to be a part of the map, they must be attached to it. As we did with the tables in Chapter 2, we use appendChild to add floating elements to tags that exist on the page.
-
We need a separate function for each image map. This is the function that creates the AREA elements for the Research submenu and attaches it to the submenu map.
-
Here's the function that creates the Store submenu's image map.
-
This is the function that removes any image map that may exist on the submenu image.
-
Like we've already seen in this script, we create an object called mapObj that holds a reference to the submenu's map element.
-
Our goal is to remove the image map from the submenu image. The easiest way to do this is not to remove the entire map, but to remove all of the AREA elements from the map. That way, the map never goes away, but without any AREA elements, it appears to no longer exist. We have to remove AREA elements from the map object, and all those AREA elements are children of the map object (remember we added them using appendChild?). That means that if the map object has no children, then there are no AREA elements inside the object. So our goal is to remove all the children of the map object, and our job isn't done until the number of children is zero. A nice way to do this is to create a while loop. While the map object still has children, at least one child must be removed. Thus, the condition of our loop sees if the map object still has children.
-
If the map object still has children, then the first child is removed. That is, the first AREA element in the map object is removed. The code then checks if the map object has any children left, or if that was the last one. If more children remain, then the current first child is removed, and so on until all the AREA elements have been removed.
Whew! Did you get all that? Good. This works nicely, and we can go from submenu to submenu smoothly. We can do one small thing that will make the page function a little bit more like a desktop software program. Right now, once the user clicks a navigation link, bringing forth a submenu, the only way to get rid of that submenu is to click another link and bring forth another. There isn't a way to remove the submenu altogether. This is a pretty easy fix, so let's make it. Most menus on desktop applications appear if you click once, and then disappear if you click the menu item again. Let's do the same for our menu system.
The first thing we do is add the blank, white block to our preloading sequence. Example 3-8 will show us how.
Example 3-8 Preloading blank image
blankSub = new Image() blankSub.src = "images/white_block.gif"
We then add some checking to the showSubmenu function, as in Example 3-9.
Example 3-9 Clicking off submenus
function showSubmenu (menuName) { // see if submenu is already visible docSource = document.images['submenuImg'].src scriptSource = eval(menuName + "Sub.src") if (docSource.indexOf(scriptSource) != -1) { document.images['submenuImg'].src = blankSub.src clearImageMap() } else { document.images['submenuImg'].src = scriptSource eval("create" + menuName + "SubmenuMap()") } }
♦ HOW THE CODE WORKS
-
To make the code more readable, we place the src property of the submenu into a variable called docSource.
-
In a similar vein, we place the value of the source of the preloaded image into a variable called scriptSource.
-
This line may seem a little confusing. What we're trying to do is see if the user clicked on same navigation link twice in a row. If they did, that means the submenu should disappear the second time they clicked it. So, if that were true, it seems that the actual source of the document (docSource) would be equal to the source of the preloaded image (scriptSource). That is, if the user clicked “Store” twice in a row, then both values should be “images/store.gif,” right? That's right, but there's an emphasis on should be. IE, bless its monopolistic heart, actually does work like this. Netscape turns the docSource into an absolute URL, instead of keeping the relative URL of the scriptSource. That's why we have to see if scriptSource (the relative URL) is contained within the docSource (the absolute URL). If the scriptSource is contained within the docSource, the two are essentially identical, and this the user clicked a link twice in a row.
-
We swap out the current submenu and replace it with a blank white graphic.
-
We then remove the image map, since a submenu is no longer visible.
-
However, if the scriptSource is not contained within docSource, then the user clicked on two different navigation links, and we can show the submenu image and create a new image map just like we did before.