Ask the CSS Guy

Row Locking with CSS and JavaScript

I was recently asked about the effect used to highlight a table row with a mouse click. This is different than highlights while hovering, as the clicked row should stay highlighted until clicked again. To differentiate this effect from 'row highlighting', I'll refer to it here as 'row locking', since the effect stays in place until the user re-clicks (or unlocks) the row.

This effect is not achieved by CSS alone, but by a combination of CSS and JavaScript. While I'm confident in CSS, I'm quite a novice at the latter, which I'm sure will become evident by the end of this post. However, after reading Jeremy Keith's DOM Scripting, I feel I have the tools to tackle the matter and come up with a possible solution. Much of the code I use to achieve the effect is directly derived from code examples in Keith's book.

An Aside

Jeremy Keith's DOM Scripting is a must for the XHTML and CSS designer who has put off learning javascript. I realized that as a web designer, I might be able to get away with not knowing PHP, mySQL, and other back-end stuff, but there's no reason to limit myself to just two-thirds of front-end web design. Copying and pasting from prototype/scriptaculous/moo.fx/etc. is no substitute for learning how to do this kind of stuff, some of it very simple stuff, on my own. I appreciate Keith's book for making the remaining third element of front-end web design so approachable.

Why?

Before going further, it might be good to mention why the effect may be useful.

  • If you're scanning over a long table, it's nice to mark a row to help guide the eye
  • If you wanted to compare two rows in a table, but they were several rows apart, highlighting each row could serve as a handy helper.
  • I noticed my Movable Type system panel uses this effect in the comments review section, and that the highlighted row also serves as a label for a checkbox on that row (handy stuff), although they are probably using a different method to achieve the effect.

The Blank Slate

I assume that a table that could use this effect would already be using table striping (making every other row a different color). So my 'blank slate' will be table with some basic data with no classes or style attributes in the xhtml. I'll use a very simple script from Jeremy Keith's book for striping the table (with a minor tweak so that it only stripes rows in the <tbody> tag, not the <thead> tag). The function works by attaching a class of row="odd" to every other row on page load.

View Example 1: The Blank Slate.

Row Locking Successfully Achieved

Now that I have a table to work with, it's time to decide how to achieve the row-locking effect. In DOM Scripting, Keith offers an example of code to achieve row highlights with a function called 'highlightRows', which uses onmouseover to change the class (and consequently, the color) of the row when the mouse moves over the table. If I can modify that script to use onclick instead, that might be all that is necessary to achieve row locking. With some minor tweaking, I ended up creating the function 'lockRow', which worked. On click, it adds a class (using addClass) to the row called 'selected'.

View Example 2: Row Locking Successfully Achieved.

Doing More

I had mentioned that I modified Jeremy Keith's 'highlightRows' function to create the 'lockRow' function. Ideally, I would like to use both effects.

To see the 'highightRows' function in action (without row locking), view Example 3: Row Highlighting. This script attaches a class of 'highlight' to any row that is hovered over.

Unfortunately, to combine 'highlightRows' and 'lockRow' in one table is not as easy as just placing all the functions on the same page.

View Example 4: Attempt at Using Both lockRow and highlightRows Not Working So Well.

I think I need to change the 'highlightRows' function to be conditional, so that it would only work on rows that wouldn't have the 'selected' class. My attempts at achieving this have turned up unsuccessful. (Update Dec 22, 2006: Thanks to simu in the comments, there is now a working example - see end of post.)

For instance, here's the 'highlightRows' function without modification:

function highlightRows() {
   if(!document.getElementsByTagName) return false;
   var tbodies = document.getElementsByTagName("tbody");
   for (var j=0; j<tbodies.length; j++) {
      var rows = tbodies[j].getElementsByTagName("tr");
      for (var i=0; i<rows.length; i++) {
         rows[i].oldClassName = rows[i].className
         rows[i].onmouseover = function() {
           addClass(this,"highlight");
         }
         rows[i].onmouseout = function() {
           this.className = this.oldClassName
         }
      }
   }
}

Here's what I thought would make the function only work:

function highlightRows() {
   if(!document.getElementsByTagName) return false;
   var tbodies = document.getElementsByTagName("tbody");
   for (var j=0; j<tbodies.length; j++) {
      var rows = tbodies[j].getElementsByTagName("tr");
      for (var i=0; i<rows.length; i++) {
         if (rows[i].className.indexOf("selected") == -1) {
            rows[i].oldClassName = rows[i].className
            rows[i].onmouseover = function() {
               addClass(this,"highlight");
            }
            rows[i].onmouseout = function() {
               this.className = this.oldClassName
            }
         }
      }
   }
}

But that doesn't do the job, so I'm kind of stumped at the moment. (Who's going to start the 'Ask the JS Guy' web site?)

Another thing I found myself wanting to do is use shift+click to highlight several rows in a row. I'm not sure why I'd want to do that with my simple table example, but for some (like in the Movable Type checkbox scenario), it could be helpful. Right now, I'm not certain how to approach that one, either.

Dec 22, 2006: Simu (first comment below) has offered a modified highlightRows() function that allows for both the highlightRows() and lockRow() functions to co-exist and work together. (Thanks!) The javascript is very clear. View Example 5: Revised highlightRows JavaScript - lockRow and hightlightRows together a Success!

Dec 26, 2006: Tom pointed out my link to example 5 was in fact still pointing to example 4. It's been corrected now - many thanks to Tom. Sorry for any confusion to others.

Feb 5, 2007: Kenny asked of a way to apply these effects to only tables of a certain class, which is a very good idea. View Example 6: Targeting tables with a specific class.

Feb 6, 2007: Raj asked about having, among other things, a checkbox for each row. Great ideas. View Example 7: Adding checkboxes to each row.

May 14, 2007: On my example with checkboxes, clicking a row would check the corresponding checkbox, but if I actually clicked on the checkbox, it was havoc. I toyed around with cancelBubble and stopPropagation, and ended up with a working example 8, which has an additional function to fix the issue.

Aug 19, 2007: Locking rows with radios is now working. See example 9 to see it in action and view source/copy.

Comments (40)

simu said:

to get this thing working you should check for the availability of the class "selected" in the event-functions rather than before assigning them... e.g.

function highlightRows() {
if(!document.getElementsByTagName) return false;
var tbodies = document.getElementsByTagName("tbody");
for (var j=0; j var rows = tbodies[j].getElementsByTagName("tr");
for (var i=0; i rows[i].oldClassName = rows[i].className
rows[i].onmouseover = function() {
if( this.className.indexOf("selected") == -1)
addClass(this,"highlight");
}
rows[i].onmouseout = function() {
if( this.className.indexOf("selected") == -1)
this.className = this.oldClassName
}
}
}
}

CSS Guy said:

@simu:

Thank you immensely! The revised javascript works and makes perfect sense. I've updated the post with a new working example. Much appreciated.

Tom said:

Just a note to let you know that the link for example 5 actually goes to the Example 4 page. I clicked on the Example 5 link a couple of times but each time I was directed to Example 4. Seeing the page name was examples/rowlock/example4.html, I changed the name in the location bar to examples/rowlock/example5.html, and finally was able to view the example 5 solution. I suggest you fix the link so others will be able to see the example 5 page via the link.

Great post otherwise.

-- Tom

CSS Guy said:

@Tom

Many thanks - I've now fixed the link. May your divs float both left and right this holiday season.

jd said:

what if you want to select only 1 row at a time.
meaning , select row, select another row, the previous row is not highlighted.
i do not know much js/css but starting to like it.
thanks
jd

Richard said:

When hovering over, try this nice hand icon instead of the standard I of edit :)

function highlightRows() {
if(!document.getElementsByTagName) return false;
var tbodies = document.getElementsByTagName("tbody");
for (var j=0; j var rows = tbodies[j].getElementsByTagName("tr");
for (var i=0; i rows[i].oldClassName = rows[i].className
rows[i].onmouseover = function() {
this.style.cursor='hand'
if( this.className.indexOf("selected") == -1)
addClass(this,"highlight");
}
rows[i].onmouseout = function() {
if( this.className.indexOf("selected") == -1)
this.className = this.oldClassName
}
}
}
}

Kenny said:

Very very cool. I've been banging my head against this problem for a long time. THANKS!

One question though.... how would i modify the js code so that it would only apply to a specific class instead of the default table class?

CSS Guy said:

@Kenny:

Good idea. See example 6 (the link is appended to the post above) to see how to target tables of a certain class.

raj said:

How to lock single row only ,
what if table having input elements like radio button,checkbox

CSS Guy said:

@Raj:

I've got the checkbox version set up (see example 7). Radio version to come soon.

raj said:

Thanks for ur replay
How to lock single row only in entire table,
I mean to say if user click on a row,
Previous selected rows not highlighted
Highlight only current selection row

CSS Guy said:

@Raj:

Working on it. It's turning out to be more challenging than I initially thought, moreso because of the striping. If someone else figures this out first, please notify!

nikosch said:

nice effects, unfortunately the checkbox itself doesn't work. but that should be easy to fix.

Veena said:

I used this code.It is working fine. But the problem is if I put my table inside 'div' tag and added position 'relative' for first row to freeze the first row,
when I scroll one page down through data, contents in first row(under thead) disappears.
When I scroll up and click on any row those contents appear again.
I tried a lot but could not solve this issue.
Can you suggest me any way to resolve this issue?
Thanks.

Regards,
Veena

CSS Guy said:

@Veena,

Can you post a link to the page that's having the problem?

sakthi said:

Hey guys,

I am amazed on seeing the examples above. Not sure can I go ahead and implement page on which I have multiple tables. What do you say on that....

CSS Guy said:

@sakthi:

Does example 6 not take care of that? Just attach a class to each table for which you want the stripe effect to occur.

ak said:

Do you have any solution on the "radiobutton" problem? I want only one row to stay selected since I am using radio-buttons instead. Can you please help me with this? Nice scripts!!

shweta said:

hi,
plz put in some help to achieve this same row locking on checkbox click in div. ie. table less layout... wud be gr8!!

Dhiraj said:

Hi there,

Wonderful work, has been looking for this from a long long time, thanks a bunch for uploading the stuff... keep up the good work.

Regards
Dhiraj

Alex said:

I have multiple Tables in a page, but need to fix the column of only one table. Any luck?

thanks

Joe said:

I'm new to javascript and was curious if there was a way to highlight rows that are selected as default and apply the highlight/select to those rows as the form loads? (along with all the other cool stuff this jscript code accomplishes )

Thanks!

peter said:

great script, just what I was looking for. Another cool feature would be column highlightning so it would highlight like a cross

Nihar Sahoo said:

I need some help on row highlighting. I have requirement is like when I come first time to page the first row of the table should highlight. Then when I click on any row of the table the page will load and accordingly that row will highlight and the previous highlight row will go back to its original color. I am using jsp and java script. I couldn't able to do this.

Can you please help me on this? Will be grateful if you have any sample code for this? This is really Urgent for me.

chrisp said:

I have made a slight modification to your script so it automatically works on lists of checkboxes or radio fields. Basically just combined both the radio and checkbox scripts...

Great work by the way thank you...

You can view the updated script via the following link: http://jellyfishit.co.uk/highlight_checkbox_and_radio.txt

IWBYTE said:

Wonderful! I haven't read thru the comments, but I don't hink you can do more than one table on a page, tho :( Or am I wrong/is there an easy parameter to add?

canli tv izle said:

plz put in some help to achieve this same row locking on checkbox click in div. ie. table less layout...

IWByte said:

Well, now i've read thru all the comments, and while option #6 does allow for multiple tables, it doesn't allow for multiple tables to be used with row-locking on EACH of them :(

Gabi said:

Hi, nice script.

I wanted to use it in a table where on one row I have a check box and a button. The check box gets selected ... but the functionality of the button is compromised ...

Eric P said:

Great article!

I'm using your technique served up as an AJAX, dynamic div tag. Onload would not work for me because of the mixed tags/frames being loaded; however, calling the functions within the tr tag works well, regardless of onLoad issues:

<tr onmouseover="highlightRows(); lockRow();">

MaryV said:

this is great code - thanks! is there a possible way for this situation: i have 2 tables - the first is loaded with rows. based on which row is selected, the bottom grid is filled. i need a postback to fill the bottom grid with it's data. but when that happens, i look the "selected" from the top grid - is there anyway to "persist" or "reset" the selected row after the postback? that would be sweet.
thanks,
MaryV

MaryV said:

saw my typo - should be "lose" the selected from the top grid...thanks.

MaryV said:

haven't heard from anyone...anyone???

MaryV said:

haven't heard from anyone...anyone???

Chris said:

Hello and thank you for the excellent example and explanation.

I'm using the code behind example 6 and everything is working fine except that the background of the row does not get highlighted. Only the font color is changed.
I have this for the css:


tr.selected {background:#D7D7D7; color:#FFFFFF}

Thanks for any help.

Metehan said:

How about if i like to lock one row out of time. When i click a row, that row gets locked change color, one row only.

CSS Guy said:

@metehan,

See example 9, working with radio buttons. Forgive me if I don't explicitly tell you how to do it - I think that's what forums are for. Instead, I should hope you'd want to dive into the javascript and find out how it works in order to engineer it to your own needs.

Marco said:

New to CSS here, and I have a strange request from the client, they would like to be able to lock the last nth columns of a table. I figured out how to do the first 2 columns using CSS but doing the last one or ones has me confused. Any suggestions?

/* Locks the left column */
td.locked, th.locked
{
/* border-right: 1px solid silver; */
position:relative;
cursor: default;
/*IE5+ only*/
left: expression(this.parentElement.parentElement.parentElement.parentElement.scrollLeft-2);
}

Thanks in advance
Marco

bestajaxscripts said:

Like the site very much, thanx 4 your efforts webmasters

JoshuaG said:

Would it be possible for you to recreate this with jQuery and see how many lines of JS you can save in the process?

Thanks!

 

Post a comment