CSS, HTML, JAVASCRIPT:
HIGHLIGHTING TABLE ROWS UPON MOUSE OVERS
|
Below is an example of what we're trying to accomplish. Move the mouse over
any row in the table and the row will be highlighted.
Brand |
Dimensions |
Price |
Size |
Color |
Type |
Comment |
Row A |
200x300 |
$200,000.00 |
small |
white |
good |
2 doors |
Row B |
200x100 |
$1,100,300.00 |
medium |
yellow |
good |
3 wheels |
Row C |
1200x2100 |
$100,000.00 |
large |
white |
good |
4 wheels |
Row D |
20x210 |
$300.00 |
medium |
blue |
good |
100 wheels |
Row E |
23x30 |
$2,300.00 |
large |
yellow |
good |
9 wheels |
Such highlighting is especially useful when you have a table that is wide and
has a small font type.
There are some specific goals in this exercise:
-
Be able to preserve the original style of the table rows
(including the cells within the row)
-
To not depend on the
initial state of the table. This means that, we should not need require
the table to follow any particular style
(ie: there should not a be a requirement to change the table appearance
attributes to be able to highlight/un-highlight
it).
Because of those requirements, we cannot just
change the style of the row to be highlighted (using className or by altering
style.backgroundColor, such as shown below):
<STYLE>
<!--
tr { background-color: #DDDDDD}
.initial { background-color: #DDDDDD; color:#000000 }
.normal { background-color: #CCCCCC }
.highlight { background-color: #8888FF }
//-->
</style>
<table border="0" cellspacing="0" bgcolor="#CCCCCC" cellpadding="0">
<tr>
<td bgcolor="#FFCC00" WIDTH="100"><b>Brand</b></td>
<td bgcolor="#FFCC00" WIDTH="100"><b>Dimensions</b></td>
<td bgcolor="#FFCC00" WIDTH="100"><b>Price</b></td>
<td bgcolor="#FFCC00" WIDTH="100"><b>Size</b></td>
<td bgcolor="#FFCC00" WIDTH="100"><b>Color</b></td>
<td bgcolor="#FFCC00" WIDTH="100"><b> Type</b></td>
<td bgcolor="#FFCC00" WIDTH="100"><b>Comment</b></td>
</tr>
<tr style="background-color:#CCCCCC;"
onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td>Row A</td>
<td>200x300</td>
<td>$200,000.00</td>
<td>small</td>
<td>white </td>
<td>good</td>
<td>2 doors</td>
</tr>
<tr
onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td>Row B</td>
<td>256x1000</td>
<td>$232,300.00</td>
<td>large</td>
<td>yellow </td>
<td>good</td>
<td>nice</td>
</tr>
<tr class="initial"
onMouseOver="this.className='highlight'" onMouseOut="this.className='normal'">
<td>Row 3</td>
<td>543x300</td>
<td>$122,111.00</td>
<td>medium</td>
<td>yellow </td>
<td>good</td>
<td>expensive</td>
</tr>
.... |
The above code uses className property to change the
background color of the row. The className is changed to 'highlight'
when the mouse is moving over the table;
and it's changed to 'normal' when the mouse is moving out.
This simple approach do work on some cases, but it has some
serious limitations. Here are some of them.
-
If a <tr> has its background color specified , that color
will be lost after highlighting (for example Row B below).
-
If a style is already specified on a <tr> element, that
<tr> will not highlight (for example, Row A below).
-
If a <td> has its background color specified, that <td> will
not be highlighted (for example, the last row of the table below).
-
<tr> style will be lost (for example, Row B, Row D below).
To see the problem, move the mouse around the table below. Note: You might need to reload the page to see the problem.
Brand |
Dimensions |
Price |
Size |
Color |
Type |
Comment |
Row A |
200x300 |
$200,000.00 |
small |
white |
good |
2 doors |
Row B |
256x1000 |
$232,300.00 |
large |
yellow |
good |
nice |
Row C |
543x300 |
$122,111.00 |
medium |
yellow |
good |
expensive |
Row D |
56x340 |
$2,300.00 |
medium |
green |
good |
noisy |
Row E |
23x30 |
$2,300.00 |
large |
yellow |
good |
9 wheels |
Compare the behavior of this table with the table at the top of the page
to see the problems.
To resolve these problems, here are the things that need to be
done:
-
Save the original state (eq: style) of the row and cells under the
mouse.
-
Alter the background color of the row under the mouse. This can be done using style sheet.
-
Restore the original background color of the row and cells.
While the options presented below are not perfect, and might not
work as expected on all cases, they try to overcome the issues mentioned
above.
|
OPTION 1:
Using Mouse Event
Handler on Every Row
|
In this option, we basically assign an onMouseOver
event handler to every row in the table, and when the onMouseOver event is fired, we
save the attributes (or style of the row AND the cells within the row).
We then highlight the row.
When the user moves the mouse out of the row, the
onMouseOut event is fired. You'll see below that we also
assign an onMouseOut event handler to un-highlight and restore
the state of the row AND the cells within the row.
The main drawback of this option is is that because there is no way
to assign an event handler to a style sheet, the onMouseOver
event handler will have
to be explicitly assigned to every row on the table. So the table row will look
something like this:
<TABLE onMouseOut="javascript:highlightTableRowVersionA(0);">
<!-- The onMouseOut above makes sure that when the mouse is moving -->
<!-- out of the table, no row is still highlighted-->
<tr onMouseOver="javascript:highlightTableRowVersionA(this, '#8888FF');">
<td>Brand C</td>
<td>1200x2100</td>
<td>$100,000.00</td>
<td>large</td>
<td>white</td>
<td>good</td>
<td>4 wheels</td>
</tr>
<tr onMouseOver="javascript:highlightTableRowVersionA(this, '#8888FF');">
<td>Brand C</td>
<td>1200x2100</td>
<td>$100,000.00</td>
<td>large</td>
<td>white</td>
<td>good</td>
<td>4 wheels</td>
</tr>
<tr onMouseOver="javascript:highlightTableRowVersionA(this, '#8888FF');">
<!-- AND SO ON -->
</TABLE>
|
See the
example.
The onMouseOver event handler is added to every row. As you can see, this can be quite a tedious task
to add manually (what if there are over 100 rows?), unless the table is generated by a script that
is. When the mouse
is over a row, the function highlightTableRowVersionA(this) will be
called. In this function, the row background
color will be altered (via style sheet). The function
highlightTableRowVersionA is shown
below. Notice also the parameter
'#8888FF'. This is the highlight color. It is added as a
parameter to make it easier to alter the highlight color between different
tables.
You will also see there, that I am
assigning an event handler to the onMouseOut event like this:
tableRow.onMouseOut="highlightTableRowVersionA(0);";
This is so that when the mouse is moving out of the row,
highlightTableRowVersionA(0) will be called, which in turn will
un-highlight the row. We know for sure once the mouse is
enters a row, the mouse will
eventually moves out of it. So, assigning the onMouseOut
like this saves time from having to assign
it manually to every row. Now let's take a
look at that function:
/////////////////////////////////////////////////////
// Highlight table row.
// myElement is the table row
// highlightColor is the color of the highlight
/////////////////////////////////////////////////////
function highlightTableRowVersionA(myElement, highlightColor)
{
var i=0;
// Restore color of the previously highlighted row
for (i; i<savedStateCount; i++)
{
restoreBackgroundStyle(savedStates[i]);
}
savedStateCount=0;
// If you don't want a particular row to be highlighted, set it's id to "header"
if (!myElement || (myElement && myElement.id && myElement.id=="header") )
return;
// The following code traverses every <td> within the <tr> and highlights it
// by changing its style[backgroundColor] property
if (myElement)
{
var tableRow=myElement;
// Save the backgroundColor style OR the style class of the row, so we can restore
// it later
if (tableRow)
{
savedStates[savedStateCount]=saveBackgroundStyle(tableRow);
savedStateCount++;
}
// Since myElement is a <tr>, then find the first <td>.
// You'd think that the <td> is going to be the
// firstChild of the <tr>, but it's not always the case (it
// depends on how the browser defines the DOM).
var tableCell=findNode(myElement, "TD");
i=0;
// Loop through every sibling. Theoretically, a sibling of a <td> should be
// another <td>, but this is not always the case on certain browsers,
// so we need to check the tagName to be sure and skip to the next
// sibling if the sibling is not a <td>)
// We then highlight every siblings
while (tableCell)
{
// Make sure it's actually a cell (a <td>)
if (tableCell.tagName=="TD")
{
// If no style has been assigned, assign it, otherwise Netscape will
// behave weird.
if (!tableCell.style)
{
tableCell.style={};
}
else
{
savedStates[savedStateCount]=saveBackgroundStyle(tableCell);
savedStateCount++;
}
// Assign the highlight color
tableCell.style["backgroundColor"]=highlightColor;
// Optional: alter cursor
tableCell.style.cursor='default';
i++;
}
// Go to the next cell in the row
tableCell=tableCell.nextSibling;
}
}
}
|
The code that actually changes the
highlight color is marked in red. As you see, this part is very simple. The
difficult task are:
-
Saving and restoring the original background color of the row and
making sure that the code does not loose the original style of the row (if
any). This part is marked in green. Notice that that
there are some delicate maneuvers that needed to be done. The
two functions are shown below. It's necessary to handle cases
where a style is specified and when the background color is specified,
therefore, we need to save both. In short, dealing with styles
using JavaScript is a fairly delicate task.
/////////////////////////////////////////////////////
// This function takes an element as a parameter and
// returns an object which contain the saved state
// of the element's background color.
/////////////////////////////////////////////////////
function saveBackgroundStyle(myElement)
{
saved=new Object();
saved.element=myElement;
saved.className=myElement.className;
saved.backgroundColor=myElement.style["backgroundColor"];
return saved;
}
/////////////////////////////////////////////////////
// This function takes an element as a parameter and
// returns an object which contain the saved state
// of the element's background color.
/////////////////////////////////////////////////////
function restoreBackgroundStyle(savedState)
{
savedState.element.style["backgroundColor"]=savedState.backgroundColor;
if (savedState.className)
{
savedState.element.className=savedState.className;
}
}
|
/////////////////////////////////////////////////////
// This function is used to find the first descendant with the specified tag name.
/////////////////////////////////////////////////////
function findNode(startingNode, tagName)
{
// on Firefox, the <td> node might not be the firstChild node of the <tr> node
myElement=startingNode;
var i=0;
while (myElement &&
(!myElement.tagName || (myElement.tagName && myElement.tagName!=tagName)))
{
myElement=startingNode.childNodes[i++];
}
if (myElement && myElement.tagName && myElement.tagName==tagName)
{
return myElement;
}
// On Internet Explorer, the <tr> node might be the firstChild node of the <tr> node
else if (startingNode.firstChild)
return findNode(startingNode.firstChild, tagName);
return 0;
}
|
In the highlightTableRowVersionA function, also notice this line:
if (!myElement || (myElement && myElement.id && myElement.id=="header") )
return;
This line enables certain rows
to be omitted from highlighting. Such rows should have its ID
set to the string "header". This is useful if you do
not wish to highlight the table header.
See the latest code: tableH.js
|
OPTION 2:
Using Mouse Event
Handler and Row Detection
|
This option is only slightly different from the
previous one, but it's also
more extensible and reusable. The gist of this method is to poll the mouse
events within
the <table> itself (as opposed to within every <tr>
like in the previous solution). The code then checks which row is
currently below the mouse, and highlights the row.
Below
is an example of a table, on which I assign an onMouseOver event
handler. When the mouse is over the table (not just a row), the code
check which row is under the mouse. It then highlight the row (or the
cell within the row). I also assigned an onMouseOut
event handler. This is intended to un-highlight previously highlighted row if the
user moves the mouse outside the table.
<table onMouseOver="javascript:trackTableHighlight(event, '#8888FF');"
onMouseOut="javascript:highlightTableRow(0);">
<!-- TABLE CONTENT -->
</table>
|
See the
example.
A major requirement of this code is the need to detect what element is
currently below the mouse position. In this example, the function trackTableHighlight(event)
does this part. I will not
explain the detail of how this detection works here, but you can read about it on my
other
tutorial.). Here's what trackTableHighlight()
looks like.
function trackTableHighlight(mEvent, highlightColor)
{
if (!mEvent)
mEvent=window.event;
// Internet Explorer
if (mEvent.srcElement)
{
highlightTableRow( mEvent.srcElement, highlightColor);
}
// Netscape and Firefox
else if (mEvent.target)
{
highlightTableRow( mEvent.target, highlightColor);
}
}
|
And here's a slight modification to
the highlightTableRow()
function. The major
change from the previous version is that we need to make sure that we're getting the
table row <tr>
element. This is because when the mouse is over a <table>, the
mouse could be hovering on any kind of elements. The mouse could be hovering
over a
<td> element, it could be over a <span> element, or an <img>
element if the
table has an image, or practically anything. So we need to traverse
the DOM tree until we got the table row <tr> element. You can
read more about this subject here: Traversing
DOM tree. The changes are shown in blue.
/////////////////////////////////////////////////////
// Highlight table row.
// newElement could be any element nested inside the table
// highlightColor is the color of the highlight
/////////////////////////////////////////////////////
function highlightTableRow(myElement, highlightColor)
{
var i=0;
// Restore color of the previously highlighted row
for (i; i<savedStateCount; i++)
{
restoreBackgroundStyle(savedStates[i]);
}
savedStateCount=0;
// To get the node to the row (ie: the <TR> element),
// we need to traverse the parent nodes until we get a row element (TR)
// Netscape has a weird node (if the mouse is over a text object,
// then there's no tagName)
while (myElement &&
((myElement.tagName && myElement.tagName!="TR") || !myElement.tagName))
{
myElement=myElement.parentNode;
}
if (!myElement || (myElement && myElement.id && myElement.id=="header") )
return;
if (myElement)
{
var tableRow=myElement;
if (tableRow)
{
savedStates[savedStateCount]=saveBackgroundStyle(tableRow);
savedStateCount++;
}
var tableCell=findNode(myElement, "TD");
var i=0;
while (tableCell)
{
if (tableCell.tagName=="TD")
{
if (!tableCell.style)
{
tableCell.style={};
}
else
{
savedStates[savedStateCount]=saveBackgroundStyle(tableCell);
savedStateCount++;
}
tableCell.style["backgroundColor"]=highlightColor;
tableCell.style.cursor='default';
i++;
}
tableCell=tableCell.nextSibling;
}
}
}
|
See the latest code: tableH.js
|
<< NEXT PAGE >>
|
|
<< MORE TUTORIALS >>
(C)2005 F. Permadi
permadi@permadi.com
Terms
of Use
|
|