DekGenius.com
[ Team LiB ] Previous Section Next Section

9.11 Determining the Element the Cursor Rolled From/To

NN 6, IE 5

9.11.1 Problem

You want to know the element from which the cursor rolled into the current element or the element to which the user rolled the cursor.

9.11.2 Solution

The IE event model supplies two different property names—fromElement and toElement—to convey references to the relevant elements. In contrast, the W3C DOM event model lets one property—relatedTarget—handle both chores because the reference depends entirely upon which event is being processed. An mouseover event reveals the element from which the cursor came; an mouseout event reveals the element to which the cursor has gone. For example, the following element has mouse event handlers to invoke separate functions for cursor rolls into and out of the element:

<img src="myImg.jp" ... onmouseover="incoming(event)" onmouseout="outgoing(event") />

The following incoming( ) function applies event-property equalization to obtain a reference to the element from which the cursor rolled:

function incoming(evt) {
    evt = (evt) ? evt : ((window.event) ? event : null);
    if (evt) {
        var from = (evt.relatedTarget) ? evt.relatedTarget : 
            ((evt.fromElement) ? evt.fromElement : null);
        if (from) {
            // work with adjacent "from" element
        }
    }
}

The parallel outgoing( ) function obtains a reference to the element to which the cursor has already rolled:

function outgoing(evt) {
    evt = (evt) ? evt : ((window.event) ? event : null);
    if (evt) {
        var to = (evt.relatedTarget) ? evt.relatedTarget : 
            ((evt.toElement) ? evt.toElement : null);
        if (to) {
            // work with adjacent "to" element
        }
    }
}

9.11.3 Discussion

To provide a more concrete example of the interaction between the mouse events and the event properties, Figure 9-1 shows a table containing one central cell and four "hot" cells, one on each side. As you roll the cursor to the center cell, the page indicates which cell the cursor rolled from; conversely, if you roll the cursor out of the center cell, you get a reading of the cell to which the cursor rolled.

Figure 9-1. Table with "hot" cells
figs/jsdc_0901.gif

The HTML for the table, center cell event handlers, and the text box is as follows:

<table cellspacing="0" cellpadding="25">
<tr><td></td><td class="direction">North</td><td></td></tr>
<tr><td class="direction">West</td>
<td id="main" onmouseover="showArrival(event)" 
              onmouseout="showDeparture(event)">Roll</td>
<td class="direction">East</td></tr>
<tr><td></td><td class="direction">South</td><td></td></tr>
</table>
   
<form name="output">
<input id="direction" type="text" size="30" />
</form>

A CSS style sheet sets cell background colors to distinguish the outer cells from the center one:

<style type="text/CSS">
.direction {background-color:#00ffff; 
             width:100px; 
             height:50px; 
             text-align:center
            }
#main {background-color:#fff6666; text-align:center}
</style>

The two functions read the event model-specific properties of the event object, and display the results in the text box:

function showArrival(evt) {
    var direction = "";
    evt = (evt) ? evt : ((window.event) ? event : null);
    if (evt) {
        var elem = (evt.target) ? evt.target : ((evt.srcElement) ? 
            evt.srcElement : null);
        if (elem) {
            // limit processing to element nodes
            if (elem.nodeType =  = 1) {
                // for W3C DOM property
                if (evt.relatedTarget) {
                    if (evt.relatedTarget != elem.firstChild) {
                        direction = (evt.relatedTarget.firstChild) ? 
                            evt.relatedTarget.firstChild.nodeValue : "parts unknown";
                    }
                // for IE DOM property
                } else if (evt.fromElement) {
                    direction = (event.fromElement.innerText) ? 
                       event.fromElement.innerText : "parts unknown";
                }
                // display results
                document.getElementById("direction").value = "Arrived from: " + 
                    direction;
            }
        }
    }
}
   
function showDeparture(evt) {
    var direction = "";
    evt = (evt) ? evt : ((window.event) ? event : null);
    if (evt) {
        var elem = (evt.target) ? evt.target : ((evt.srcElement) ? 
            evt.srcElement : null);
        if (elem) {
             // limit processing to element nodes
             if (elem.nodeType =  = 1) {
                // for W3C DOM property
                if (evt.relatedTarget) {
                    if (evt.relatedTarget != elem.firstChild) {
                        direction = (evt.relatedTarget.firstChild) ? 
                            evt.relatedTarget.firstChild.nodeValue : "parts unknown";
                    }
                // for IE DOM property
                } else if (evt.toElement) {
                    direction = (event.toElement.innerText) ? 
                        event.toElement.innerText : "parts unknown";
                }
                // display results
               document.getElementById("direction").value = "Departed to: " + 
                   direction;
            }
        }
    }
}

Because the W3C DOM event model processes events for text nodes, the functions above must limit their processing to the td element that contains the text label. If we do nothing to filter the event processing, the mouseover event of the text node bubbles up to the td element (assuming no event handler is attached to the text node to cancel event bubbling), and the relatedTarget value points to the central td element itself. In other words, the text node's event regards the surrounding central td element as the node from which the cursor came. This is one case (in contrast to the example in Recipe 9.3) where automatically referencing the parent node of the text node doesn't work, since the event object properties for the two node types have different values for the relatedTarget property.

The complexity of the elements for which you're using this event object feature may have an impact on how successful you are in achieving your goal. This is especially true in the W3C DOM event model, but isn't limited to there. If the container element has a lot of nested elements that don't supply sufficient padding or margin space to allow the mouseover or mouseout events to fire in a timely fashion, you may miss events that you believe should occur. The problem occurs when users are fast with the mouse, and the cursor skips over the container's exposed area so quickly that no event fires.

All of these cautions point to deployment of this technique in carefully controlled environments. The ideal situation has large elements abutting each other, and none with nested content. A table full of images acting as a rectangular mosaic, for example, should work well. Assuming, of course, that you have an application-specific need to capture adjacent element information during mouse events.

9.11.4 See Also

Recipe 9.1 for equalizing event objects of disparate event models.

    [ Team LiB ] Previous Section Next Section