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