Friday, June 12, 2009

SharePoint jQuery: Scrolling View with Frozen Header

SharePoint lists that contain a lot of numeric data type columns can be hard to look at when the column headers scroll out of view. Such lists are typically created by moving data entry into SharePoint from Excel, where users could easily freeze the header row. As I showed back in 2005 for ASP.NET datagrids, you can achieve a frozen header row also for HTML tables by applying a simple CSS style.

The following jQuery script contains a selector to first find the table that contains the view of the specified list. Then it wraps the table in a scrolling pane and finally applies the CSS style that freeze the header row. The style class itself is also included above the actual jQuery script. The script shows how to use a jQuery object as the selector context to speed up element lookup.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" 
type="text/javascript"></script>
<style type="text/css">
<!--
.DataGridFixedHeader { position: relative; top: expression(this.offsetParent.scrollTop);}
-->
</style>
 
<script type="text/javascript">
$(function(){
var $table = $("TABLE[ID^='{4C9CFF20-B467-4E10-820C-0A132442CF98}']:first"
"#MSO_ContentTable");
 
<!--WRAP TABLE IN SCROLL PANE-->
$table.wrap("<DIV style='OVERFLOW: auto; HEIGHT: 420px'></DIV>");
<!--FROZEN HEADER ROW-->
$("TR.ms-viewheadertr:first", $table).addClass("DataGridFixedHeader");
});
</script>

The ID attribute of the SharePoint view <TABLE> element is a combination of the the list's GUID and the view's GUID. The script shows how to use the attribute starts with filter (^) on the table's ID. The found table is kept in a jQuery object named $table for manipulation and for use as a jQuery context. Use View-Source to view the page HTML, search for ContextInfo and look for the first <TABLE> element beneath it to find the list's GUID.

The $table object is then injected into a DIV with a fixed height and overflow:auto to get a scrollbar on the list, using the wrap(html) manipulation function. The script then looks up the table's header TR element based on it's class and appends the DataGridFixedHeader style to the header row.


To avoid hardcoding list GUIDs into your scripts, you can use the SharePoint JavaScript ContextInfo object to set the value of the table ID attribute filter:

$("TABLE[ID^='" + ctx.listName + "']")

Each view page has an instance of the ContextInfo object named ctx, and you can access any JavaScript object on the page from jQuery - afterall it is just a JavaScript DSL.

Using the ContextInfo object allows you to put generic jQuery scripts into common files that you include using a CEWP in view pages for more than just one list. In addition, I recommend putting script that only applies to a specific list in a list specific file that is only included in the view pages of the list.

[UPDATE] As the most common problem people have with this is getting their jQuery selector right, please see this post by Chris O'Brien on useful jQuery tools.

[UPDATE] As the used CSS expression is for IE7 and not supported in other browsers or newer versions of IE, go to the jQuery Plugin site to look for standard compliant solutions for fixed header.

72 comments:

wordoftruth said...

Awesome! Thank you very much. This really should be built into WSS.

Anonymous said...

Awesome code .It works for lists.But is it possible for document libraries also.

Kjell-Sverre Jerijærvi said...

Certainly, all it does is manipulate the HTML of the page. Use the IE dev toolbar or Firebug to find the actual doc lib column HTML, note that using Page>View>Source shows just a snapshot of the initially loaded page, before any manipulation takes place.

Ronak said...

HI Kjell,
Nice Post very very Useful.
I have one question though its display only vertical scrollbar but i want to both vertical and Horizontal because i have one list with big content and as well as no of list columns so i got both scrollbar around page.

Kjell-Sverre Jerijærvi said...

AFAIK you need to set a fixed width then also, the example just sets a fixed height.

Julian said...

Pretty cool. Is it possible to only freeze a column rather than the header to scroll horizontally?

Anonymous said...

Hi Kjell,
I do not manage to use it in a DocLib. It seems that the $table is not correct.
I use:
var $table = $("TABLE[ID^='" + ctx.listName + "']:first","#MSO_ContentTable");

Otherwise: clever code

pault said...

i can't seem to get this to work, sure it's me! can you (or anyone with success) post step by step instructions (ie, where should the CEWP reside, what is meant by "e ID attribute of the SharePoint view TABLE element is a combination of the the list's GUID and the view's GUID", et al)?

Kjell-Sverre Jerijærvi said...

SharePoint has a querystring and GUID fetish, instead of adhering to REST priciples. So, the ID of a list is a GUID, so is the ID of a view. The HTML table that renders a specific view of a list, combines the list GUID plus the view GUID as the table ID. This makes the ID very overcomplicated for sure.

pault said...

got it working...thanks!

Eric Nash said...

Great solution! One question... doesn't seem to work in Firefox although I don't see anything obviously browser-specific in the script. I'm not a coder but I normally don't have any issues with jQuery in FF.

Any suggestions? I have my Windows users running IE Tab but my Mac folks are a problem right now.

bcobra said...

why wasnt my comment asking about a frozen first column acknowledged?

bcobra said...

ok let me restate:
is there a way to freeze the first column as well, so users can scroll horizontaly and know what record id they are viewing all the way across the spreadsheet?

Kjell-Sverre Jerijærvi said...

This example should give you some ideas: http://www.javascripttoolbox.com/lib/scrollingdatagrid/

Jillian Marohnic said...

I was able to make this work for a ListViewWebPart, but not for a DataFormWebPart that shows a multi-item view of the list. I don't find a ContextInfo for the DataFormWebPart. It starts out:

WebPartPages:DataFormWebPart runat="server" IsIncluded="True" FrameType="None" NoDefaultStyle="TRUE" ViewFlag="0" Title="Projects" __markuptype="vsattributemarkup" __WebPartId="{E2262631-CCBC-4A2A-B5B5-C2468969ACCB}" id="g_e2262631_ccbc_4a2a_b5b5_c2468969accb" __AllowXSLTEditing="true" WebPart="true" Height="" Width="">

Is there anything in there I can use?

Kjell-Sverre Jerijærvi said...

ContextInfo is not required, thats just a trick when creating reusable scripts.

Look for the id attribute of the table, inside the web-part element you show in your comment.

Anonymous said...

Hi,
I use this code for sharepoint lists. I tried to also use it for the document library. It doesn´t work. Is it possible to use it for the document library and if yes, how? Thank you in advance.

Anonymous said...

hi this is a great post but i have a small problem i have a page with multiple list views on so i have multiple ctx.listNames in the html.

any ideas how i could use jquery to pick the right ctx.listName?

thanks

jon

Kjell-Sverre Jerijærvi said...

Haven't looked into that, but the simple solution is to hardcode list IDs - making it a page specific script.

Johnny said...

Hello,

Thank you for posting this code.

How do I assign the code (below) to a SharePoint list on my site called "Project List"?

Thank you.

var $table = $("TABLE[ID^='" + ctx.listName + "']:first","#MSO_ContentTable");

Kjell-Sverre Jerijærvi said...

The ID variable listName is a misnomer, it is a GUID as shown in the article, not the user friendly display name - your "Project List". Use view source in your browser to find the ID.

Johnny said...

Thank you for your reply.

I used "View Source" like you said and it worked.

Thank you.

Carine said...

This works great thank you. Only issue is when I use the scrollbar on my browser window (IE 8) the header bar moves up and down and sometimes disappears...anybody else run into this issue?

TooMuchTime said...

I have a sharepoint list that is long and will get longer, so I'm hoping this works for me. All I have is Sharepoint Designer 2007 and I can't figure out exactly where to insert this code. I've opened the list page but viewing it in code just shows ASP content.

I've tested this by saving the view source text file and adding this code to it. I know it works, but I need to know how to do this in Sharepoint Designer 2007.

Thanks in advance.

Kjell-Sverre Jerijærvi said...

You can do it directly in the .aspx page by adding the JavaScript <script> block in the <head> section of the page. Note that you then will customize (unghost) the page. Using a content editor web-part in a WebPartZone keeps the page uncustomized as the script block is then injected into the page when rendered.

TooMuchTime said...

Unfortunately, when I view the page in Sharepoint Designer 2007, there is no header tag. Everything is just ASP content.

The only header tag I can find in any page is in the "default.master" page under the "_catalogs" folder. I tried adding it there and it didn't work.

Kjell-Sverre Jerijærvi said...

Look for the asp:Content control called additional page head and add your script block there. Or unghost/customize the file to edit it anywhere.

TooMuchTime said...

Thank you, sir! That worked perfectly.

Anonymous said...

Thanks for the code. #it works for my list, but i have huge problems to implement sth like that for a document or even from lib. Can you please explain how to do that? Would make lots of Sharepoint users happy :)

Eric said...

Thank you very much for the code! With a little tweaking I was able to get it working perfectly.

I also have a solution for anyone trying to get this to work in a document library. As far as I can tell, document libraries do NOT store their list ID in the ctx.listName variable. In fact, they don't appear to be stored in a variable at all. Use ctrl+f to find "TABLE ID=" where "summary=(name of your page)". For example, if I had a page named MyDocs, I would look for the following line:

TABLE ID="table ID here" width="100%" class="ms-listviewtable" border=0 cellspacing=0 cellpadding=1 dir="None" summary="MyDocs"

It looks like you have to hard code document library IDs when using this script.

Hope this helps!
-Eric

Anonymous said...

Hi Kjell,

I must be missing something since I've tried implementing this with the GUID hardcoded as well as using

var $table = $("TABLE[ID^='" + ctx.listName + "']:first", "#MSO_ContentTable");

Simply put no vertical scroll bar shows up. Any ideas what I'm missing?

ralph said...

hello is there a to wrap columns. for example every 4 column:
- - - -
- - - -
- - - -
hope this helps explain what i am trying to do.

thank you in advance.

Anonymous said...

This rocks! Thanks for posting a simple solution to a common problem. It works great!

Anonymous said...

My list is grouped and now appears with all groups expanded by default. Any way to get it to display with all groups collapsed when the page is opened?

Kjell-Sverre Jerijærvi said...

You'll find group collapse/expand scripts at EUSP: http://www.endusersharepoint.com/STP/viewtopic.php?f=3&t=136&start=0

Faisal said...

Hi, thanks for the code.

Is it possible to use the code as-is in a list template? I'd like to allow users to create lists based on a template which contains this code, but users may name the lists anything they want.

Thanks!
Faisal

Kjell-Sverre Jerijærvi said...

@Faisal: it might work if you include the general JQuery files in your masterpage, and then put the list definition specific script in e.g. AllItems.aspx - have never tried this my self though.

pmartensen said...

I'm not a programmer, but I would really like to get this to work in SP WSS. I can't find the GUID. I went to List Settings and copied the URL (http://scy01-sp2/animal_health/Main/_layouts/listedit.aspx?List=%7BC47344E9%2D7E1D%2D4DED%2D920F%2DB297D80174A8%7D) and used everything after "List", but that didn't work. I also viewed the source and searched for "table", but "table" wasn't found. Can you give me more explicit instructions for finding the GUID?
Thanks in advance.

Kjell-Sverre Jerijærvi said...

if there is no "table" in view-source, then you cannot use this JQuery script. Your URL encoded GUID %7BC47344E9%2D7E1D%2D4DED%2D920F%2DB297D80174A8%7D is easily decoded into {C47344E9-7E1D-4DED-920F-B297D80174A8}

Anonymous said...

Thanks dude!

Alex said...

Thanks for the code, it works great!

However, On the page with the script I keep getting a dailog box asking if I want to only view items delivered securely. If I say yes, it does not work.

Is there a fix for this?

Thanks!

Kjell-Sverre Jerijærvi said...

If you mix HTTPS and HTTP content on the page, such as a HTTPS page with HTTP linked content such as scripts, IE will give this warning. Make sure that all HTTPS pages contains only HTTPS delivered content. Or adjust your IE mixed content setting for the IE zone that your site belongs to.

Alex said...

Thanks, I went in to the script and changed the http link to https.

One other question. The code apparently defeats the "max width" setting in the Web Part editor dialog box. The toolbar is limited but the table itself is not. Is there a way around this? (i.e. to limit the width of the table?)

I don't think it's possible, from what I read, but it would be even better to be able to freeze left columns!

I really need to limit the width though.

Thanks.

jeff said...

Thanks for posting, but I can’t get this to work for me. My lists' url ends with listedit.aspx?List=%7B1F9514E0%2DC551%2D4F43%2D9CBC%2D356490576256%7D, so I used the following in your code but it didn't work. Any ideas what's wrong?

var $table = $("TABLE[ID^='" + {1F9514E0-C551-4F43-9CBC-356490576256} + "']:first","#MSO_ContentTable");

Anonymous said...

Hi Kjell..Excellent post...but i want to freeze first two column in a listwebpart....Could you please help out.

Kjell-Sverre Jerijærvi said...

This example should give you some ideas: http://www.javascripttoolbox.com/lib/scrollingdatagrid/

Kai_Kiste said...

Hi Kjell,

Thx for this great post about the posibilty to fix Headers.
I tried to realize this with Sharepoint 2010 ith IE8 and wondered why there nothing works.

Now i think the solution is the ended support for CSS-Expressions in IE8 to get more standard with others Browsers, but to use Sharepoint 2010 with any mode lower then IE chrashes the most design of the ribbon and some other things.

So for now the Code seems to be outdated, if you look forwards to SP2010...
is there any solution so get the "scrolltop" working on the CSS with pure Javascript?
'im not a really good coder, so for me the answer is far far away for the moment.

Thx,
Kai

Kjell-Sverre Jerijærvi said...

Check this table scrolling example in your browser to see if it works: http://www.javascripttoolbox.com/lib/scrollingdatagrid/

If it does, your jQuery selector is wrong.

Kai_Kiste said...

Hi Kjell,

your script works on my table like it should, but only the part of the css (top: expression(this.offsetParent.scrollTop)) isnt includes in the css, its missing because IE8 doenst compile it.
and without this expression it seems there is no fixed header at the moment...

Greetz, Kai

Anonymous said...

Hi Kjell,

When I use this script with Grouped By columns it works but with a caveat. I have a grouped by view on a list which is collapsed by default on a field however when I use this script for fixing the header row it expands the Grouped By column hence resulting in a problem as I have so many items grouped by. Any solution for that? Your help is much appreciated.

Thanks! Shalin

Anonymous said...

Great solution!
But,
It doesn´t work SharePoint 2010 & ie8. :-(

Kjell-Sverre Jerijærvi said...

That's correct, it used non-standard IE7 specific CSS expressions. The compliant way is to do this with javascript if possible.

Kjell-Sverre Jerijærvi said...

You can look for standard compliant jQuery solutions here: http://plugins.jquery.com/plugin-tags/fixed-header

Walter said...

Is there a version of this that will work with IE8?

Srikanth SharePoint MCP said...

Hi, Thanks for wonderful solution. It works great if i use out of box master page, but if I use custom master page then it is not working. Can you please let me know how can I trouble shoot.

Kjell-Sverre Jerijærvi said...

Perhaps it is a JavaScript onload event error that causes the script not to run. Use Firebug to debug or look for the JS error warning in IE.

Anonymous said...

Great script, i can add ti to a CEWP, but it doesn't seem to work (i've added alerts but none fired) - i'm using SP2010:



$(function(){
$("TH.ms-vh2-nograd:contains('Notes')").css("width", "300px");
alert('here');
});

does it make any difference that i'm on a https page? will this affect the call to the jquery file?

thanks

Hariny said...

This works perfect for me - However, I would like to freeze the row below the header too. This row displays the count of the number of rows (entries)- I included this row by modifying current view to include column total for one of the first columns. I would like this row to be frozen as well as I scroll down the list. Is that possible? Or is there some other way with which I can display the total no of entries (it should update based on filters applied)? I have been exploring web partts, but have had no success so far. Would greatly appreciate your help!

Walter said...

The expression(this.offsetParent.scrollTop) doesn't seem to be supported is there an alternate method.

Kjell-Sverre Jerijærvi said...

correct, the expression no longer supported, haven't looked into alternatives

Miri said...

Rellay nice.
It works fine within lists.

But how can I fix the header columns in a document library.?
Unfortunately the script does not work for this issue.

Do you have any idea?

Thanks in advance.

Kjell-Sverre Jerijærvi said...

Have a look at Eric comment above, you need to get the selector right.

Anonymous said...

The original post of this solution was for SharePoint 2007 I assume? Does anyone know the 2010 solution that works with any IE (7,8,or 9)? I tried this solution with 2010 and both IE9 and IE8. I get the right scroll bar and anything above the web part stays frozen but the header and content all scrolls. If anyone can help, I would much appreciate it!

Stephen St.Esprit said...

Has there been any recent solutions to this in SP 2010 and IE8 yet? I have a requirement to do this and every search I do ultimately sends me back to this blog.

Any assistance is greatly appreciated!

STEPHEN

Anonymous said...

Another solution that is SP2010 ready has been published by Benjamin Gemperle (GERMAN blog) - code can be downloaded from there:

http://2sicgeeks.wordpress.com/2012/02/09/spaltenuberschriften-in-sharepoint-tabellen-listenansichten-einfrierenfixieren/

Preston P. said...

I've got it working great, but I have to hard code the GUID for the list (ctx.listName) and view (ctx.view), but would like to be able to save the CEWP and let users apply as needed. I've tried:

$(function(){var $table = $("TABLE[ID^=’”+ctx.listName+”-“+ctx.view']:first", "#MSO_ContentTable");

to get the current List / View, but to no avail. Any thoughts?

Kjell-Sverre Jerijærvi said...

Open the IE debugger using F12 and inspect the ctx variable. In 2010 there is another SP context object available.

Anonymous said...

Hello,

i work with the Internet Explorer 10 and SharePoint 2010. I tested your code but the header doesen't freeze. The scrolling function works.
Do you have an idea how i can fix this?

Greetings from Germany

Kjell-Sverre Jerijærvi said...

The script in this blog uses discontinued CSS expressions, so it won't work. There are several suggested solutions in the above comments, alas I have not tested any of them.

You can look for standard compliant jQuery solutions here: http://plugins.jquery.com/plugin-tags/fixed-header

soupau said...

Hi Kjell,

I have almost similar issue. I have more column and large content in list and bcoz of that it doesnt fit in one page. However I have vertical bar but not horizontal bar to see the extreme right.. I want the horizontal bar to scroll the page to right. Can any one help me please..

soupau

Anonymous said...

Looks great but looking for a solution on Sp2013. You said "It can be made to work with 2013, but there's a few extra steps that I'll need to document". Any clue how to extend the current script?
Thanks

Kjell-Sverre Jerijærvi said...

There is nothing on SP2013 in the post or comments, but this might give some clues to make it work: http://2sicgeeks.wordpress.com/2012/02/09/spaltenuberschriften-in-sharepoint-tabellen-listenansichten-einfrierenfixieren/