How to Dynamically Highlight HTML Image Map Regions with JavaScript

Creating dynamic image maps that toggle highlighting of each area when clicked, and that submits a form with a list of areas selected (highlighted) using HTML and simple JavaScript:

Partial screenshot of mapped image showing 3 selected fields (in translucent pink).
I was tasked at work with creating an image map from one of their busier screens (one with a large number of input fields) to provide an interface for users to select fields with their mouse for creating their own custom reports on the fly.  I came up with the image map idea since the users are already very familiar with the fields and where they appear on this screen.

A normal HTML image map is composed of multiple “click-able” regions which, when “clicked” with a mouse, a CGI program is launched based on which region is clicked.  I needed a slight variation of that concept where the user could click a region representing a field and the field would be toggled (added to or removed from the list of fields the user had selected).  The problem I soon discovered was is that there was no easy way to mark / highlight a region as it is selected, so that the user can easily see which fields he has already selected.  After googling around I found out that this is because HTML image map <AREA> tags are not “visible”, meaning that they can not be “styled” with CSS to change their appearance dynamically.  I then remembered creating a background image with HTML input form elements overlaying it a while back for another project (details in my September 2010 blog-post “Absolute Positioning for Absolute Control“).  I was able to layer the input fields over the background image with precise relative location specifications.  I got to thinking about overlaying the fields when the user clicks them with a translucent rectangle image highlighting the field while permitting the user to still see the field he has selected.  HTML permits one to specify the width and height of an image and JavaScript permits one to dynamically create, display, and hide images and other HTML elements.  I created a red, translucent image the size of my background image using The GIMP by selecting a slightly pinkish-red color and filling the canvas with that.  Then I created an alpha channel and reduced the opacity to 33% and saved it as a PNG.

The trick is to dynamically create an HTML image element containing my newly-created image and then place it precisely over the <AREA> tag that the user clicked, with the same dimentions as the coordinates of the selected region.  First, I had to create the page, which simply consists of the image and an HTML form with a submit button below it (for the user to press when they have selected all their fields).  After the <BODY> and <FORM> tags, you need a <DIV> tag styled for “relative” positioning.  Your <IMG> and <MAP> tags must be enclosed with it.  I added a hidden field called “fields”, which the JavaScript function called when the form is submitted will put the final list of fields the user has selected as a string, for return to the CGI script.  I also included the submit button (the rest of the page) within it as well.  This DIV must be followed immediately with a second, empty <DIV> tag with an ID that our JavaScript will fill with our translucent highlighting image that I previously discussed creating.

I also used GIMP to create the image map.  GIMP allows you to use your mouse to select each click-able region on your image and automatically generates the HTML image map in a file with the same name as your image, but with a “.map” extension.  I then pasted the content of that file into my HTML form.  I had to tweak the <AREA> tags to change the URL (HREF) parameter to “#” and add an “onClick” parameter referencing the JavaScript function I want called when the user clicks on the region bounded by each <AREA> tag.  For parameters, I pass “this” (a self-reference to the <AREA> tag) and a string representing the name of the field represented by that particular area of the map – to distinguish which area the user has clicked on.  I named this function “addfield()”, since it’s function is to add the field clicked on to a list.  I declared the list as a global JavaScript array variable, calling it “fldlst”.

Even though the user’s clicking on a given area of the image needs to “toggle” the field by either highlighting it and adding it to the list, or removing the highlighting and removing it from the list of selected fields, the “addfield()” function need only add the field and highlight it.  The reason for this is that when the field is highlighted, the red translucent image is displayed “on top of” the main image.  This results in the “onClick” function in the image map’s <AREA> tag NOT being called! Instead, a separate “onClick” function I called “togglefield()” must be specified in the dynamically created <IMG> tag that displays the highlighting image.  This function must be a separate function, since we will not pass the “this” argument to it (since “this” would refer to an <IMG> tag and not an <AREA> tag, and besides, we don’t need the coordinates), which exists solely to remove the highlight image and the field from the list.  Actually, to keep it simple and clean, we don’t actually remove the highlight image from the area it’s highlighting if the user deselects a previously-selected field.  We instead simply make it invisible by setting the “display” style to “none”.  This way, if the user later re-selects the same field, the “addfield()” function checks for the existence of the image before attempting to create it and simply sets it’s “display” style back to “”, making it visible once again, if the image already exists.  This requires that we create the <IMG> tag with a unique ID value that includes the field name (we pass to addfield() and togglefield()) for later referencing.

A third JavaScript function is needed for invocation when the form is submitted to put the final list of selected fields in a hidden form field I named “fields” to be returned to the CGI handler script.  I called this function “doonsubmit()”.  It appends the global list (“fldlst”) built by the addfield() function into a comma-separated string of field names and puts that into the hidden form field “fields”.

I also found that the IE browser offsets the image on the top of the page slightly different than Firefox or Chrome, so I have to query the browser name and set the “xoffset” and “yoffset” values appropriately to get the highlighted image’s coordinates to match up with those of the corresponding coordinates from the <AREA> tags.  You may need/want to adjust these slightly to improve the visual appeal.  Also note, that in the addfield() function, we have to get the <AREA> tag’s coordinate values, which are two X,Y pairs and convert them into a single X,Y pair and width and height values for the <AREA> tag it builds for the highlight image.

The full HTML page and the three JavaScript functions are shown below:

<html>
<head>
<script>

var fldlst = [];  //ARRAY CONTAINING FIELD NAMES THE USER HAS SELECTED.
var ua = window.navigator.userAgent;  //BROWSER ID.
var msie = ua.indexOf ("MSIE ");  //!0 IF IE (VSN#), 0 IF FIREFOX/CHROME, ETC.
var xoffset = (msie > 0) ? 9 : 5;  //PIXEL OFFSETS FOR ABSOLUTE POSITIONING TO ALIGN IMAGES.
var yoffset = (msie > 0) ? 15 : 8;

function addfield (ara, fld) {  //ADDS FIELD TO SELECTED LIST AND HIGHLIGHTS CORRESPONDING AREA.
   var crds = ara.coords.split(',');  //COORDINATES OF FIELD'S <AREA> TAG.
   var lf = xoffset*1 + crds[0]*1;  //ADJUST FOR BROWSER.
   var tp = yoffset*1 + crds[1]*1;
   var wd = crds[2] - crds[0];  //CALCULATE WIDTH & HEIGHT FOR HIGHLIGHT IMAGE:
   var ht = crds[3] - crds[1];
   var imgthere = document.getElementById("img_"+fld);
   if (typeof imgthere == "undefined" || imgthere == null) {  //1ST CLICK: CREATE AN IMAGE TAG:
      var newimg = '<img id="img_'+fld+'" style="position:absolute; left:'+lf+'; top:'+tp+';" width="'+wd+'" height="'+ht+'" onClick="javascript:togglefield('+"'"+fld+"'"+')" src="/imgpath/highlight.png">';
      document.getElementById("highlights").innerHTML += newimg;
      fldlst.push(fld);
   } else if (imgthere.style.display == 'none') {  //SUBSEQUENT: REDISPLAY EXISTING HIGHLIGHT IMAGE:
      imgthere.style.display = '';
      fldlst.push(fld);
   }
   return false;
}

function togglefield (fld) {  //TURN HIGHLIGHT BACK ON AND RE-ADD THE FIELD:
   var imgthere = document.getElementById("img_"+fld);
   if (imgthere.style.display == 'none') {
      imgthere.style.display = '';
      fldlst.push(fld);
   } else {  //OTHERWISE, TURN OFF HIGHLIGHT AND REMOVE FIELD IF SELECTED:
      imgthere.style.display = 'none';
      var newfldlst = [];
      for (var i=0;i<fldlst.length;i++) {
         if (fldlst[i] != fld) {  //ADD FIELD TO LIST IF NOT THERE.
            newfldlst.push(fldlst[i]);
         }
      }
      fldlst = newfldlst;
   }
   return false;
}

function doonsubmit () {  //CALLED ON FORM SUBMIT - STORE LIST OF SELECTED FIELDS IN HIDDEN FORM FIELD:
   var s = '';
   for (var i=0;i<fldlst.length;i++) {
      s += fldlst[i]+',';
   }
   document.f.fields.value = s;
   return true;
}

</script>
</head>
<body>
<form NAME="f" METHOD="post" ACTION="cgi-script.pl" onSubmit="doonsubmit()">
<div style="position:relative; ">
<img src="/imgpath/lookup_order1.1.jpg" width="1114" height="671" border="0" usemap="#map" />

<map name="map">
<!-- #$-:Image map file created by GIMP Image Map plug-in -->
<!-- #$-:GIMP Image Map plug-in by Maurits Rijk -->
<!-- #$-:Please do not edit lines starting with "#$" -->
<!-- #$VERSION:2.3 -->
<!-- #$AUTHOR:Jim Turner -->
<area shape="rect" coords="5,52,546,79" alt="com1" href="#" href="#" onClick="javascript:addfield(this,'com1')" />
<area shape="rect" coords="547,53,1091,79" alt="com2" href="#" onClick="javascript:addfield(this,'com2'); return false" />
<area shape="rect" coords="3,110,66,134" alt="init" href="#" onClick="javascript:addfield(this,'init'); return false" />
<area shape="rect" coords="68,110,143,134" alt="number" href="#" onClick="javascript:addfield(this,'number')" />
</map>
<P><input name="gotfields" type="SUBMIT" value="Go"><input name="fields" type="hidden" value=""></P>
</div><div id="highlights"></div>
</form>
</body>
</html>



Dialup Internet, ’70s-style!  Photo courtesy (c) Hugh Mann, Wrybread.com.
<<< Retro internet access! LOL!

Tutorial: The best tips & tricks for bash, explained.
Pinhead, October 3, 2012.

Today’s quote:  “Veblen warned that a combination of this aristocratic disdain for industrial progress and an ill-informed proletariat that had no real understanding for business but simply resented businessmen as “the rich,” could form a lethal tandem to undermine any advanced society. This alliance between the top and the bottom would squeeze the productive middle classes that produce most of the wealth.”
by: William Tucker,
from:  “With Obama It’s Top and Bottom Against the Middle“,
American Spectator.org, Oct. 11, 2012.

Cuba Announces Release of the World’s First Lung Cancer Vaccine
by: Clay Dillow, Popsci.com, Sep. 8, 2011.

Advertisements

2 Comments

  1. silez
    Posted May 27, 2013 at 9:29 am | Permalink | Reply

    where is the ACTION script (CGI script)`???

    • Posted May 27, 2013 at 10:56 am | Permalink | Reply

      #My original script was long, complicated and not particularly pertinent to the article, but here’s the parts that are:

      #!/usr/bin/perl

      use CGI;
      my $q = new CGI;
      my $qv = $q->Vars;
      print $q->header();
      $qv->{fields} =~ s/\,$//; #STRIP TRAILING COMMA, IF THERE IS ONE. THE SELECTED FIELD NAMES ARE SEPARATED BY EITHER ‘,’ OR NULL (chr(0)).
      my @fields = split(/[\,]/, $qv->{fields}); #RETRIEVE THE LIST OF “HIGHLIGHTED” FIELD NAMES (‘fieldname1′,’fieldname2′,…’fieldnameN’).
      if (defined $qv->{gotfields}) {
      #GENERATE AND DISPLAY NEXT SCREEN.
      } else {
      #DISPLAY INITIAL HTML SCREEN WITH JAVASCRIPT FOR USER TO HIGHLIGHT FIELDS (HTML ABOVE IN ORIGINAL POST)
      }

      exit(0);

      __END__

      #COMMENT BACK IF YOU HAVE FURTHER QUESTIONS.
      #JIM

Feel Free to Comment (Name/Email/Website optional):

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: