Highlight Table Rows, Columns, and Cells Using MooTools 1.2.3

Row highlighting and individual cell highlighting in tables is pretty simple in every browser that supports :hover on all elements (basically everything except IE6). Column highlighting is a bit more difficult. Luckily MooTools 1.2.3 makes the process easy.

The XHTML

<table id="highlight-table">
       <colgroup></colgroup>
       <colgroup class="slim"></colgroup>
       <colgroup class="slim"></colgroup>
       <colgroup class="slim"></colgroup>
       <colgroup class="slim"></colgroup>
    <thead>
       <tr>
           <th></th>
           <th></th>
           <th></th>
           <th></th>
           <th></th>
       </tr>
    </thead>
   	<tbody>
   		<tr>
   			<td></td>
   			<td></td>
   			<td></td>
   			<td></td>
   			<td></td>
   		</tr>
   		<tr>
   			<td></td>
   			<td></td>
   			<td></td>
   			<td></td>
   			<td></td>
   		</tr>
		<!-- MORE ROWS -->
	</tbody>
</table>

A normal table. The cells being empty is inconsequential to the functionality -- place whatever you'd like in the table.

The CSS

table               { border-collapse: collapse; width: 100%; margin-top:10px; }
td                  { border: 1px solid #ccc; padding: 10px; }
thead               { width: 100%; height: 109px; background: url(header.png) no-repeat; }
.slim               { width: 88px; }
.column-hover		{ background:#eee; }
.row-hover			{ background:#ddd; }
.cell-hover			{ background:#fffea1; }

Note that I've created 3 separate hover classes -- one for each type of hover possible within the table.

The MooTools JavaScript Version 1 - Inefficient

	window.addEvent('domready',function(){
	// variables and settings
	var table = document.id('highlight-table');
	table.getElements('td').each(function(el) {
		var parent = el.getParent('tr');
		var siblings = parent.getElements('td');
		var index = siblings.indexOf(el) + 1;
		var childSelector = 'tr td:nth-child(' + index + ')';
		//add events to the table cell
		el.addEvents({
			mouseenter: function() {
				//this column
				table.getElements(childSelector).addClass('column-hover');
				//this row
				parent.addClass('row-hover');
				//this cell
				el.addClass('cell-hover');
			},
			mouseleave: function() {
				//this column
				table.getElements(childSelector).removeClass('column-hover');
				//this row
				parent.removeClass('row-hover');
				//this cell
				el.removeClass('cell-hover');
			}
		});
	});
});

The first step is grabbing every TD within the table. Iterate through that array of TDs adding mouseenter and mouseleave events to the element. The mouseenter method adds the necessary CSS classes, the mouseleave event removes them. The difficult part, in theory, is the column highlighting. Using a CSS 3 "nth-child" selector made the column-highlighting process easier than I had expected.

The MooTools JavaScript Version 2 - More Efficient

window.addEvent('domready',function(){
/*  METHOD 2:  Better  */
var table = document.id('highlight-table');
var rows = table.getElements('tr');

//for every row...
rows.each(function(tr,trCount){
	//we don't want the header
	if(tr.getParent().get('tag') == 'thead') { return false; }
	//add the row class to the row
	tr.addClass('row-' + trCount);
	//add the row listener
	tr.addEvents({
		'mouseenter': function(){
			tr.addClass('row-hover');
		},
		'mouseleave': function(){
			tr.removeClass('row-hover');
		}
	});
	//for every cell...
	tr.getElements('td').each(function(td,tdCount) {
		//remember column and column items
		var column = 'col-' + tdCount;
		var friends = 'td.' + column;
		//add td's column class
		td.addClass(column);
		//add the cell and column event listeners
		td.addEvents({
			'mouseenter': function(){
				$$(friends).erase(td).addClass('column-hover');
				td.addClass('cell-hover');
			},
			'mouseleave': function() {
				$$(friends).erase(td).removeClass('column-hover');
				td.removeClass('cell-hover');
			}
		});
	});
});	
});

This version is much faster because more of the work is done up front and done only once. I give all the proper class names at the beginning of the script and access the collections only when I need them.

A Few Thoughts

  • Remember that since every TD is analyzed, this process can be very taxing on large page.
  • You can use simple CSS :hover definitions for the row and cell highlighting in all browsers except IE6.
  • Nested tables will raise havoc on this system. The easy way to get prevent the problem is checking the element's TABLE parent ID to make sure it's the same as the initial table you wanted to highlight.
  • Since this was more of an experiment than anything, I skipped making this into a MooTools class. If anyone wanted to give that a go, I'd love to see what you come up with.

Great idea and execution by Chris. I am obligated to say MooTools FTW though!


Comments

  1. Adriaan

    Pretty cool, maybe with a little more subtle colour change, it will be very useful…

  2. Chris Bolson

    Great effect!

    Just an idea,
    Rather than doing this:
    //we don’t want the header
    if(tr.getParent().get(‘tag’) == ‘thead’) { return false; }

    wouldn’t it be better to define just the tbody rows like this:
    var rows = table.getElements(‘tbody tr’);

    Chris

  3. David Walsh

    @Chris Bolson: Yep, but that’s why the first code block is inefficient.

  4. Chris Bolson

    @David Walsh: :( how does that make it inefficient?
    As I understand it, the more concise you can be with the selectors, the faster it is as the javascript doesn’t have to check unneeded elements.

  5. David Walsh

    Chris Bolson: Oops, my bad — I thought you were citing the code from my “bad” example. My apologies. Clever point on your part — I’ll try that out!

  6. Chris Bolson

    @David Walsh: hehe.
    And whilst you are there, if you want to save another line of code you could define the variable “row” like this:

    var rows = document.id(‘highlight-table’).getElements(‘tbody tr’);

    I don’t think that that makes it any more efficient, just saves defining the variable “table” (which you don’t use)

    Anyway, I realise that this is just a demo ;)

  7. Darkimmortal

    Could probably be quite a bit faster without the recursion ;)

  8. derschreckliche

    Hey, nice approach!
    Could be very useful!

  9. Jesus DeLaTorre

    I was going to suggest that you cache the tr elements and then noticed you did this in the more efficient method. :) Good job. I definately like the second version a lot better as well.

  10. Chris the Developer

    how about this approach?

    window.addEvent(‘domready’, function() {
    var columns = [], rows = [], cells = [], length = 0,
    table = document.id(‘highlight-table’);

    rows = table.getElements(‘tbody tr’);
    cells = $$(rows.getElements(‘td’));
    length = rows[0].getElements(‘td’).length;

    for (i = 0; i < length; i++) {
    columns[i] = table.getElements('td:nth-child(' + (i + 1) + ')').set('data-column', i);
    };

    for (j = 0; j < rows.length; j++) {
    rows[j].getElements('td').set('data-row', j);
    }

    cells.addEvents({
    'mouseenter': function() {
    columns[this.get('data-column')].addClass('column-hover');
    rows[this.get('data-row')].addClass('row-hover');

    this.addClass('cell-hover');
    },
    'mouseleave': function() {
    columns[this.get('data-column')].removeClass('column-hover');
    rows[this.get('data-row')].removeClass('row-hover');

    this.removeClass('cell-hover');
    }
    });
    });

  11. wguru

    Likely this’s a site where advanced users get together and swap stories and tips, but in this article, while it seems to relate to what I’m seeing, might someone here educate the common user as to how (aside from DOS or injections of script or code) most users can configure what gets highlighted in Windows folders? Aside from hovering and automatically highlighting a file name, assumably assumedly, crap however you spell, where/how it is that can configure how single clicking a file name only hightlights the file name and not the entire row of details?

    One of my os’s (XP SP3′s) only highlights the file name, while annoyingly my other os (Vistas’s) highlights the entire row of columnized details for the chosen file name.

    That latter, highlighting all details, is a bit of an annoyance because it forces users to expand windows enough to extend beyond the last column so the user can click outside the columns in order to click-hold and drag a capture box for a group of files. Likely it doesn’t require learning DOS to change this behavior, so where does the common user configure what gets highlighted (just the file name as opposed to it and all it’s details)?

    Please don’t ask me if I want wine with that cheese.

  12. Chris the Developer

    @wguru – You speak of things from the realm of desktop…You should consult the arcane wizard known only as goo-Gle. He knows the secret things of the enemy. He will be your weapon at the hordes.

  13. Casey

    Hello,

    First, want to tell you I love this code. I’m using it to make a military pay chart:

    http://militarytips.net/military-pay-chart-2010/

    Can you tell me how to do this with multiple tables on the same page? I tried it, but only the first table works. My plan is to copy three versions of the code and rename “highlight-table” in each one. But I know there’s got to be an easier way! What I want to do is divide out the table into Officer, Warrant Officer, and Enlisted.

    Thanks,
    Casey

  14. justin

    He David,

    I want to use this great thing in a “tabbed content” with 3 different tabs and put one table in each tab.
    The problem hereby is that it only highlights the first table in the first tab and the other ones aren´t highlighted, unfortunately.
    I noticed that the table “highlight-table” is an id, what tells me that it can only be used for one time on the same page.
    So my question: how can I use the effect, by defining the entire table “highlight-table” as a class, rather than an id???

    Thanks a lot! :) Hit me up, if you need to see an example of what I mean.

    Cheers,
    Justin

  15. Josh

    Hi David -
    Thanks for the great coding. I just launched a microsite that makes use of your work (with a few newbie-level modifications). You can see it here: http://www.crittervidder.com.
    Thanks again!
    - Josh


Be Heard!

Share your thoughts without being a jerk! And wrap your code in <code> tags, f00!

Name*:
Email*:
Website: