Highlighting rows of a table is pretty darn easy in CSS. tr:hover { background: yellow; }
does well there. But highlighting columns has always been a little trickier, because there is no single HTML element that is parent to table cells in a column. A dash of JavaScript can handle it easily, but Andrew Howe recently emailed me to share a little trick he found on StackOverflow, posted by Matt Walton.
It was a few years old, so I thought I’d just clean it up and post it here.
The trick is using huge pseudo elements on the <td>s, hidden by the table overflow
You don’t really know how big the table is from CSS, so just make the pseudo elements super tall with a negative top value of half of that.
table {
overflow: hidden;
}
tr:hover {
background-color: #ffa;
}
td, th {
position: relative;
}
td:hover::after,
th:hover::after {
content: "";
position: absolute;
background-color: #ffa;
left: 0;
top: -5000px;
height: 10000px;
width: 100%;
z-index: -1;
}
The negative z-index
keeps it below the content. Negative z-index
is a fun trick, but beware this table then can’t be nested within other elements with backgrounds, otherwise the highlight will go below them.
You can see that in action here:
See the Pen Row and Column Highlighting with CSS Only by Chris Coyier (@chriscoyier) on CodePen.
Making it work with touch
Hover pseudo classes only kinda work on touch devices. First, the element needs to be focusable, which table cells are not by default. You could certainly add a click event handler to the table cells and just do everything in JavaScript, but here’s a method to keep most of the work in CSS:
// Whatever kind of mobile test you wanna do.
if (screen.width < 500) {
// :hover will trigger also once the cells are focusable
// you can use this class to separate things
$("body").addClass("nohover");
// Make all the cells focusable
$("td, th")
.attr("tabindex", "1")
// When they are tapped, focus them
.on("touchstart", function() {
$(this).focus();
});
}
Then in the CSS you add :focus styles as well.
td:focus::after,
th:focus::after {
content: '';
background-color: lightblue;
position: absolute;
left: 0;
height: 10000px;
top: -5000px;
width: 100%;
z-index: -1;
}
td:focus::before {
background-color: lightblue;
content: '';
height: 100%;
top: 0;
left: -5000px;
position: absolute;
width: 10000px;
z-index: -1;
}
In my final demo, I got a little fancier with the CSS selectors ensuring that empty table cells didn’t trigger anything, table headers in the <thead>
only selected columns, and table headers in the <tbody>
only selected rows.
You can see that in the final demo. And here’s touch working:

This is a great. It will help me with some larger tables were being able to highlight rows and columns will make reading the tables much easier… code saved for future projects, thanks for posting.
I remember reading that using huge divs can cause a performance hit even if hidden via overflow, do you know any details on that? Like the old image replacement text-index: -99999px; trick?
That is ‘SUPER’ neat. I will be using this on my site. Thank you. Keep up the excellent work
There’s a new column selector coming in CSS 4 that will allow this to work ‘natively’: https://grack.com/blog/2015/01/11/css-selectors-4/#column-combinator–and-nth-column
Browser support right now is zero, but fingers crossed it’ll be coming soon.
Yeah, I think you can just use colgroup in the HTML for this as well.
Reading this post: Only just realised the absolute positioning within a td in FF bug was fixed. This is a nice thing to rely on in future.
Really?! Good gracious, about time! :D It’s been, like, a 15 years old bug, back in Netscape!
Wouldn’t use this trick as the w3c clearly states:
“The effect of ‘position:relative’ on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined.”
It may be undefined as per spec, but more or less every vendor now agrees that it should define the element as the offset parent at least.
So you can absolutely position elements inside a cell! Yay!
Pseudo elements to the rescue…again!
Good to keep in mind next time I’m needing to jazz up a table.
I agree, pseudo elements are incredibly powerful. I’m creating all sorts of magic with them. (And it’s something that I learned here! So thanks for that.)
I just wonder that if you are puting there some js because of touch devices, you can then use js entirely as it is then one row JS solution and doesnt need css hacking, something like
and
Isn’t there a more accurate trigger for touch?
screen.width < 500
seems like a “dirty” check.I think that’s a fun thing to think about. From what I’ve heard “it’s impossible to test for touch”, so I maybe it’s a fools errand? Screen width doesn’t seem too awful to me, since: are there any relevant examples of screen that small that dont’ support touch? And in those cases of hybrid devices, it’s probably no big deal they don’t get the touch functionality here as it’s non-critical (in this totally hypothetical situation).
Best way to detect actual presence of touch is imho to just listen for a touchdown event in the capturing phase:
This works well enough for anything that does not have to engage touch-specific logic until a user starts interacting with content.
I think even trying to handle touch differently to mouse or any other input method is the wrong approach. What about devices that support both? On a touch notebook I might use the mouse but to point to something in a big table (as in this example) might use the finger. Of course this mostly is interesing for Windows devices as obviously OSX does not support touch (screens). But even on e.g. Android tablets with connected mouse this is an issue.
I guess starting on Pointerevents is a lost cause as Google hates them but that would be one point to use them (not sure how but that is not my point).
Anyway, I hate stuff that works on desktop but does not on e.g. my Surface. Even minor but nice stuff like this demo.
Sorry, but the touch hack makes it even more insulting :(
You can use Modernirz test -> Touch Events
if (Modernizr.touch) {
alert(‘Touch Screen’);
} else {
alert(‘No Touch Screen’);
}
http://modernizr.com/docs/#features-css
At the download page make sure touch event option is selected.
Great article!
$(this).focus()
→this.focus()
Its really awesome, Does it possible to apply this trick with existing wordpress table plugin CSS files?
Have you done any research into whether those really huge pixel sizes on top and height create any performance issues? Or are the browsers smart enough to just run the math, figure out the rendered shape, and go from there?
Awesome share. Thanks!!!
How can i highlight only row?
To highlight only a row put a background color on
tr:hover
:Demo
@Myriam @R. Otten
even if you know the device supports a touch screen you do not know if he actually uses it or a mouse, a keyboard or speech or whatever. So you have to be careful with this. I’ve seen a lot of sites “working” on touch and failing as soon as I use a mouse on the same device which is sooo annoying
You can also use
colgroup
and a tiny bit of jQuery to accomplish the same thing (demo)HTML (add a
colgroup
for each column)Script
A simple fix for this is to specify a
z-index
for the table.Demo
Interesting point on this – I was struggling to get the column pseudo element to show up until I removed the z-index. After that I had another issue of the row not updating when moving the hover vertically upwards, over the column pseudo element (you could move down a column with the row matching your position but when you moved back up the column the row wouldn’t update)
Eventually I had the thought to include ‘pointer-events:none’ to allow hovers through the element.
(http://codepen.io/joshharrington/pen/gbjyNZ)
Then you just have to fix how you show the content through the column!
This only seems to work for me if there is not background color specified, even on the page.