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>
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.
Simple, efficient and elegant.
Thank you.
As always ;)
Chris doing a good work!
As usually!!
I agree man, I look forward to using this on one of my websites. Should be yet another cool edition I got from css tricks.
This decision to update many of your posts is great. Makes the site into more of a resource rather than a blog.
Thanks,
@Prydie
Totally agree with Andrew; specially bringing jQuery on board to solve or optimise old issues. Well thought and thanks!
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…
I like the Offspring shout out.
That is a great rewrite sir. Thanks.
Nice work done Chris..thanks…
And If you want just row highlighting you can do that with just CSS and no jQuery.
tr:hover { background-color: #444; }
[)amien
Could you use tr:hover alongside the jQuery, and do away with the extra line in the function? Would that bring much of an improvement?
This method may be easy but it doesn’t work in ie6 and is very inefficient, more info can be found here
http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors
Chris,
Would you have any metrics that show how much each method is improving on the previous one?
Greg
Simple and easy way thnks
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.
very effective tutorial..thanks for post..
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.
@Mike Dedmon
That’s exactly what removeClass does. It removes the CSS class. Nothing more, nothing less!
In this case it is “hover”.
tables?
Wow
That’s great
Thanks
Merci :)
Great Chris…
Thanks…
Delegate is nice!
Am I doing something wrong? I just see a table with no hover effects for all three demos. Running latest Firefox on XP pro.
OK, weird. It works fine on my computer at home. Not sure what is up with my computer at work.
One more question. How do some users get their picture next to their post?
Your email address is used using gravatar system (see http://www.gravatar.com). Your picture will them show up on every site you use
Aw, thanks sage.
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.
Good, will like to try any any dynamic content website……
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
Cool stuff.
How can this still be accomplished without using colgroups?
Very Nice
nice example.
But I do not want to show background on
mouseover
for the firstth
and the firsttd
of lasttr
.Can there be a solution?
Giovanni Crisci wrote in with this: