Grow your CSS skills. Land your dream job.

Table Row and Column Highlighting

Published by Chris Coyier

In mid-2009 I did a screencast about tables which featured how to do row and column highlighting with jQuery. The method in the video isn't nearly as efficient as it could be, so this is an update to that. Shout out to Bronislav Klucka who contacted me about the idea.

Attaching events to table cells is the quintessential example, and also read like a history for event handling in jQuery.

  • Original demo (version < jQuery 1.3 ) - Using .hover() on every single <td>
  • Live demo ( jQuery 1.3 <= version < jQuery 1.4.2 ) - Using .live() on <td>'s
  • Delegate demo ( jQuery 1.4.2 <= version ) - Using .delegate() on <table>

HTML of typical table

Let's assume this markup. There are five columns, and so there are five <colgroup>s. Colgroups allow us to style an entire column without needing to manipulate individual table cells (as table cells in a particular column share no common unique parent).

<table>
    <colgroup></colgroup>
    <colgroup></colgroup>
    <colgroup></colgroup>
    <colgroup></colgroup>
    <colgroup></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>
    		<tr>
    			<td></td>
    			<td></td>
    			<td></td>
    			<td></td>
    			<td></td>
    		</tr>
    	</tbody>
</table>

CSS

Nothing much to see here, just that we will have a class name ready to handle the actual styling of "current" rows and columns.

.hover { background-color: #eee; }

It's best to handle the actual styling information through CSS rather than directly in the JavaScript.

You gotta keep 'em separated

- Offspring

Best Method: Delegate

For jQuery 1.4.2+, the best bet is going to be delegate. Here is the two second overview. You attach a single event listener (efficient!) to the table itself specifying which elements qualify. If anywhere inside the table is clicked, it checks if that click was inside one of the specified elements. If there is a match, it fires the function.

$("table").delegate('td','mouseover mouseout', function(e) {
   // do stuff
});

Our "do stuff" is applying a class name to the row and colgroup, when they are "hovered" over only. "hover" isn't a real event though, the real events are mouseover and mouseleave. Delegate will take a space-separated list of events. Within the function we can test which type of even was fired and act accordingly.

$("table").delegate('td','mouseover mouseleave', function(e) {
    if (e.type == 'mouseover') {
      $(this).parent().addClass("hover");
      $("colgroup").eq($(this).index()).addClass("hover");
    }
    else {
      $(this).parent().removeClass("hover");
      $("colgroup").eq($(this).index()).removeClass("hover");
    }
});

Since delegate is watching the entire table, if new table rows/cells are added dynamically later, they will still behave with this code.

Still good method: Live

Post jQuery 1.3, but pre jQuery 1.4.2, the best method for event handling in a situation like this was .live(). Remember how with delegate we watched the table itself for the specified events? With live, we watch the entire <body> for those events. This has the distinct benefit of allowing new rows/cells to be added dynamically without the need to re-attach events, but with less efficiency than delegate.

However it should be noted that should entire new table be added to the page dynamically, live would continue to work while delegate would not, as the new table would not have delegate attached to it.

$('td').live('mouseover', function(){
    var i = $(this).prevAll('td').length;
    $(this).parent().addClass('hover')
    $($cols[i]).addClass('hover');

}).live('mouseout', function(){
    var i = $(this).prevAll('td').length;
    $(this).parent().removeClass('hover');
    $($cols[i]).removeClass('hover');
});

The old and kinda bad method

Before we had access to live or delegate, or only method was to bind the events directly every single table cell. jQuery still had a helper function for it, but it still meant adding an event listener for every single solitary cell (inefficient!, e.g. lots of browser memory usage). Not to mention that if new table rows or cells were added dynamically, they wouldn't have any of the events attached them that their brethren do.

$("td").hover(function() {
   // do stuff
}, function() {
   // do stuff
});

If you'd like to see all the old nastiness, it's here.

Demo and Download

  • Original demo (version < jQuery 1.3 ) - Using .hover() on every single <td>
  • Live demo ( jQuery 1.3 <= version < jQuery 1.4.2 ) - Using .live() on <td>'s
  • Delegate demo ( jQuery 1.4.2 <= version ) - Using .delegate() on <table>

Download Files

Have a more complex table?

With colspans and rowspans and whatnot? Gajus Kuizinas's Wholly project might be able to help you with highlighting in those cases.

Comments

  1. Mateus

    Simple, efficient and elegant.

    Thank you.

  2. This decision to update many of your posts is great. Makes the site into more of a resource rather than a blog.

    Thanks,

    @Prydie

    • ymo

      Totally agree with Andrew; specially bringing jQuery on board to solve or optimise old issues. Well thought and thanks!

  3. Mike Dedmon

    Interesting how after you’ve hovered over the tr, jQuery the leaves the empty class=”” in the markup. I can’t see that this is any type of problem. It just seems that maybe jQuery isn’t cleaning up completely. Or is that best practice?

    curious…

  4. Steven Schrab

    I like the Offspring shout out.

  5. That is a great rewrite sir. Thanks.

  6. Nice work done Chris..thanks…

  7. Greg

    Chris,
    Would you have any metrics that show how much each method is improving on the previous one?
    Greg

  8. Simple and easy way thnks

  9. Excellent rewrite. I was unsure what the pros and cons of delegate vs. live, and this article illuminates some of them.

    Damien – not sure if IE7 supports :hover on non-link elements, but I know IE6 does not.

  10. very effective tutorial..thanks for post..

  11. Dalibor Sojic

    It would be nice with each example to “measure” usage, execution time and so on (firebug console)

    Sometimes “old bad” methods use less resources.

    For example… one of my tests…. using jquery accordion take around 17ms for init… and same effect “manualy coded” 0.2ms.

  12. @Mike Dedmon

    That’s exactly what removeClass does. It removes the CSS class. Nothing more, nothing less!

    In this case it is “hover”.

  13. tables?

  14. Wow

    That’s great

    Thanks

  15. Merci :)

  16. Colwyn

    Great Chris…

    Thanks…

  17. Delegate is nice!

  18. Mike

    Am I doing something wrong? I just see a table with no hover effects for all three demos. Running latest Firefox on XP pro.

    • Mike

      OK, weird. It works fine on my computer at home. Not sure what is up with my computer at work.

  19. Mike

    One more question. How do some users get their picture next to their post?

  20. In Opera 10.52, only the row is highlighted, not the column.

    It’s easy to blame Opera, and it might well in fact BE Opera, but I just thought I’d mention it.

    Still interesting to see “delegate” explained the way it was explained here. jQuery APIs is well written, but somehow it usually takes a 3rd-party explanation before I really “get it”.

    • Yup, replying to myself. ;-)

      This row+column table (an em conversion chart for interested parties!) does both:

      http://aloestudios.com/tools/emchart/

      However, it’s using a custom “pure” Javascript library, not jQuery. So I’m not 100% sure how useful it would be as an example of something that works in Opera in the context of a jQuery article.

  21. Good, will like to try any any dynamic content website……

  22. Chris,

    You are a real source for inspiration. It never fails…every time I read one of your articles it relates to something I’m doing – somehow. And some sort of light bulb always goes on.

    You rock, sir. Seriously.

    Scott

  23. Ben

    Cool stuff.

  24. How can this still be accomplished without using colgroups?

  25. Ankur
    Permalink to comment#

    Very Nice

  26. Mohammad Faisal
    Permalink to comment#

    nice example.
    But I do not want to show background on mouseover for the first th and the first td of last tr.
    Can there be a solution?

    • Giovanni Crisci wrote in with this:

      if you want to avoid highlighting the first column just add
      && $(this).index() > 0
      to your if clause, e.g.
      if (e.type == ‘mouseover’ && $(this).index() > 0)

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".