Freezing GridView Column Headers using Only CSS

Recently I wrote an article describing how you could use CSS plus a GrdiView Control Adaptor to add the 'frozen column headers' feature to your GridView. 

Live Demo (IE only) | Download

I received the following comment from a kind reader named Mitch:

I have also noticed that applying a skin to the gridview also seems to create TH elements for the headers. I have a simple solution that freezes the headers by doing the following:

    • apply a skin to a gridview.
    • wrap the gridview in a div or fieldset.
    • Apply a css class to the wrapper that sets the overflow to scroll and the TH position to relative. You may need to set the TR height to 0px for IE.

I set the height of the wrapper in the local style so I can reuse the css class. I have tested this in IE6 & 7 and Firefox and it works great.

Wow.  If this is true, it has a number of advantages over my original solution:

  1. It can be applied without using Control Adaptors - one less dependency
  2. It works with both IE and FF
  3. It is really simple!

So I took this feedback and tried to fix up my old sample site.  I created a new site from my trusty GridView web site template and started following Mitch's advise.  First off, I created a regular GridView and placed it inside a DIV element like so.  Notice I assign the DIV to the container class.

<div class="container" style="height:300px; width:700px;">
    <asp:GridView 
        runat="server" AutoGenerateColumns="false" 
        AllowSorting="true" DataSourceID="odsCustomers" BorderWidth="0px">
        <Columns>
            <asp:BoundField HeaderText="ID" DataField="CustomerID" SortExpression="CustomerID" />
            <asp:BoundField HeaderText="Name" DataField="ContactName" SortExpression="ContactName" />
            <asp:BoundField HeaderText="Title" DataField="ContactTitle" SortExpression="ContactTitle" />
            <asp:BoundField HeaderText="Address" DataField="Address" SortExpression="Address" />
            <asp:BoundField HeaderText="City" DataField="City" SortExpression="City" />
        </Columns>
    </asp:GridView>
</div>

Next, I created the container css style class as Mitch suggests ...

/* So the overflow scrolls */
.container {overflow:auto;}

/* Keep the header cells positioned as we scroll */
.container table th {position:relative;}

/* For alignment of the scroll bar */
.container table tbody {overflow-x:hidden;} 

Finally, I added back in the other styles that are not related to the frozen headers to spruce things up a bit.  At this point I opened IE to test it out and sure enough it works great.  The only problem is that (again) it doesn't work in FireFox.  It behaves better than my original solution in that the grid renders fine, just the header columns do not stay stationary.  Even though it isn't perfect, I figured I would pass Mitch's solution on since it seems to be hands down better than my original one.  If anyone know's how FF can be supported with pure CSS, I would be interested.

That's it.  Enjoy!         


TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/67

Comments


Posted by: Mustafa Basgun on September 18, 2007 11:29 AM

Matt,

In this approach, using Panel control instead of DIV may make more sense since Panel itself is more playable at the VB side.

<asp:Panel CssClass="container" ID="Panel1" runat="server">
<asp:GridView ID="GridView1" runat="server" DataSourceID="SQLDataSource1" />
</asp:Panel>

By this way, we can dynamically set the height of the panel (well, actually DIV) when freezing of the header is really needed depending on the real estate on the UI. Sample code can be like:

Dim dsa As DataSourceSelectArguments = New DataSourceSelectArguments()
dsa.AddSupportedCapabilities(DataSourceCapabilities.RetrieveTotalRowCount)
Dim dv As Data.DataView = CType(SQLDataSource1.Select(dsa), Data.DataView)
Dim m As Integer = CType(dv.Count.ToString, Integer) '# of rows in GridView1

If m > 10 Then
Panel1.Height = "500"
End If

Thanks for this great post!

Would position:fixed on the TH work in Firefox?

Posted by: Atanas Korchev on September 18, 2007 02:30 PM

I think you can overflow just the tbody for FF. Check this out:
http://web.tampabay.rr.com/bmerkey/examples/nonscroll-table-header.html

Posted by: Mike on September 27, 2007 02:51 PM

Have you tried this in a file that uses a MasterPage? I added the style to the Master and my gridview headers don't freeze.

@Mike

I did try using this with a Master page and I didn't have any issues. There must be something else going on ...

You can donwload the sample project here
to see if you can spot any differences.

Hope that helps.
Matt.

Posted by: Mesut on October 11, 2007 06:54 AM

this works great for me. Thanks Matt.

mesut

Posted by: Rohit Singh on October 11, 2007 12:47 PM

Hi Matt,
By looking at this article, I'm really hopefull of getting some clue for my strange problem with Gridview.
The requirement is that, I need to provide a gridview in a tag with width in % not in px and same with each bounded column's width. And using css "table-layout:fixed" and "word-wrap:break-word" for grid and coulumn respectively to achieve word wrapping. The issue is word wrapping is happening but the width is not what i have defined.
For eg: width of grid is 50% and for two columns with 20% and 80%. But its rendering the columns as 50% each.
One thing I found, if we remove the tag from rendered page (view source) it renders correctly as 20% and 50% but if don't it renders as 50% each.

i guess some thing related to i'm missing.

Posted by: Steve on October 14, 2007 10:51 AM

Good stuff, but without firefox support it's not even usable.

Interesting that we get all this great ajax toolkit from MS, etc... but we don't have simple header freezing code built into gridviews. Especially since we've already rewritten the datagrid once already lol

@Steve -

Thanks for the feedback. You are right, without FF support this isn't a complete solution.

Matt.

Posted by: Vince on November 21, 2007 01:48 PM

Matt, seems to work but has a few side effects.

My gridview was inside a div with .
That no longer works, it seems the culprit is the "style:height: width:" attribute. I tried adding text-align:center; but that didn't help. Any idea how to keep the table centered?

The second problem was that the table has a border and when scrolled, the border scrolls too and leaves a 2px space on top of the header through which you can see part of the text as you scroll by. I'm using master pages by the way.

Posted by: Vinc3 on November 21, 2007 02:29 PM

Regarding the centering issue, I enclosed the div containing the gridview in a panel with a cssstyle which says "text-align:center;" and that worked fine.

Posted by: Vince on November 21, 2007 03:37 PM

Ok, after much fiddling, I found that if I made the enclosing table have no top border and the header have a 2px top border, it seems to solve the problem of the top table border scrolling up. Here's what I used:

.container {overflow:auto;}
.container table {border-width: 0px 2px 2px 2px;}
.container table th { position:relative; border-width: 2px 1px 0px 1px;
    border-style: solid; border-color:Black White Black #336699; }

Posted by: Gaurav Rawat on November 23, 2007 04:11 AM

Hi Matt,
This works fine if we had one row of column headers.My probleim is that I can have 2 or three rows of column headers.So when I scroll I am able to keep my original column header row fixed but other rows of column headers above it are gone.Any idea how to solve this.
Thanks in advance.

Posted by: Socko on November 28, 2007 11:36 PM

Does this technique work if the table also needs to scroll horizontally?

I have used other CSS methods (like: http://www.codeproject.com/aspnet/DataGridFixedHeader.asp?df=100&forumid=170262&exp=0&select=1671948)

which works well - unless the table needs to scroll horizontally - in which case the header cells stay in place while the detail scrolls left and right.

Your live demo only scrolls vertically - so I am curious whether it suffers from the same limitation.

Posted by: Manoj Aggarwal on November 29, 2007 05:10 AM

I am using gridview in update panel. I was loading UserControl dynamically in update panel. My gridview contains a checkbox column. What happens is whenever i scroll the gridview and checks a checkbox the header resets it's position to top of the page equivalent to the amount of scroll that i have done in div. Any ideas behind the strange behaviour?

Posted by: varadhg on December 6, 2007 04:28 PM

Hi Matt,

Is there a way to freeze the gridview column wise ? so for e-x, if we freeze column 0, column 1 and when scroll horizontally these 2 columns are always visible ?

Thanks,

varadhg

Posted by: vlad on January 3, 2008 04:02 AM

Thanks for the post, but when I change one data in the XML file (the database) and i tried to exceed with more than 100 char without space, the header ruin the layout. Is there other way to limit the width and wrap the row data?

Posted by: mary on January 20, 2008 08:09 AM

how do you save the scroll position on refresh/postback?

Posted by: David on January 24, 2008 02:07 AM

I am also havng a similar problem to Socko. Quote:

"I am using gridview in update panel. I was loading UserControl dynamically in update panel. My gridview contains a checkbox column. What happens is whenever i scroll the gridview and checks a checkbox the header resets it's position to top of the page equivalent to the amount of scroll that i have done in div. Any ideas behind the strange behaviour? "

I also have two cases of this in my project. One is a gridview that has a mouse over event which is inside an Accordion Control (essentially updatepanel) and if you have scrolled down and then mouse over, the heder resets its position instead of staying fixed.

My other problem is a gridview control within an AJAX Tab Control. When you have scrolled down and then mouse over the tab header (causing it to flash) the header zooms up and the whole gridview shoots to bottom of page.

Thanks for your help

Posted by: Adeel on January 30, 2008 07:48 AM

It is not working in mozilla firefox


Posted by: Anu on February 4, 2008 04:30 AM

Hi,

This works fine util you attach a cssclass to the mouseover event for every datarow. When you do this the header moves up by the no of rows that you have scrolled and moves down when scrolled down using the mouse. Works fine though when u scroll using arrow keys or click on the scroll bar.
Can you please explain this and also how to solve the problem.

Thanks.

Posted by: RagingKore on February 20, 2008 09:24 AM

This is the only thing that works and is very simple.

.div_grid_container
{
overflow: auto;
height: 150px;
}

.grid_header
{
background-color: #01a0bc;
height: 26px;
position: relative;
top: expression(this.offsetParent.scrollTop-2);
}

Posted by: Paul on April 10, 2008 04:07 PM

I found the solution for the moving header problem: For the parent div that the table headers are located in, set the position to relative as well.

Here's example code:
/* parent div */
div.scrollTable
{
position: relative;
width: 95%;
height: 400px; /* must be greater than tbody*/
overflow: auto;
margin: 0 auto;
padding-right: 10px;
}

/* static header */
.scrollTable th
{
position: relative;
top: expression(offsetParent.scrollTop);
}
--------------
Not 100% sure why it works, but it may be because the jabascript needs to compute the header's location from the parent's location (which was set to fixed before, and is now relative).

Posted by: Paul on April 10, 2008 04:20 PM

Incidentally... this also fixes the horizontal scroll issue. Neat!

Posted by: Elaine Seah on April 25, 2008 02:53 AM

Hi, i have the paging function on my grid in top and bottom, may i know how to fix the position for the paging? Thank for help ^^

Posted by: MikeT on April 30, 2008 12:20 PM

Great code, thanks! I was able to use Matt's code plus some of the code in the comments (Paul & RagingKore) in order to solve all of my needs. Thanks! Your site rules!

Posted by: Dharmeen on May 5, 2008 04:35 PM

Thanks. it solved all my problems

I'm trying to follow this for Firefox 2. Posts by Elaine on 4-25 imply she got working for Firefox but I'm not really clear about that. Here's what I adapted from the above posts (works fine in IE7 but in Firefox the table header row just scrolls out of sight; also the CSS expression on the top property is indicated invalid by Visual Studio 2005 editor)
/* So the overflow scrolls */
div.coveragesContainer
{
overflow: auto;
width: 95%;
height: 380px; /* must be greater than tbody*/
position: relative;
margin: 0 auto;
}
/* Keep the header cells positioned as we scroll */
.coveragesContainer table th
{
position:relative;
top: expression(this.offsetParent.scrollTop);
}

/* For alignment of the scroll bar */
.coveragesContainer table tbody
{
overflow:hidden;
}

Posted by: Satheesh on May 19, 2008 10:00 AM

Hi am facing performance issue while using CSS to freeze column. The browser gets hang when number of records increases. let me if you have any solution for this issue.Thanks in advance.

Posted by: Andrew on June 10, 2008 11:15 AM

No matter what method I employ, I can not get the header to freeze in place. This "simple" method of applying the css to the wrapper does not work. I have even compared the html source from the sample site and my own.

Any ideas what would prevent the solution from working?

Posted by: Noor on June 18, 2008 01:30 AM

Can you please tell me how to fix coloum for the GridView

any idea ?

Posted by: Vijai Prakash Maurya on June 20, 2008 01:17 AM

Hi,

I am still facing moving down whole grid some time while clicking on Girdview Sorting.

Can any one provide Solution for this.

Regards
Vijai

Posted by: Travis on June 25, 2008 08:12 AM

For some reason - when I scroll and move off the tabpanel I have the GV in - the header just disappears.

Posted by: Travis on June 26, 2008 08:20 AM

On my post - when I scroll back to the top of the GV, it "reappears" - why would the css just "disappear"?

Posted by: Phillip Beeke on July 1, 2008 01:36 AM

Hey chaps just tried this out and it works for me. Strange behaviour tho when there is a dropdown list in the grid. The dropdowns float over the header. Check boxes dont tho. Any ideas please

Posted by: Phil on July 3, 2008 11:29 AM

Re: Travis

Yeah I've got the same thing. I've found that if I have a link on one of my table cells that has a style on it A:HOVER {background: yellow} - it causes the header to disappear. Really frustrating. Will have to ditch this solution and have my table header row in a separate table (yuck)... This CSS solution just doesn't seem to be stable enough, not in IE anyway.

Unless anyone has any ideas...?

Posted by: Irman on July 3, 2008 09:37 PM

Mine have more than 30 columns with multiple row header, its become so "messy", the width is set to 100% but the row header widht extended out of the , no longer inside the .

Please, is there any solution for this?

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Sponsor

Recent Comments

  • Irman wrote: Mine have more than 30 columns with multiple row header, its become so "messy", the width is set to...
  • Phil wrote: Re: Travis Yeah I've got the same thing. I've found that if I have a link on one of my table cells ...
  • Phillip Beeke wrote: Hey chaps just tried this out and it works for me. Strange behaviour tho when there is a dropdown l...
  • Travis wrote: On my post - when I scroll back to the top of the GV, it "reappears" - why would the css just "disap...
  • Travis wrote: For some reason - when I scroll and move off the tabpanel I have the GV in - the header just disappe...
  • Vijai Prakash Maurya wrote: Hi, I am still facing moving down whole grid some time while clicking on Girdview Sorting. Can any...
  • Noor wrote: Can you please tell me how to fix coloum for the GridView any idea ?...
  • Andrew wrote: No matter what method I employ, I can not get the header to freeze in place. This "simple" method o...

Sponsor