Highlight Table Rows, Columns, and Cells Using MooTools 1.2.3

By  on  

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!

Recent Features

  • By
    Write Better JavaScript with Promises

    You've probably heard the talk around the water cooler about how promises are the future. All of the cool kids are using them, but you don't see what makes them so special. Can't you just use a callback? What's the big deal? In this article, we'll...

  • By
    CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

Incredible Demos

  • By
    MooTools TextOverlap Plugin

    Developers everywhere seem to be looking for different ways to make use of JavaScript libraries. Some creations are extremely practical, others aren't. This one may be more on the "aren't" side but used correctly, my TextOverlap plugin could add another interesting design element...

  • By
    Add Styles to Console Statements

    I was recently checking out Google Plus because they implement some awesome effects.  I opened the console and same the following message: WARNING! Using this console may allow attackers to impersonate you and steal your information using an attack called Self-XSS. Do not enter or paste code that you...

Discussion

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

  2. 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. @Chris Bolson: Yep, but that’s why the first code block is inefficient.

  4. @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. 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. @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. Hey, nice approach!
    Could be very useful!

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

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!