Foliotek Developer Blog

Scrollable DataGrid table with Fixed Header with jQuery

Problem

Implement scrolling on a table, but make the header fixed.

I had a really long table and wanted to make it scrollable, but keep the header in place. I saw a few examples of this, but with very little explanation to go with it. So, I just did it myself.

Sample Markup

<div>  
  <table id="MyDataRows">
    <thead>
      <tr class="grid_header">
        <th>Col1</th>
        <th>Col2</th>
        <th>Col3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Data</td>
        <td>Data</td>
        <td>Data</td>
      </tr>
    </tbody>
  </table>
</div>  

First Attempt

So, the first option I saw online was to add “overflow-y: auto; display: block; height: 330px;” to the tbody tag. I saw somewhere where this could work with a single column, but with multiple columns, the header is pushed to the right, and the table rows are pushed to the left. I played with this awhile, but never could get it to work.

Solution

While not as elegant as I would like, this solution does work. Of course, if you isolate the header and then wrap the data rows in a div with the overflow-y stuff, that would work. However, a datagrid populates the header automatically. You could build another datagrid and mess with that, but jQuery quickly solves the issue. Here’s the plan:

  • Add a place for the header to be moved outside of the table with the rows where the overflow-y will be used
  • Move the header
  • Realign the columns

New Markup

This is pretty basic. ?I added a table just before the table with data in it. ?This will hold the header. ?I also styled the div to allow for scrolling.

<table id="MyHeader"></table>  
<div style="overflow-y: auto; height: 330px;">  
  <table id="MyDataRows">
    <thead>
      <tr class="grid_header">
        <th>Col1</th>
        <th>Col2</th>
        <th>Col3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Data</td>
        <td>Data</td>
        <td>Data</td>
      </tr>
    </tbody>
  </table>
</div>  

jQuery Code

<script type="text/javascript">// <![CDATA[  
$(function () {
    /* move the table header from the datagrid outside, so it doesn't scroll" */
    $("#MyHeader").append($("#StudentData thead"));
    $("#MyDataRows").css("margin-bottom", "0");</script>

 var ths = $("#MyHeader th"); var tds = $("#MyDataRows tr:first td");

 /* Reset the padding of the th's to match the td's */ ths.css("padding-left", tds.first().css("padding-left")); ths.css("padding-right", tds.first().css("padding-right"));

 /* Set the widths of all the th's (except the last to acccount for scroll bar) to the corresponding td width */ for (var i = 0; i

Final Markup

So, the end result is two tables whose column widths match but which function as a scrollable datagrid table with a fixed header.

<table id="MyHeader">  
  <thead>
    <tr class="grid_header">
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
</table>  
<div style="overflow-y: auto; height: 330px;">  
  <table id="MyDataRows">
    <tbody>
      <tr>
        <td>Data</td>
        <td>Data</td>
        <td>Data</td>
      </tr>
    </tbody>
  </table>
</div>  

Make Table Rows Sortable Using jQuery UI Sortable

So you want to make table rows sortable using jQuery UI? Luckily, the Sortable interaction does most of the work for you.

But there’s a catch: one problem that I ran into when implementing this (with UI version 1.7) was the cell widths of the row would collapse once I started dragging it.

Suppose you have a table of data, like this one:

<table class="grid" id="sort" title="Kurt Vonnegut novels">  
<thead>  
<tr><th>Year</th><th>Title</th><th>Grade</th></tr>  
</thead>  
<tbody>  
<tr><td>1969</td><td>Slaughterhouse-Five</td><td>A+</td></tr>  
<tr><td>1952</td><td>Player Piano</td><td>B</td></tr>  
<tr><td>1963</td><td>Cat’s Cradle</td><td>A+</td></tr>  
<tr><td>1973</td><td>Breakfast of Champions</td><td>C</td></tr>  
<tr><td>1965</td><td>God Bless You, Mr. Rosewater</td><td>A</td></tr>  
</tbody>  
</table>  

Your first attempt to make it sortable might look like this:

 $(“#sort tbody”).sortable().disableSelection();  

And it actually works, but there is a bit of a problem. The cell widths seem to be collapsing once you start dragging a row (notice how close the “C” cell is to the “Breakfast of Champions” cell). It looks like this:

Sortable row collapsed widths

The problem has to do with the helper object. The helper object is basically the DOM element that follows the cursor during the drag event. When it is created by default, the cells collapse to the size of the content inside of them.

You can specify a function that returns a jQuery object to create a custom helper object. By creating a function that will keep the cell widths consistent, this problem can be fixed.

**Update:** I have posted a [jsFiddle demo of table sorting with jQuery UI](http://jsfiddle.net/bgrins/tzYbU/) to show how the fix works. There is another fix, [proposed on StackOverflow](http://stackoverflow.com/a/1372954/76137) that includes cloning the original row (to not set the width permanently). This is also included in the jsFiddle.
// Return a helper with preserved width of cells  
var fixHelper = function(e, ui) {  
  ui.children().each(function() {  
    $(this).width($(this).width());  
  });  
  return ui;  
};
$(“#sort tbody”).sortable({  
 helper: fixHelper  
 }).disableSelection();  

Now it works as expected:
Sortable row fixed