In usability we trust

UX and all things web

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;
  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

Update [2009-05-04]: There’s now an example using jQuery available. Also check out the JavaScript file: map_jquery.js.

Update [2010-02-27]: I’ve made a new example using jQuery and a fade effect.

42 Comments

  1. 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. 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. 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. Hej gabriel,

    Jag har använt ditt sätt för imagemap rollover fast min index sida är i php. jag får inte till rollovern att funkar.Har testat i ren html och du funkar utmärkt.Vet inte riktigt vad felet är.
    Några ideer?

    Om du är intresserad av att hjälpa mig så kan jag lägga in koden.

    Tack

  5. You should be able to add keyboard support fairly easily. Add two new events, onfocus and onblur, and use the mouseover code for onfocus and the mouseout code for onblur. I haven’t tested this, but barring some odd behavior due to the image map, it should work. I haven’t used an image map in YEARS but I’m contemplating using one for an upcoming project, so thanks for the demo!

  6. i want finland country map by coords method so plz help me

    thanks:

  7. Excellent concept…have got this working down to a T….with one map.

    However I want to have 3 maps on the same page, I have a jist of javascript but can’t see how to work round this…
    I want to use the same function,but add 2 variables ie:

    var map = $$(‘map1’, ‘map2′,’map3’,);

    and then

    have it run through the funtion checking all 3 maps? Possible?

    Each map has a specific id, in the css so thats working fine…however i cannot get the different maps to rollover…

    Can you help?

    Thanx in advance…

  8. Sorted…Duplicated the function and modified the var map.

    var amap = $$(‘amap’);
    if(amap) {
    var country_list = amap.getElementsByTagName(‘AREA’);
    }
    var bmap = $$(‘bmap’);
    if(bmap) {
    var country_list = bmap.getElementsByTagName(‘AREA’);
    }
    There has to be a more elegant way of doing this….
    Cheers!

  9. Ricardo Santos

    June 15, 2009 at 1:01 pm

    Hi guys want to ask some question!!i’m putting the refence of DOM to html but nothing happens!!don’t know if i’m doing well!!PARDON MY ENGLISH

  10. I have no idea how you figured a single-pixel transparent gif would solve the flickr issue, but *thank you* for sharing! I attempted a few other hacks before finding your solution, which worked like a charm.

    Cheers!

  11. Sune Radich Christensen

    August 13, 2009 at 9:51 am

    Nice read indeed. But the map is not of Scandinavia, normally Scandinavia is used to describe Denmark, Norway and Sweden, and not including Finland 😉

  12. So I’ve got the rollover working like a charm thanks to your code work. But it is a bit abrupt on the transition. Is it possible to add a transition effect to allow them to fade back and forth?

    I’ve been looking high and low and found plenty of faders out there but the problem I cannot blend them with the JS that is reading the map and making the transition. A particular one I like is Approach Transition.

    It’s driving me mad trying to figure out how to add this to the current JS so any helpw would be greatly appreciated!

    Kyle

  13. Kyle: Sorry for being so extremely slow to answer your question but I have now made a new example with a fade effect when the layers are shown and hidden. This way the transition isn’t so abrupt.

  14. Hello again,

    Funnily enough, I’m working on another map and remembered this post and looked back at my old project. Then I thought, it would suit this new project more if it was in jQuery… Thought I’d check back here and share my thoughts on a jQuery re-write, BUT you beat me to it! 🙂 Thought, I’d add some transitions (esp as it’s so easy in jQuery), again you’re already there!! 🙂 I realise this is a bit of a pointless comment, just seems we were on the same wave length. Good work, mister!

    R 🙂

  15. Just a tiny observation of the jQuery fade version, I think it might be good to fade the rollovers in, so on mouseover: $(‘#’+country_id).fadeIn(‘fast’);

    But hide them on mouseout, cutting dead the transitions. Since if you move the mouse quickly back and forth over the areas, the transitions can start to lag otherwise. So I suggest using on mouseout: $(‘#’+country_id).hide();

    R 🙂

  16. You’re right r-doll. Using hide() instead of fadeOut() is a far better solution! Thanks!

  17. Glad to be of assistance! R 🙂

  18. Hej! Firstly I love this concept. I’ve got this working perfectly so the user clicks a mapped region to go to a specific part of the site, though if the user clicks the ‘back button’ the region is still highlighted? how would I stop this so the map region does not stay active??

    Erik

  19. ** I forgot to mention I’m using the fading version of this, I don’t have the same problem with the non-fading version. =)

  20. Erik: I suggest that you follow r-dolls advice and do a .hide() instead of .fadeOut() when hiding the highlighted parts. I have updated the jQuery version to do this. Let me know if this solves your problem.

  21. Hey awesome job Erik,

    I was wondering can a jQuery zoom-in function be added to your code & a navigation to move around the map? because i was thinking of implementing your code to a world map but i need those 2 functions.

    Thanks.

  22. This is so good. Thank you for sharing. This is very helpful for me. Now I know where to start. Thanks and God bless!

  23. Really clear explanation – thanks

  24. I was wondering how your code could keep the region highlighted when clicked, and still show the highlight on the other regions until another region is clicked.

    I need to show a map on the left of the screen and display results in an iframe to the right of the screen. I would like the selected region to stay highlighted until the user selects another region to see those results, etc.

    I changed the click event to this, but it does not stay highlighted.

    $(this).click(function(e) {
    var country_id = $(this).attr(‘id’).replace(‘area_’, ”);
    $(‘#’ + country_id).show();
    });

  25. This was a life saver today. Great work. Thanks much!

  26. Kevin Waterland

    May 18, 2011 at 3:21 am

    This article was extremely helpful. I had a project where I needed to do a mouseover highlight on a custom pie chart and still preserve usability. Couldn’t use Flash or Canvas, so this was the alternative. It works perfectly. Just wanted to drop a note to say thanks.

    Thanks!

  27. Gabriel,

    Thanks for sharing this! 3 years later and still helping people out. I’m using for an application I’m working on write now which is a quiz of the states in the USA (just a project for fun for myself). I’ll post the result if and when I have it.

    • That’s good to hear. I’ll look forward to seeing the result!

      • Gabriel,

        Here’s a link to the program.

        http://maxchadwickspace.com/mapquiz_2/

        I am using the jQuery version of the code. I’m running into issues where they area stays highlighted on mouseout when the mouse passes over the area quickly. Do you know of a solution?

        • Hi Max,

          To deal with the same issue (staying highlighted if too quick), I added this to my mouseover to hide all list items in the map div before showing the new one : $(‘#map li’).hide();

          eg:
          // Assigning an action to the mouseover event
          $(this).mouseover(function(e) {
          $(‘#map li’).hide();
          var country_id = $(this).attr(‘id’).replace(‘area_’, ”);
          $(‘#’+country_id).fadeIn(‘medium’);
          });

  28. Gabriel,
    Thanks for sharing this great bit of code. Probably a newbie question, but how would you get the click event to follow the href, rather than showing the “You clicked…” alert?
    Thanks,
    Bert

    • Hi Rob,

      Just remove the piece of code that handles the click event and the default action, which is to follow the url, will happen instead. Below is the code that handles this (taken from the jQuery example).


      // Assigning an action to the click event
      $(this).click(function(e) {
      e.preventDefault();
      var country_id = $(this).attr('id').replace('area_', '');
      alert('You clicked ' + country_id);
      });

      • First off, thank-you for posting this!!

        My issue is that removing this piece of code to activate the links doesn’t seem to work for me. I am probably doing something wrong, but when I click a link it just doesn’t go anywhere at all.

  29. hi i’m using it on a project and it fits pretty nice, i’m having trouble in firefox it doesn’t work at all… what can i be doing wrong? thks a lot

  30. Just wanted to give a huge thanks for this code. Saved my life on a deadline. Thank you. Brilliant.

  31. hey gabriel, thanks for making such an awesome tutorial. when the map area is activated with a mouseover, is there any way to replace/hide the original image map area with the mouseover image? i don’t want the entire original image map to disappear- just the activated hot spot. apologies- i’m new to javascript & am unsure how to begin editing the js file.

  32. thanks for the nice article. a question: i don’t see the list items (finlad,sweden , etc..) on the side. I’ve tried three browsers (chrome, ffox, ie ) . what can be done?

  33. Thanks for the great tutorial. Using parts of it in a project I’m doing at work. Mostly to see if I can do it. Mostly because I have no idea how to do the rollover. ImageMap = easy. Rollover = not so easy. Anyway. Just wanted to chime in and say thanks. 🙂

    Also, how do I get it to go to a spesific URL when clicked instead of popping up the alert?

  34. Very nice. Thanks for the lesson and insight.

  35. Thank you. Great explanation and solved an issue for us.

  36. Hey thanks for this. Pretty useful. I was tasked with making a map similar too yours. I haven’t done an imagemap since probably 2004. This will help a lot.

Leave a Reply to kyle bellamy Cancel reply

Your email address will not be published.

*