reptile7's JavaScript blog
Sunday, August 31, 2008
 
lowEndMapcreate
Blog Entry #124

An image of a monkHorizontal crosshairVertical crosshair

The previous post presented a method of returning coordinates for image-map subregions based on the client-side event object and its clientX/clientY properties, and driven by user mouse clicks; we'll refine this method in today's post via the use of movable crosshairs.

Creating, positioning, and monitoring the crosshairs

We can very easily create crosshairs for overlying an image from the 2px-by-2px 'colored dot' images that Joe makes available in HTML Goodies' "So, You Want A 1 X 1 Image, Huh?" tutorial. For example, for spanning the 262px-by-255px "So You Want A Client-Side Image Map, Huh?" monk.gif image above, we can 'stretch' Joe's blue.gif image horizontally and vertically as follows:

<img id="horizcross" width="262" height="1" src="blue.gif" alt="Horizontal crosshair">

<img id="vertcross" width="1" height="255" src="blue.gif" alt="Vertical crosshair">

The crosshairs can be positioned statically in the usual CSS way; if monk.gif and the crosshair images are wrapped in, say, a positioned div element, then

#horizcross { position: absolute; left: 0px; top: 128px; }
#vertcross { position: absolute; left: 131px; top: 0px; }

will 'quadrisect' monk.gif with the crosshairs as shown above. However, in order to move the crosshairs dynamically (vide infra), I find that the left and top values must be set scriptically:

var xcross, ycross;
if (document.getElementById) {
xcross = document.getElementById("vertcross");
ycross = document.getElementById("horizcross");
xcross.style.left = "131px";
xcross.style.top = 0;
ycross.style.left = 0;
ycross.style.top = "128px";
/* You can also set the position values this way if you prefer:
xcross.style.position = "absolute"; ycross.style.position = "absolute"; */ }

Form input fields provide a convenient way to monitor the crosshair positions:

<form action="">
x-coordinate: <input size="5" name="xcoord"><br>
y-coordinate: <input size="5" name="ycoord">
</form>

In practical terms, the xcoord and ycoord fields will display the coordinates of the crosshair intersection. The following script code loads the initial crosshair positions into the xcoord and ycoord fields:

var xposition, yposition;
xposition = parseInt(xcross.style.left);
yposition = parseInt(ycross.style.top);
document.forms[0].xcoord.value = xposition;
document.forms[0].ycoord.value = yposition;

Are you with me so far?

An image of a monkHorizontal crosshairVertical crosshair
x-coordinate:
y-coordinate:

Moving the crosshairs

For pinpointing the precise coordinates of desired image-map subregions, the crosshairs can be moved statically - change the CSS, reload the page, change the CSS, reload the page, etc. - very tedious, needless to say. It would be much better if we could simply drag the crosshairs to where we want them to be, and this is easily achievable via the capture of user mousemoves at the document object level. (My inspiration for this idea: the 'cat chases the mouse' script of HTML Goodies' JavaScript Script Tip #91.) As described in Blog Entry #108, we can use a

document.onmousemove = moveCrosshairs;

assignment statement to coassociate the document object, a mousemove event, and a moveCrosshairs( ) function that (a) assigns the final coordinates of a mousemove event (e.clientX,e.clientY) to the positions of the crosshairs, in effect dragging the crosshairs to the near vicinity of the mouse cursor, and (b) loads the new crosshair positions into the xcoord and ycoord form fields:

function moveCrosshairs(e) {

e = e ? e : (event ? event : "");
if (e) { // If the browser supports the event object:

xcross.style.left = e.clientX;
document.forms[0].xcoord.value = parseInt(xcross.style.left);
ycross.style.top = e.clientY;
document.forms[0].ycoord.value = parseInt(ycross.style.top); } }

We'll roll out a demo in just a bit. The moveCrosshairs( ) conditional code comes from the Apple Developer Connection's "Supporting Three Event Models at Once" tutorial and allows the function to accommodate both Microsoft's event model and the Mozilla/W3C event model.

Older browsers

My code above is designed for 'modern' browsers that support the DOM getElementById( ) method. Regarding older browsers, both MSIE 4.x and Netscape 4.x do not support the getElementById( ) method but do support the event object, and given that some users somewhere out there are probably still using these browsers, I think, "Why not bring them into the loop if at all possible?"

MSIE 4.x

Only minor modifications are needed to get my code to work with MSIE 4.x, chief among them switching the document.getElementById references to document.all references:

xcross = document.getElementById ? document.getElementById("vertcross") : document.all("vertcross");
ycross = document.getElementById ? document.getElementById("horizcross") : document.all("horizcross");


Also: with MSIE 4.5 on my computer, in order (a) to get accurate xcoord/ycoord readings upon moving the crosshairs and (b) to ensure that the monitoring form appears below the monk.gif image, I found it necessary to wrap monk.gif, the crosshair images, and the monitoring form in an absolutely positioned div element anchored at the viewport origin, i.e., at left:0;top:0;. (In contrast, both MSIE 5.1.6 and Netscape 7.02 allowed me to wrap the images in a relatively positioned, "normal flow" div element without any problems.)

Netscape 4.x

More challenging, but doable. I'm not going to discuss this in detail, but here are the highlights:

(1) We noted in the previous entry that Netscape 4.x will not CSS-position the img element. To create movable crosshairs, I wrapped the horizcross and vertcross images in layer elements

<layer left="0" top="128" id="horizlayer"> ...Horizontal crosshair image... </layer>
<layer left="131" top="0" id="vertlayer"> ...Vertical crosshair image... </layer>

whose corresponding JavaScript objects have writable left and top properties.

(2) Recalling that for MSIE 4.x we placed monk.gif, the crosshair images, and the monitoring form in an absolutely positioned div element, we can access the div container, the horizlayer and vertlayer layers, and the monitoring form via the following hierarchy expressions:

document.layers[0] // For the div container
document.layers[0].document.layers["horizlayer"] // For the horizlayer layer
document.layers[0].document.layers["vertlayer"] // For the vertlayer layer
document.layers[0].document.forms[0] // For the monitoring form

Reference: see "The Layer Object" in Netscape's "Dynamic HTML in Netscape Communicator" resource.

(3) The following command enables mousemove capture at the document object level:

document.captureEvents(Event.MOUSEMOVE);

Demo

Use your mouse cursor to drag the crosshairs to wherever you might like them to be in the div below:

An image of a monkHorizontal crosshairVertical crosshair
x-coordinate:
y-coordinate:

August 2016 Update
In reconfiguring my original moveCrosshairDemo.html demo for a self-contained id="crosshairsdemo" div:
(a) It is necessary to replace the clientX reading with an offsetX reading and the clientY reading with an offsetY reading.
(b) The moveCrosshairs( ) function is associated not with the document as a whole but with the monk.gif image itself via a document.getElementById("img1").onmousemove = moveCrosshairs; assignment.
(c) A #crosshairsdemo { overflow: hidden; } styling keeps the blue.gif crosshairs visually within the div.
(d) The previous section notwithstanding, the MSIE 4.x/Netscape 4.x-accommodating code has been thrown out - no one should be using those browsers in this day and age.

The crosshairs will obviously allow you to delineate image-map subregions with greater precision than would mouse clicks alone. I again trust that you are up to the task of using the xcoord/ycoord readings to directly or indirectly set coords values for anchor/area elements.

Application

I've used my crosshair method to create an image map for the photograph below:

Mischief the CatMischief's headMischief's food bowlMischief's body

No, this is not my cat. His name is Mischief, and he lives in England. Move your mouse cursor over Mischief's head, body, or food bowl to pop up an appropriate alert( ) message.

<img src="Mischief.jpg" width="450" height="422" alt="Mischief the Cat" usemap="#catmap">
<map name="catmap">
<area shape="circle" coords="197,143,48" alt="Mischief's head"
onmouseover="window.alert('Hi - I\'m Mischief.');">
<area shape="circle" coords="156,356,59" alt="Mischief's food bowl" onmouseover="window.alert('You\'ll notice that my food bowl is empty.\nLet\'s get it filled!');">
<area shape="circle" coords="215,238,105" alt="Mischief's body" onmouseover="window.alert('Three stone - impressive, eh?');">
</map>

I thought it would be a good idea to wrap up our discussion of image maps with a look at a 'real-world example', so in the next post we'll check over the image maps appearing at http://www.time.gov/, the U.S. Government's "Official U.S. Time" Web site (if I make it through Gustav).

reptile7

Thursday, August 21, 2008
 
Image Maps: Then and Now
Blog Entry #123

The map element and the area element were introduced in 'HTML 3'; the W3C discusses them here in the HTML 3.2 Specification. (Had Microsoft or Netscape implemented them proprietarily before that? I don't know.) In its original incarnation, the map element had a more restrictive content model than it does now; specifically, it could only contain area elements:

<!ELEMENT MAP - - (AREA)+>
<!-- Recall that the area element is an empty element that does not contain other elements or even text. -->

As noted two entries ago, the map element can now hold block-level elements for containing anchor elements that define the map's subregions. And given that area elements do not have an external-to-the-image rendering (e.g., in a visual context, area elements do not generate inline boxes before or after the image) whereas anchor elements do, the W3C prefers that you build your map elements with anchor elements: Authors should use this method to create more accessible documents. Accordingly, most of the W3C's client-side image map examples feature map elements composed of anchor elements wrapped in p elements. (Another relevant example: check out this page's map image map, whose subregion-defining anchor elements appear as items in an unordered list (ul element).)

Let's get back now to the monkareas image map of HTML Goodies' "So You Want A Client-Side Image Map, Huh?" tutorial. I don't know when exactly this tutorial was written, but it is clearly of the 'HTML 4 era' and does not date to the 'HTML 3 era' because the monkareas map contains a shape="default" area element; default was not a valid value for the shape attribute in HTML 3.2:

<!-- From the post's first link, the shape attribute's data type designation is %SHAPE, whose declaration is: -->
<!ENTITY % SHAPE "(rect|circle|poly)">

So Joe definitely could have formulated the monkareas map in terms of anchor elements had he wanted to, for example:

<map name="monkareas">
<p>
<a shape="rect" coords="91,30,186,98" href="http://www.nfl.com">NFL.com</a> |
<a shape="circle" coords="25,72,28" href="http://www.cnn.com">CNN.com</a> |
<a shape="poly" coords="9,115, 86,79, 98,116, 69,131, 86,175, 48,206" href="http://www.usatoday.com">USATODAY.com</a> |
<a shape="circle" coords="107,158,132" href="http://www.cbs.com">CBS.com</a> |
<a shape="default" href="http://www.htmlgoodies.com">HTML Goodies</a>

</p>
</map>
<!-- N.B.: The alt attribute is shared by the area, img, and input elements but is not valid for the anchor element. -->

Understandably, the first wave of browsers to support image maps did not support anchor element-based image maps - on my iMac, I can confirm that Internet Explorer 4.5, Netscape 4.79, and Opera 6.03 do not apply the preceding map to the monk.gif image - and Joe almost certainly used area elements in his map in order to accommodate such browsers. But this isn't an 'either/or situation', at least with respect to HTML. The W3C notes, Authors may wish to mix content so that older user agents will handle map geometries specified by area elements and new user agents will take advantage of richer block content; in other words, we can load all of Joe's area elements and my anchor elements - the whole shebang - into a single map element for maximum browser support:

<map name="monkareas">
<p>
<a shape="rect" coords="91,30,186,98" href="http://www.nfl.com">NFL.com</a> |
<a shape="circle" coords="25,72,28" href="http://www.cnn.com">CNN.com</a> |
<a shape="poly" coords="9,115, 86,79, 98,116, 69,131, 86,175, 48,206" href="http://www.usatoday.com">USATODAY.com</a> |
<a shape="circle" coords="107,158,132" href="http://www.cbs.com">CBS.com</a> |
<a shape="default" href="http://www.htmlgoodies.com">HTML Goodies</a>
</p>
<area shape="rect" coords="91,30,186,98" href="http://www.nfl.com" alt="NFL Home Page">
<area shape="circle" coords="25,72,28" href="http://www.cnn.com" alt="CNN Home Page">
<area shape="poly" coords="9,115, 86,79, 98,116, 69,131, 86,175, 48,206" href="http://www.usatoday.com" alt="USA TODAY Home Page">
<area shape="circle" coords="107,158,132" href="http://www.cbs.com" alt="CBS Home Page">
<area shape="default" href="http://www.htmlgoodies.com" alt="HTML Goodies Home Page">

</map>

Here's what we get when we apply our new-and-improved monkareas map to the monk.gif image:

An image of a monk

NFL.com | CNN.com | USATODAY.com | CBS.com | HTML GoodiesNFL Home PageCNN Home PageUSA TODAY Home PageCBS Home PageHTML Goodies Home Page

Validation note
Block-level content and area elements cannot be validly mixed in a map element in XHTML. Consider the HTML 4.01 and XHTML 1.0 map element content models:

HTML 4.01: <!ELEMENT MAP - - ((%block;) | AREA)+ -- client-side image map -->
XHTML 1.0: <!ELEMENT map ((%block; | form | %misc;)+ | area+)>

The difference in the respective positions of the + quantifiers in these content models is crucial:
• For HTML, the external quantifier of ((%block;) | AREA)+ means that the map element can contain one or more block-level elements or area elements, i.e., mixing block-level content and area elements is OK.
• For XHTML, the internal quantifiers of ((%block; | form | %misc;)+ | area+) mean that the map element can contain one or more block-level elements OR one or more area elements, but not both.



Using mouse clicks to define image-map subregions

In justifying his use of the Mapedit program to create the monkareas map, Joe laments, It's impossible to eyeball the size of the map and pick out points. Impossible? Not with a bit of help from the browser. In this section, I'll describe a quick-and-dirty method for nailing down map coordinates via the 'capture' of mouse clicks. Here's what you do, it's real simple:

(1) Put the image to be mapped in a separate file and push the image into the document content area's upper-left-hand corner so that the viewport and image x-y coordinate systems line up:

img { position: absolute; left: 0px; top: 0px; }

<img src="myImage.gif" alt="Alternate text">

(2) Add the following event handler to the img tag:

onclick="window.alert('The x-coordinate is: ' + event.clientX + '\nThe y-coordinate is: ' + event.clientY);"

(We previously discussed the cross-browser clientX and clientY properties of the event object in Blog Entry #108.)

That's it! Now, with the preceding code in place, click on the image - each click pops up an alert( ) message giving the click coordinates, e.g.:

An alert( ) box showing the coordinates of a click on the image

These coordinates can be used to set the coords attribute values for rect, circle, and poly shapes in the anchor and/or area elements of your image map - I trust you can take it from here.

This method should work for all browsers that support the event object except Netscape 4.x, which (a) does not support the CSS position, left, or top properties* (or most other CSS properties, for that matter), (b) does not support the clientX/clientY event object properties, and (c) supports onclick for the button, document, checkbox, link, radio, reset, and submit objects but not for the image object - what a party pooper, huh? (*Hold it - later experimentation has revealed that Netscape 4.79 does in fact apply these properties to a variety of block-level and inline elements, just not to the img replaced element.) But don't despair, Netscape 4.x users - here's what you can do instead to achieve the same effect:

(1) Wrap the img element in a layer element:

<layer><img src="myImage.gif" alt="Alternate text"></layer>

(2) Use the event object's layerX and layerY properties** and event capture at the document object level to capture mouse clicks on the image via the following script, which can be placed before or after the layer element:

<script type="text/javascript">

if (document.layers) {
document.captureEvents(Event.CLICK);
document.onclick = getCoords;

function getCoords(e) {
window.alert("The x-coordinate is: " + e.layerX + "\nThe y-coordinate is: " + e.layerY); } }

</script>

**Use of the layerX/layerY properties (as opposed to the event object's pageX/pageY properties) renders unnecessary the lining up of viewport and layer/image coordinate systems.

You can try out the above code for yourself by clicking on the monk.gif image in the div below:

An image of a monk

In the next entry, I'll outline an alternate, crosshair-based method of determining coordinates for image-map subregions.

reptile7

Monday, August 11, 2008
 
Trappiste Landscape
Blog Entry #122

In this post, we will in source order work through the area elements of the monkmap/monkareas image map that is applied to the monk.gif image in HTML Goodies' "So You Want A Client-Side Image Map, Huh?" tutorial.

(1) <area shape="rect" coords="91,30 186,98" href="http://www.nfl.com" alt="NFL Home Page">

As noted in the previous post, the shape attribute is shared by the area and anchor elements; for both elements, the default shape value is rect. As you would guess, shape="rect" specifies a rectangular subregion in an image-map image.

For the coords attribute, we impose on the image a screen/viewport/layer-type coordinate system whose
(a) origin, (0,0), is at the image's upper-left-hand corner,
(b) horizontal x-coordinate increases as we go from left to right across the image, and
(c) vertical y-coordinate increases as we go from top to bottom across the image.

For a rectangular image-map subregion, the coords attribute value should hold four coordinates:

coords="left-x, top-y, right-x, bottom-y"

In relation to the coords="91,30 186,98" attribute, these coordinates have the following meanings:
(a) 91/left-x: the area rectangle's left edge is 91 pixels to the right of monk.gif's left edge.
(b) 30/top-y: the area rectangle's top edge is 30 pixels below monk.gif's top edge.
(c) 186/right-x: the area rectangle's right edge is 186 pixels to the right of monk.gif's left edge.
(d) 98/bottom-y: the area rectangle's bottom edge is 98 pixels below monk.gif's top edge.

The coords="91,30 186,98" rectangle is approximately centered around the monk's head in the image:

An image of a monk

There should actually be a comma between the 30 and 186 coordinates - All [coords] values are separated by commas, the W3C informs us - but at least on my computer, the absence of this comma doesn't cause any problems. Moreover, the 91,30 186,98 coords value is not invalid vis-à-vis the (X)HTML DTDs. The coords attribute's data type designation is %Coords;, an entity reference whose replacement text is "CDATA", for which white space characters are OK, as noted in Blog Entry #120.

I trust we don't need to go over the href and alt attributes, which indicate that the coords="91,30 186,98" subregion links to the NFL's home page.

(2) <area shape="circle" coords="25,72 28,97" href="http://www.cnn.com" alt="CNN Home Page">

Per its shape attribute, the next area element defines a circular subregion. For shape="circle", the coords value should hold three coordinates:

coords="center-x, center-y, radius"

CDATA-wise, the coords="25,72 28,97" attribute is valid, but it has an extra coordinate. In the "Notes on invalid documents" section of the HTML 4.01 Specification, the W3C recommends, If a user agent encounters an attribute value it doesn't recognize, it should use the default attribute value. Like most HTML attributes, the coords attribute has an #IMPLIED default value designation; for an #IMPLIED attribute, the default value of the attribute is implicit...[and consequently] must be supplied by the user agent, although I have no idea what that value (if any) might be for the coords attribute.

The coords attribute is a presentational attribute of sorts in that it indirectly sets the dimensions (e.g., width and height) of an image-map subregion, and it occurred to me that the CSS 2.1 Specification might discuss image maps and address our wrong-number-of-coordinates situation, but alas, it does not.

Anyway, moving the mouse cursor over the monk.gif image in the tutorial does pick up a small http://www.cnn.com subregion in the image's upper-left quadrant, and I speculated, "Perhaps the browser is acting on the first three coordinates and ignoring the fourth coordinate," and this is evidently what happens in this case. So getting back to the above shape="circle" coords value, the first three coordinates of the coords="25,72 28,97" attribute have the following meanings:
(a-b) 25,72/center-x,center-y: the center of the area circle is 25 pixels to the right of monk.gif's left edge and 72 pixels below monk.gif's top edge.
(c) 28/radius: the area circle has a 28-pixel radius.

The coords="25,72,28" circle is approximately centered around the red apple (or whatever it is) in the image:

An image of a monkThe circular subregion of the second area element

Note that a very small part of the circle is actually outside of the image. External-to-the-image parts of image-map subregions are not 'active': they don't link to their href destinations or do whatever else they're supposed to do.

(3) <area shape="circle" coords="107,158 132,162" href="http://www.cbs.com" alt="CBS Home Page">

Per the preceding discussion, the third-in-source-order area element defines a circular subregion whose center is 107 pixels to the right of monk.gif's left edge and 158 pixels below monk.gif's top edge, and whose radius is 132 pixels:

An image of a monkThe circular subregion of the third area element


It's not quite clear what Joe was trying to do with this area element, which covers most of the image and extends 25 pixels to the left of the image's left edge and 35 pixels below the image's bottom edge. The area circle is centered on the cup in the monk's left hand, and the circle's 'right side' (the arc running clockwise from 12 o'clock to 6 o'clock) approximately traces the right-hand contour of the monk, so perhaps this area of the map is meant to be a 'catchall' that covers the remainder of the monk (recall that the monk's head is mapped by the 'NFL rectangle' described earlier), linking it to http://www.cbs.com. Unfortunately, the circle's large size - note that its diameter (264px) is larger than both the image width (262px) and height (255px) - causes it to override the next area element...

(4) <area shape="poly" coords="9,115 86,79 98,116 69,131 86,175 48,206" href="http://www.cnn.com" alt="USA TODAY Home Page">

<!-- Yes, the href URL is a mistake - USA Today's home page is of course located at http://www.usatoday.com/. -->

The poly shape value is short for "polygonal", i.e., the area subregion is a polygon of some sort. For shape="poly", the coords value

coords="x1, y1, x2, y2, ..., xN, yN"

sequentially lists the (x,y) coordinates of adjacent vertices of the polygon (it should thus hold an even number of coordinates): the first vertex is located at (x1,y1), the next vertex is at (x2,y2), the next at (x3,y3), etc., going clockwise or counterclockwise around the polygon periphery. The coords="9,115 86,79 98,116 69,131 86,175 48,206" attribute defines an irregular, L-shaped hexagon whose coordinates are (9,115), (86,79), (98,116), (69,131), (86,175), and (48,206), and that approximately traces the book in which the monk is writing:

An image of a monkThe polygonal subregion of the fourth area element

Let's take stock of where we are for a moment. The third area element completely blankets the fourth area element and largely overlays the first and second area elements:

An image of a monk
The circular subregion of the second area elementThe circular subregion of the third area elementThe polygonal subregion of the fourth area element


The W3C importantly notes, If two or more defined regions overlap, the region-defining element that appears earliest in the document takes precedence (i.e., responds to user input); consequently, the third area element will override the fourth area element but will not override the first and second area elements. Accordingly, if you run your mouse cursor over the monk's head, the apple, and the monk's writing book in the monk.gif image in the tutorial, you will respectively see http://www.nfl.com, http://www.cnn.com, and http://www.cbs.com (not http://www.cnn.com) in the browser window's status bar.

As you have probably figured out, preventing the third area element from overriding the fourth area element is a simple matter of switching the source order of these elements.

(5) <area shape="default" href="index.html">

By itself, a shape="default" area element would apply to the entire image; if present, it is placed last in a map element as a catchall for the parts of the image not covered by preceding area elements. Looking at the 'putting it all together' image above, we can easily identify the heretofore-unmapped image subregions (mostly adjoining the top and right edges, but don't miss the 'southwest corner') that this area element will link to http://www.htmlgoodies.com/index.html, the HTML Goodies home page. A shape="default" area element obviously does not need a coords attribute but should still hold an alt attribute (e.g., alt="HTML Goodies Home Page"), which is the one #REQUIRED attribute of the area element.

In the following entry, we'll discuss the use of anchor elements to define image-map subregions - depending on how long that takes, I may also next time follow through on my promise in the previous entry to "show you how to define image subregions via crosshairs and mouse [events]."

reptile7


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)