In usability we trust

Imagemap rollover

Imagemaps was very popular in the early days of webdesign but seem to have fallen out of grace. Probably due to accessibility problems and the rise of CSS based designs. I think that it still has it’s place when used properly and one being aware of it’s potential accessibility problems.

In a recent project I was building a map where different regions of a map would highlight when you hover with the mouse over it. Using an imagemap in combination with CSS and Javascript seemed like the natural way to do it.

I thought that to actually code this solution would be pretty straightforward, but soon discovered some peculiar quirks. I also couldn’t find any information about this, so I thought that I’d share my experiences with you.

This is what I had in mind when I first started of.

  • Use an image of a map.
  • Make it into an imagemap and define the clickable areas.
  • Do images of the different parts that is to be highlighted.
  • Position them with absolute positioning.
  • Use Javascript to show or hide them when the user hovers with the mouse over them.

The XHTML

I started of with a regular imagemap. (Some code is removed for brevity). I use a map over Scandinavia as the the image for the imagemap.

<div id="map">
	<img src="img/scandinavia.png" alt="" usemap="#scandinavia_map" />
	<map name="scandinavia_map" id="scandinavia_map">
		<area id="area_finland" shape="poly" coords="193,64,207,[...]" alt="Finland" href="finland.html" />
		<area id="area_sweden" shape="poly" coords="192,65,206,[...]" alt="Sweden" href="sweden.html" />
		<area id="area_norway" shape="poly" coords="187,64,194, [...]" alt="Norway" href="norway.html" />
	</map>
	<ul>
		<li id="finland"><a href="finland.html">Finland</a></li>
		<li id="norway"><a href="norway.html">Norway</a></li>
		<li id="sweden"><a href="sweden.html">Sweden</a></li>
	</ul>
</div>

As you can see I also made a list with links. This list serves a dual purpose. First of all I’m using the list items as hooks for the “highlight images”. But they also serves as a backup, in case the user doesn’t have CSS and images turned on.

The CSS

First of all I style the containing <div> with position: relative. This is necessary in order to later on position the different regions with absolute positioning. I’m also defining it’s width and height which is the same as the size of the map image.

#map {
	position: relative;
	width: 340px;
	height: 438px;
}

Then I style the unordered list. The important details here is to give the <li> an absolute positioning to be able to position them on the right spots, a text-indent so that the text won’t show and display: none to hide them. (They will later be displayed with the help of Javascript.)

#map ul {
	list-style: none;
}
#map li {
	position: absolute;
	z-index: 20;
	text-indent: -1000em;
	display: none;
}

Finally I style the different regions giving them the right image as a background, the right dimensions and the right position over the map. It can take some trial and error to get them in the right position. Temporarily set display: block while you position them.

Here are the images that I use as backgrounds on the elements I position over the map. (Well actually these are smaller version of the once I use)

#sweden {
	background: url(../img/sweden.png) no-repeat;
	width: 158px;
	height: 374px;
	bottom: 0;
	top: 64px; 
	left: 80px;
}
#finland {
	background: url(../img/finland.png) no-repeat;
	width: 148px;
	height: 283px;
	top: 27px;
	right: 0;
}
#norway {
	background: url(../img/norway.png) no-repeat;
	width: 292px;
	height: 362px;
	top: 0;
	left: 0;
}

That sets the looks of the map. Now we just need some Javascript to make the rollover effects.

The Javascript

I use the DOM Assistant Javascript library to help with the DOM manipulation. If you prefer you can just as easily use jQuery, Prototype or any other similar Javascript library to do the same thing. Still, to use this example, be sure to include a reference to DOM Assistant in the head of the HTML document.

The script is pretty straightforward. What it basically does is that it loops through the list elements and assign mouseover, mouseout and click events to them. Review the comments in the code for a deeper explanation.

// When the DOM is loaded, initilize initPage
DOMAssistant.DOMReady(initPage);

function initPage() {
	// Get a reference to the containing div with id='map'
 	var map = $$('map');

	// Check if map exists
	if(map) {

		// Get all the areas of the imagemap
		var country_list = map.getElementsByTagName('AREA');

		// Loop through all areas
		for(var i=0; i<country_list.length; i++){
			$(country_list[i]).style.display = 'none';

			// Assign an action to the mouseover event
			$(country_list[i]).addEvent('mouseover', function(e) {
				// Get the id from the hovered area
				var country_id = this.id;

				// Extract the "country"-part of the id = the id of the list-item
				country_id = country_id.substring(country_id.indexOf('_')+1, country_id.length);

				// Set the <li> to "display: inline" = show it
				$$(country_id).setStyle('display', 'inline');
			});

			// Assign an action to the mouseout event
			$(country_list[i]).addEvent('mouseout', function(e) {
				// Get the id from the hovered area
				var country_id = this.id;

				// Extract the "country"-part of the id = the id of the list-item
				country_id = country_id.substring(country_id.indexOf('_')+1, country_id.length);

				// Set the <li> to "display: none" = hide it
				$$(country_id).setStyle('display', 'none');
			});

			$(country_list[i]).addEvent('click', function(e) {
				var country_id = this.id;
				country_id = country_id.substring(country_id.indexOf('_')+1, country_id.length);
				alert('You clicked ' + country_id)
				DOMAssistant.preventDefault(e);
			});
		}
	}
}

This should do it one should think. But here’s where strange things starts to happen. The highlighted image flickers constantly as if the mouseOver and mouseOut events are triggered over and over again. Watch it in action.

Solution to the flicker problem

After some experimenting I came up with a solution where I use a transparent image as an overlay on the map image. I then assign the imagemap to the transparent image and voilĂ , the problem is solved.

<img src="img/transp.gif" id="transparent_map" alt="" usemap="#scandinavia_map" />

It’s important to give the transparent image the same dimensions as the image of the map.

#transparent_map {
	position: absolute;
	z-index: 30;
	width: 340px;
	height: 438px;
	border: none;
}

With that fix the map works just fine. View a live demo.

Accessibility

My conclusion is that this solution, if not perfect, is still pretty accessible. With CSS turned off, the list is visible and even though the map isn’t clickable you can click the links in the list. With Javascript turned off, the different parts of the map doesn’t highlight but they are still clickable.

For those who uses the keyboard to navigate, it’s still usable, but I haven’t been able to find a way to highlight an area when it’s “active”. If anyone has an idea of how to do this, please tell me.

I found one other weakness and a more serious that is. If you browse with images turned off and CSS enabled, neither the map nor the list of links is visible thus you can’t use it. If you have a solution to this, please let me know!

The files

Comments

1. November 3rd, 2008 at 21.55 by r-doll

Hi Gabriel,

Just a note to say this is great! I’m working on something very similar at the moment. I’d also been trying to preserve an accessible version using s, but s do the job and are probably more semantic. :) The image map I’m working is pretty big so I’m going try to also use a background sprite to cut down on the load time - ALA stylee (http://www.alistapart.com/articles/sprites/) Will let you know how I get on combining these ideas!

R:)

2. November 3rd, 2008 at 22.58 by r-doll

oops that should have said…

“using tags, but tags do the job and are probably more semantic”

also not sure if you’re worried about IE6, but seems the positioning is a little off, so will see if I can get that sorted too :)

3. November 3rd, 2008 at 23.32 by Gabriel Svennerberg | Author comment

I’m glad that you found the article useful! :-)

Also thanks for telling me that one of the overlays positioning was a bit off in IE6. I had completely missed that. It’s now corrected in the example. Apparently there’s some extra space at the bottom of the map in IE6, so using bottom as a reference in the CSS wasn’t that good. On the other hand it was easily fixed by changing the position to be relative to the top instead.

Using a sprite seems like a smart thing to do. I’m looking forward to see what you come up with!

/Gabriel

4. November 20th, 2008 at 11.47 by r-doll

Hello again, this is how it turned out so far:
http://www.dollypower.com/staging/breastscreening/index-javascript.html
It is a big ol’ image, so even when using a sprite it may be a bit slower than the example. Will continue to optimise the graphic… But do let me know what you think. R :)

5. November 20th, 2008 at 13.16 by Gabriel Svennerberg | Author comment

r-doll: Hello, It looks like I don’t have permission to access the URL that you provided. I get a 403 Forbidden message. But I’m excited to see what you’ve come up with.

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

About me

My name is Gabriel Svennerberg and I'm an usability oriented web developer living in Sweden.

This is my blog and it's all about web development and usability.

More about me

Blogroll

Copyright © 2007-2008
Gabriel Svennerberg