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.

Oct 22, 2008: New version of example 9 now using jQuery. See example 10 and the article describing it.

This article was ported from an older CMS, and comments were purposefully lost in the migration. If some were found most relevant, I pasted them to the bottom of that post. No new comments are accepted on these older articles.