Bob The Geek | Web Development For Fun & Profit

Mar/10

4

Easy jQuery Locking Table Headers (They even Toggle!)

Hello, my fellow geeks! Bob the Geek here, bringing you more of that sweet, sweet web dev goodness.

On a recent project I had a need to create a control that would allow me to lock and unlock the HTML table header, at will. Thinking this had to be a common requirement, I opened a shiny new Google page and began to search. Surprisingly, while I found a number of posts asking how to do this, I could find nothing in the way of a solution.

Actually, that is not entirely true. The solution I eventually came up with is based, in part on this post by Stu Nicholls over at CSS Play.

I took Stu’s trick, based on CSS, and combined it with some nifty jQuery to achieve the final result. If you want to just skip to the good stuff, you can see the complete, working example here.

“The Setup” or “Kiss’s Revolving Door Policy”

For our example, let us say we are creating a report that details all the members of the band, Kiss, since they were formed (and that is no small feat, I can assure you). Given our marching orders, we create the following table:

Kiss: The Long And Drawn Out Story

Kiss: Behind The Makeup

Here is our exciting, and informative, tableĀ  detailing Kiss’s high turnover rate. Here is the complete HTML for the table and its surrounding tags: Easy jQuery Locking Table Headers – List 1.

There a couple of important things to notice about this code:

The first, is that we have wrapped our table in two DIV tags, to which we have applied the CSS classes “outer” and “inner”, like so:

<div class="outer">

 <div class="inner">
 ...table HTML...

The outer DIV will serve as the container for our table header, which we will absolutely position when the locking header feature is turned on, while the inner DIV will serve to contain the rest of our table data and act as the scrollable container.

Also, notice that the first table row, contained within our THEAD tag, has a class of “header”.

Next, we need to take a look at the CSS stylesheet info we have setup. You can view that here: Easy jQuery Locking Table Headers – Listing 2.

Notice that while our table is set to a width of 800px, we actually set the width of the outer and inner DIVs to 820px and 818px, respectively. We do this to accommodate the extra space used by the scroll bar. (You could actually make both 820px. I just made the inner DIV slightly less to make sure that my scroll bar appears right up against the right side of my table. I am anal that way) If you made the width the same as the table, when the user activated the locking header, they would get a horizontal scroll bar at the bottom of the table and the header would look “off”.

Also, you can see that we set our outer DIV to position: relative. We do this because when the locking header is turned on, we will set its CSS position property to “absolute”. This means it will be absolutely positioned within the first parent container that has a position value other than static (the default).

Additionally, by setting the overflow attribute of the inner DIV to “auto”, we are essentially telling it that we don’t it to scroll and that it should expand to fit whatever content we fit into it, which is our default behavior.

Finally, we need a switch for our locking header magic. In this case, I have an icon that I place in the left-most column of the table header. In it’s initial state, the icon appears faded and transparent, to indicate that the feature is currently turned off. When the user activates the locking header feature, we will swap this icon for another which is complete opaque to indicate that the feature is now turned on.

“The Trick” or “jQuery Triggers My Love Gun”

So, now that we have everything setup, you can see that our table looks great and that Peter Criss has a definite on-again, off-again relationship with the band (I hate those will-they, won’t they things).

Now, however, we make the real magic happen with the assistance of jQuery. (Let me go on record saying that I was made for loving jQuery and jQuery was made for loving me. It truly is an amazing time and effort saver). To view the complete jQuery code, go here: Easy jQuery Locking Table Headers – Listing 3

The first thing we do is add a zebra-striping function to the $(document).ready handler. This will simply apply a pre-defined class from our style-sheet, called “striped”, to every other row in our table.


//zebra stripe your rows to make 'em look pretty...

$("table tr:nth-child(even)").addClass("striped");

Now, however we get to the good part. Essentially, when a user clicks on our lock icon, we want the following to occur:

  • The script should check to see what state the table is currently in; locked or un-locked.
  • If it is currently un-locked, then we want to activate the locked header feature.
  • If it is currently locked, thenĀ  we want to turn off the locked header feature and revert to the original appearance.

The jQuery .toggle() method

One of the many incredible event handlers that jQuery offers is the .toggle() method. Basically, this binds two event handlers to each of the matched elements from the selector. Each of the two event handlers are executed on alternate clicks. So as you can see, this is a wonderfully quick and easy way to allow our lock/un-lock icon to turn the locking header feature on and off.

First, to bind the toggle event to our image, we will use the following jQuery selector followed by the toggle function:

//bind the toggle event to the lock/unlock image...
$("img.icon-lock").toggle(function () {
...

Our selector is looking for any IMG tag with a class of “icon-lock” and then adding a the toggle event to it.

When the user clicks on the icon for the first time, we know that the table is unlocked by default, so the first event handler will turn on the locking header function. To do this, we will first change the src attribute of the image to display the locked header icon (“images/icon_header_locked.png”):


//set the image source to be the locked version...
$(this).attr("src", "images/icon_header_locked.png");

Notice that since the toggle event was bound to the image, itself, within our function we can refer to the image simply by using $(this). We then use the jQuery attr method which simply allows us to specify a specific, HTML attribute and the value to be applied all objects matched by the selector.

Next, in order to make the locking header feature work, we have to do several things:

  • We want to change the CSS position attribute for the header row to be “absolute”. This will position the header, absolutely, withing the first parent container with a position other than static. Since we have already set our outer DIV to have a position of “relative”, it fits the bill. We will also set the left and top values to 0, so that it will be anchored to the top left of the outer DIV:

    $("tr.header")
    .css({ 'position' : 'absolute', 'left' : 0, 'top' : 0 })
    .after("<tr id=\"inserted\"><td style=\"height: 20px; background: #fff;\" colspan=\"5\">&nbsp;</td></tr>");

  • Additionally, we need to add an additional, empty row to the top of the table. This is because once we change the position attribute of the header, it will be like we removed it, entirely from the table, so the table will actually shift upward and behind the header, hiding our first row of data. By adding a new first row which is blank and the same height as the other rows, it will appear as if the table has not shifted at all. We accomplish this by using the jQuery after() method, which will simply insert the provided content after each element in the set of matched elements, which in this case is the table header row:

    $("tr.header")
    .css({ 'position' : 'absolute', 'left' : 0, 'top' : 0 })
    .after("<tr id=\"inserted\"><td style=\"height: 20px; background: #fff;\" colspan=\"5\">&nbsp;</td></tr>");

  • Finally, we need to set an absolute height on the inner DIV, so that it will scroll anything that goes beyond that height. In this example, I simply set it to 200px, since we have such a small amount of data. This is made wonderfully easy, thanks to jQuery’s css function, which allows us to set one or more CSS properties for the set of matched elements. Remember, as we said earlier, this will always add these properties as inline style attributes:

    $("div.inner").css({ 'height' : '200px' });

And that’s it! Your table should now be scrolling smoothly and your header will stay locked in place.

Now, when the user clicks on the icon again, if the table is in it’s locked state, we want to un-lock it and essentially return everything to normal. To do this, the second handler needs to do the following:

  • First, we change the images src attribute back to the “faded” version of the icon:

    $(this).attr("src", "images/icon_header_unlocked.png");

  • Next, we remove the absolute position property from the table header.

    $("tr.header").css({ 'position' : '', 'top' : 0 });

  • Then, we delete the additional “spacer” row we created earlier from the page. We accomplish this using the jQuery remove() method, which simply removes the set of matched elements from the DOM.

    $("tr#inserted").remove();

  • Finally, we change the inner DIV’s height attribute back to auto:

    $("div.inner").css({ 'height' : 'auto' });

Once again, you can see the complete, working example here.

Of course, this is a simplified scenario. Most of us won’t be hired by a group of international rock stars in makeup and platform shoes to create something so simple. However, I have applied this to far more complex solutions, including to an ASP.NET GridView control.

Please write me and let me know what you would like to see me write about. Perhaps you have a particular feature or function you have been struggling with or you just want to learn more about a particular topic. Give me a shout and I will see what I can do. As always, comments and feedback are welcome.

· · ·

12 comments

  • Brian · April 30, 2010 at 10:17 am

    Hey Bob, Is it possible to make this solution work with flexible width tables?

  • Admin comment by Bob The Geek · May 7, 2010 at 5:54 am

    Brian,

    Yes, it is. You can use jQuery to figure out the width of your table and the time the page loads and then apply that value to the outer and inner DIVs.

    So, let’s say you have a table with an id of “flexiTable”. You could do something like this:

    //get the width of the table, itself....
    var tblWidth = $("table#flexTable").width();

    //set the width of the inner DIV
    $("div.inner").css('width', (tblWidth + 18) + 'px');

    //set the width of the outer DIV
    $("div.outer").css('width', (tblWidth + 20) + 'px');

    You would want to place this routine in your $(document).ready() handler.

    Let me know if you have any other questions.

    Thanks.

    Bob The Geek

  • shawn · May 13, 2010 at 7:44 pm

    Hi Bob:
    Can it lock the first table columns? if yes, can you can give an example?
    Thanks

  • Dave Cahall · May 26, 2010 at 10:30 am

    I have found two problems with the solution that I am wondering if you can help me figure out how to address.

    1. I need to have multiple header rows. I was able to address this by putting a different style on each of the rows and adjusting the ‘top’ attribute on each of the header rows. Not too bad but a little awkward. I tried locking the instead but it did not work.

    2. I cannot get colspan and rowspan attributes on table cells to work. In particular, the row span drops the column from the 2nd row meaning the the header rows no longer match. With the case of colspan, the data does span the columns but even if I use an attribute to center the information in the spanned columns, it only applies the attribute to the first column in the spanned set.

    Obviously this solution solves a couple of problems:
    1. Performance

  • Dave Cahall · May 26, 2010 at 10:34 am

    Forgot to add the second item under the last paragarph which is that this approach should work in any browser versus using the IE ‘expression’ approach for setting the ‘top’ attribute.

  • Wordpress Themes · July 3, 2010 at 5:09 am

    Genial post and this mail helped me alot in my college assignement. Gratefulness you on your information.

  • Wordpress Themes · July 26, 2010 at 1:06 pm

    Amiable post and this fill someone in on helped me alot in my college assignement. Thanks you seeking your information.

  • Denise · October 10, 2010 at 2:16 pm

    Have been looking for ages for a table like this – thank you! I’ve created my table and it works fine. Question: How can I make my table ‘locked’ by default (IE with scroll bar)on opening my web page instead of being ‘unlocked’ by default. Want to save space! Am a complete novice at JavaScript so any help would be appreciated!

  • Rani · November 30, 2010 at 9:02 pm

    Thanks for the code.

    When adding a position:relative to the one td in the table cells or to a div inside it – IE6 / IE7 make this td/div fixed.

    Is there a way to solve this?
    Thanks
    Rani

  • Lotus Shiv · March 24, 2011 at 11:24 am

    Hi,
    1. I need to be able to use JQuery to bind datasets from Server (which I accumulate as a list)
    2. Paging
    3. Search
    4. Assign my own style properties in the javascript side

  • Lotus Shiv · March 24, 2011 at 11:31 am

    Hi,
    I have large datasets (from the server side call of JQuery ) which I assemble as a serialized data string using the JSSSerializaion on the server itself. Now on the javascript side, I need
    1. To be able to bind this data to a table
    2. Page without going to server if possible
    3. Perform Search (Find)
    4. Assign my own Style properties – for e.g header row has certain style settings for background color, font color etc., and detail rows have different sets of settings. Basically I want to be able to assign myself. Right now since I am using the jquery.dataTables.js it doesn’t let me override the style settings. Or I am missing something, as I am new to using JQuery.

    Thanks in advance.

  • Lotus Shiv · March 24, 2011 at 11:33 am

    One other thing I forgot to mention is that the datasets are ~ 10 K and over records that I am dealing with.

Leave a Reply

<<

>>

Theme Design by devolux.nh2.me