Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Lightmapper

4.84/5 (17 votes)
31 Jul 2007CPOL5 min read 1   838  
Article presents the Lightmapper - a helper script to highlight areas of an image map.

Screenshot - lightmapper.jpg

Introduction

The HTML map element defines a client-side image map: a set of regions that are bound to an img element. From a web designer's point of view, it would be nice to be able to highlight or replace the currently active region. Pure HTML doesn't provide a way to alter the contents of an image map. area tags -- the immediate children of the map -- are nothing more than hyperlink hot spots. They do not allow any styling, such as changing a background image or border color, for instance. The easiest way to indicate a current map region via JavaScript is to change the entire image on the mouseover event and then restore the original on mouseout. Here is an example. This method can be very bandwidth-consuming if the target image is big. Another way, which involves CSS and overlay images, is much better in terms of bandwidth, but is very complex to install.

This article explains an alternative technique for changing the appearance of an image map's areas that is easy to use and easy enough to set up. Also presented is a script, the Lightmapper, that uses this technique. Installation of the script requires a casual knowledge of HTML and JavaScript, as well as basic image editing skills. The script is perfectly compatible with all mainstream browsers: Mozilla/Firefox 2, Opera 9 and MS IE 6/7. Lightmapper script and the accompanying art are distributed under the zlib/libpng license.

Setup

The setup procedure assumes the following:

  • You have the image -- in JPEG, GIF or PNG format -- to be bound to the image map. I'll use a scanned black and white map of Europe.
  • You have already made the image map itself, so now you have HTML code with map and area tags. I've used the Imagemapic, which is a free tool for image mapping. However, there are many tools on the Internet that do this.
  • You have a raster image editor advanced enough to handle transparency. Paint.NET or GIMP will be okay. The following explanation assumes that you use Paint.NET.

At first, you have to prepare the overlay images, i.e. the images that will be shown when the cursor enters the respective area. Basically, these images are the areas you've previously defined in the image map. They have been cut from the original "big" image and pasted to the new smaller images. Do the following or skip it if you understand what I meant:

  1. Open your "big" image in a image editor.

  2. For each region:

    • Use the rectangular selection tool to encompass the region in a selection, using zooming to be more accurate.

      Czech Republic is selected

    • Copy the selection to the clipboard either by selecting "Edit" from the menu -> "Copy" item or by pressing Ctrl+C.

    • Create a new image via "File" from the menu -> "New" item. With your selection in the clipboard, the image dimensions will be set to fit this selection.

      File -> New dialog

    • Paste the clipboard contents into the new image by selecting "Edit" from the menu -> "Paste" item or by pressing Ctrl+P.

      Image pasted into the new image from the clipboard

    • If the area/region in the image map is not rectangular, you should use the "Eraser" tool (E key) to erase the contents of the image that go out of the region the overlay is intended for. To do this:

      • Select the "Eraser" tool in the tools window or press the E key

      • In the "Colors" window, select "Secondary" from the drop-down list. Click the "More" button and then move the slider under "Transparency - Alpha" to 0.

        Color selection dialog

      • Accurately erase any parts of the image that do not belong to the desired area.

        Overlay image before painting

    • Edit the new image. For instance, you can use the "Paint Bucket" (F key) to change the color of the whole image.

      Final overlay image

      Note that if the selected area has exclaves, these should be painted on the overlay image with the parent area. However, these can and should be defined separately in the image map. Doing this increases control usability.

The artistic part is now over. Next comes the programming:

  1. Make sure your map tag has both id and name attributes and that these are equal.
    HTML
    <map id="map_of_europe" name="map_of_europe">
  2. Make sure that all areas within a map have an id attribute.
    HTML
    <area href="#" id="Austria" shape="polygon" 
        coords="230, 319, 236, 325, 251, ... >
  3. Include the Lightmapper script file into your HTML page.
    HTML
    <html>
        <head>
            <script type="text/javascript" src="lightmapper.js"></script>
            ...
  4. In the page's head section, create a script block that will contain the setup function.
    HTML
    <html>
        <head>
            <script type="text/javascript" src="lightmapper.js"></script>
            <script type="text/javascript"></script>
  5. Start Lightmapper. The code will look like this:
    HTML
    <html>
        <head>
            <script type="text/javascript" src="lightmapper.js"></script>
            <script type="text/javascript">
    
            function mouse_over(id)
            {
                document.getElementById("name").innerHTML = id;
            }
    
            function mouse_out(id)
            {
                document.getElementById("name").innerHTML = "";
            }
    
            function prepare()
            {
                new Lightmapper(
                    "map_of_europe", "image_of_europe", 
                    mouse_over, mouse_out, [ 
                    ["Albania", "images/_albania.gif", 1, -2],
                    ["Austria", "images/_austria.gif", -1, -2],
                    ["Belarus", "images/_belarus.gif", -2,  3],
                    ["Belgium", "images/_belgium.gif", 1,  3],
    
                    ...
    
                    ["Russia", "images/russia.gif", 1, -7],
                    ["Russia_Kaliningrad", "images/russia.gif",
                        -2,  1, , , "Russia"],
                    ...
    
                    ["Spain",       "images/_spain.gif",       -1, -5],
                    ["Switzerland", "images/_switzerland.gif", -2,  4],
                    ["Sweden",      "images/_sweden.gif",      -2,  5],
                    ["Ukraine",     "images/_ukraine.gif",     -2,  0] ]);
            }
        </script>
  6. Bind the prepare function to the body's onload event.
    HTML
    <html>
        ...
    
        <body onload="prepare()">
    
        ...

Constructor and inner workings

The constructor of the Lightmapper object may seem quite complex. In fact, it is complex until you understand the purpose of each parameter.

JavaScript
function Lightmapper(imgId,     // id of IMAGE element the map is bound to
                     mouseOver, // global onMouseOver handler,
                     mouseOut,  // global onMouseOut handler
                     bindings)  // array of arrays of 
                                // area/image/position/handler bindings:
                                //
                                // [area_id, image_src, x_displacement, 
                                // y_displacement,on_mouse_over,on_mouse_out,
                                // parent_area_id]
                                // where:
                                //
                                // area_id        - id of the area,
                                //
                                // image_src      - path to the image that 
                                //                will be shown on mouseover,
                                //
                                // x_displacement - horiz. displacement 
                                //                  required for correct 
                                //                  alignment of mouse-over 
                                //                  image & the original map
                                //                  (see note below),
                                // y_displacement - vertical displacement,
                                //
                                // on_mouse_over  - onMouseOver handler for 
                                //                  current (area_id) area
                                //                  (replaces the 
                                //                  global handler),
                                // on_mouse_out   - onMouseOut handler for 
                                //                  current (area_id) area
                                //                  (replaces the global 
                                //                   handler),
                                // parent_area_id - id of the parent area 
                                //                  (=pops up with the 
                                //                  current area).

Note that the script makes use of displacement values, specified in pixels, that are used to align the mouse-over image of an area with the original image. These values must be found experimentally by your artist/programmer during the art setup. Provided all of the above, the script does the following:

  • It calculates the position of the target image, in pixels, relative to the body of the document.
    JavaScript
    var ct = document.getElementById(this.imgId);
    
    // Retrieve the position of the target image:
    var initX = 0, initY = 0, ct_ = ct;
    
    if(ct_.offsetParent)
    {
        while(ct_.offsetParent)
        {
            initX += ct_.offsetLeft;
            initY += ct_.offsetTop;
            ct_ = ct_.offsetParent;
        }
    }
    else if(ct_.x)
    {
        initX += ct_.x;
        initY += ct_.y
    }
  • For each area listed in the array of bindings:
    JavaScript
    for(var i = 0, l = this.binds.length; i < l; i++)
    • Given the area's coordinates, i.e. the coords attribute, the position of the image and the displacement values, the script calculates the position of the overlay image associated with the current area.
      JavaScript
      // find area coords/width/height:
      var xmin = 0, xmax = 0, ymin = 0, ymax = 0;
      
      var coords = area.coords.split(",");
      xmin = xmax = +coords[0];
      ymin = ymax = +coords[1];
      
      var m = coords.length, k = 2;
      while(k < m)
      {
          var x = +coords[k++];
          var y = +coords[k++];
      
          if(x < xmin) xmin = x;
          if(x > xmax) xmax = x;
      
          if(y < ymin) ymin = y;
          if(y > ymax) ymax = y;
      }
    • It creates an absolutely positioned div element and places it in the recently calculated position.
      JavaScript
      var div = elem.overlay = document.createElement("DIV");
      div.id = "ml_div_" + String(Lightmapper.prototype._idCount++);
      
      ...
      
      var dts = div.style;
      
      dts.position = "absolute";
      dts.top      = ymin + initY + elem["ydsp"];
      dts.left     = xmin + initX + elem["xdsp"];
      dts.width    = xmax - xmin;
      dts.height   = ymax - ymin;
      dts.opacity  = 0;
      dts.filter   = "alpha(opacity=0)";
      dts.display  = "none";
    • It creates an img element, sets its src attribute to the path to an overlay image and attaches this img to the recently created div.
      JavaScript
      var img = document.createElement("IMG");
      img.src = elem["isrc"];
      img.border = "0";
      
      ...
      
      div.appendChild(img);
      document.body.appendChild(div);
    • It creates a new map with a new single area containing the region of the original area, but transformed to the image-space of the overlay image. It then attaches this map to the img created earlier.
      JavaScript
      // create a map with a single area to bind to our div:
      var map = document.createElement("map");
      var mid = map.name = map.id = div.id + "_bound_map";
      
      img.useMap = "#" + mid;
      
      var area_new   = document.createElement("area");
      area_new.href  = area.href;
      area_new.shape = area.shape;
      
      k = 0;
      while(k < m)
      {
          coords[k++] -= xmin;
          coords[k++] -= ymin;
      }
      
      area_new.coords = coords.join(",");
      
      map.appendChild(area_new);
      document.body.appendChild(map);
    • It attaches a mouseover event to the original area and a mouseout event to the new area.
      JavaScript
      var mi = elem["musi"] || this.mouseOver;
      var mo = elem["muso"] || this.mouseOut;
      
      this._setup_event(area, 'mouseover', 
          this._callLater(this._fade, div.id, 100, 50, 20, mi));
      this._setup_event(elem["prnt"] ? area : area_new, 'mouseout', 
          this._callLater(this._fade, div.id, 0, 50, 20, mo));

To be continued?

This project is in its infancy; it will be developed and expanded. If you wish to help, please vote for this article and/or any other articles of mine. It is the best help I can dream of. :-) For other means of help, please see the donate.txt file in the source archive. Thank-you.

History

  • May 23rd, 2007 - Initial release
  • July 30th, 2007 - Bugfix release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)