JQuery for Everyone: AOP in Action – Clone List Header
This idea came from Carolyn on my post about Fixing the Gantt View.
Do you have a similar trick for freezing column headings on sharepoint lists?
Well, Carolyn, I do now. At first, my research led me to try to move the header row into the toolbar as I did for the Gantt chart. This presented problems not seen on the Gantt:
- The List View is naturally an expanding container of content. The Gantt on the other hand was naturally limited by the List View on the page and thus had a scrollbar. Fixing the height of the List View and adding a scroll bar seemed like the wrong approach.
- If you expand a group that changes the width of columns due to the length of content, the header has to “re-sync” to the new column widths. That became a huge ordeal.
The more I thought about it, the more I thought this solution would need to compromise with the existing functionality. I decided to add the header row to each group. This way, you should always have a group header near by but we don’t need to figure out a way to “float” it outside of the container it describes.
A collapsed list with a lot of content looks something like this:

Once you expand a “middle” group, you start to loose context for the data and you no longer have sorting and filtering capabilities near by.

After a little research, AOP could help make the solution clean and simple. This time, like my last AOP article, I needed data from a SharePoint function when called. In this case, I want to key on the call to ExpCollGroup, which fires every time a group expands or collapses. However, since I don’t need to wait for the data being loaded, I can just use the around method.
Here’s the final result:

<script type="text/javascript"> if(typeof jQuery=="undefined"){ var jQPath="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/"; document.write("<script src='",jQPath,"jquery.min.js' type='text/javascript'><\/script>"); } </script> <script type="text/javascript"> /* * jQuery AOP - jQuery plugin to add features of aspect-oriented programming (AOP) to jQuery. * http://jquery-aop.googlecode.com/ * * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * Version: 1.1 */ //check if AOP already exists if(typeof $.aop=="undefined"){ (function(){var E=1;var B=2;var G=3;var C=4;var F=true;var A=function(K,L,J){var H=K[L];var I;if(J.type==E){I=function(){var M=H.apply(this,arguments);return J.value.apply(this,[M,L])}}else{if(J.type==B){I=function(){J.value.apply(this,[arguments,L]);return H.apply(this,arguments)}}else{if(J.type==C){I=function(){return J.value.apply(this,arguments)}}else{if(J.type==G){I=function(){var M={object:this,args:arguments};return J.value.apply(M.object,[{arguments:M.args,method:L,proceed:function(){return H.apply(M.object,M.args)}}])}}}}}I.unweave=function(){K[L]=H;pointcut=K=I=H=null};K[L]=I;return I};var D=function(I,H){var K=(typeof (I.target.prototype)!="undefined")?I.target.prototype:I.target;var J=[];if(H.type!=C&&typeof (K[I.method])=="undefined"){for(var L in K){if(K[L]!=null&&K[L] instanceof Function&&L.match(I.method)){J[J.length]=A(K,L,H)}}if(J.length==0){throw"No method: "+I.method}}else{J[0]=A(K,I.method,H)}return F?J:J[0]};jQuery.aop={after:function(I,H){return D(I,{type:E,value:H})},before:function(I,H){return D(I,{type:B,value:H})},around:function(I,H){return D(I,{type:G,value:H})},introduction:function(I,H){return D(I,{type:C,value:H})},setup:function(H){F=H.regexMatch}}})(); } </script> <script type="text/javascript"> /* * Copyright (c) 2008 Paul Grenier (endusersharepoint.com) * Licensed under the MIT (MIT-LICENSE.txt) */ $(function(){ var header = $("tr.ms-viewheadertr:first").clone().css({display:"none"}); $("tbody[id^='titl']:not(:has(tr.ms-viewheadertr))").append(header); //AOP around ExpCollGroup(groupName, imgName) $.aop.around({target: window, method: 'ExpCollGroup'}, function(invocation) { $("#titl"+invocation.arguments[0]+">tr.ms-viewheadertr").toggle(); return invocation.proceed(); }); }); </script>
- JQuery for Everyone: Accordion Left Nav
- JQuery for Everyone: Print (Any) Web Part
- JQuery for Everyone: HTML Calculated Column
- JQuery for Everyone: Dressing-up Links Pt1
- JQuery for Everyone: Dressing-up Links Pt2
- JQuery for Everyone: Dressing-up Links Pt3
- JQuery for Everyone: Cleaning Windows Pt1
- JQuery for Everyone: Cleaning Windows Pt2
- JQuery for Everyone: Fixing the Gantt View
- JQuery for Everyone: Dynamically Sizing Excel Web Parts
- JQuery for Everyone: Manually Resizing Web Parts
- JQuery for Everyone: Total Calculated Columns
- JQuery for Everyone: Total of Time Differences
- JQuery for Everyone: Fixing Configured Web Part Height
- JQuery for Everyone: Expand/Collapse All Groups
- JQuery for Everyone: Preview Pane for Multiple Lists
- JQuery for Everyone: Preview Pane for Calendar View
- JQuery for Everyone: Degrading Dynamic Script Loader
- JQuery for Everyone: Force Checkout
- JQuery for Everyone: Replacing [Today]
- JQuery for Everyone: Whether They Want It Or Not
- JQuery for Everyone: Linking the Attachment Icon
- JQuery for Everyone: Aspect-Oriented Programming with jQuery
- JQuery for Everyone: AOP in Action - loadTip Gone Wild
- JQuery for Everyone: Wiki Outbound Links
- JQuery for Everyone: Collapse Text in List View
- JQuery for Everyone: AOP in Action - Clone List Header
- JQuery for Everyone: $.grep and calcHTML Revisited
- JQuery for Everyone: Evolution of the Preview
- JQuery for Everyone: Create a Client-Side Object Model
- JQuery for Everyone: Print (Any) Web Part(s) Plugin
- JQuery for Everyone: Minimal AOP and Elegant Modularity
- JQuery for Everyone: Cookies and Plugins
- JQuery for Everyone: Live Events vs. AOP
- JQuery for Everyone: Live Preview Pane
- JQuery for Everyone: Pre-populate Form Fields
- JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
- Use Firebug in IE
- JQuery for Everyone: Extending OWS API for Calculated Columns
- JQuery for Everyone: Accordion Left-nav with Cookies Speed Test
- JQuery for Everyone: Email a List of People with OWS
- JQuery for Everyone: Faster than Document.Ready
- jQuery for Everyone: Collapse or Prepopulate Form Fields
- jQuery for Everyone: Hourly Summary Web Part
- jQuery for Everyone: "Read More..." On a Blog Site
- jQuery for Everyone: Slick Speed Test
- jQuery for Everyone: The SharePoint Game Changer
- JQuery For Everyone: Live LoadTip
Very cool and great work! However I think this only solves the problem half. If you have a long list and you start scrolling you still loose the context…
@Mike
I totally agree. It’s better than nothing and shows off AOP again. The “full solution” to that problem may need to be a custom web part.
This is pretty helpful for my site, where I use grouping by pending approver. It allows supervisors to use the expand/collapse all groups tool without completely losing the headers…It would be nice if you could scroll through the list under the group and keep the headers (since managers usually have quite a few approvals requests pending around here), but at least this will help quite a bit in expanded view. Thanks!
Looks great, thank you. However filtering functionality was damaged – I can use Ascending/Descending only.
Also how did you add Collapse All / Expand All buttons on the blue header? I tried to add this code (http://www.endusersharepoint.com/?p=1167) at the same web part but it did not work…
@i5
Re: Filters: Someone else noticed strange things with filtering that I could not replicate unless I had the drop-down version of the filters. Can you confirm the type of control holding your filter choices (pop-up or drop-down)? I think I know what’s happening and what I need to do to work around it but I want to have all of the test cases first.
Re: expand/collapse: Look here for an updated version of the code for jQuery 1.3: http://www.endusersharepoint.com/?p=1386
Thanks,
Paul
Hi Paul,
First of all, great idea! I tried implementing this solution on a very large list (1500 items) with grouping and sub-grouping, and it worked, but it killed the performance on the page, slowing it to tremendously slow load times forcing me to remove it. Is there anyway that this can be implemented without affecting the performance?
Thanks in advance,
Erich
Paul, I have made screenshots to illustrate missing filter functionality. Please have a look here: http://www.flickr.com/photos/35892858@N07/
@Erich
1500 items is already close to the practical limit for a SharePoint view. Can you expand on what scenarios showed a performance hit? Like how many items were loaded, how many nested levels had been loaded, etc.?
@i5
This will take considerable testing. Options include: remove extra headers before filter scripts are run, user choice to remove all extra headers (and stop adding them), rewrite filtering choices script to ignore the extra headers. Until I have some time to investigate it, I’m not even sure which direction to go yet.
Maybe the solution is to make this feature optional–something the users chooses knowing that certain filtering capability will be compromised.
Thanks for the tests.
Hi Paul,
The list I have created is a status list. The status is first grouped by effort, and then by the date (week of) that the status is reported. At this time, the view that I would like to use this script on only contains 302 of the close to 1500 items in the list, so I don’t think that the amount of items in the view is causing the performance degradation that you mentioned. Got any ideas?
Once again, thanks!
Erich
@Erich
I just made a Task list with 368 items and two levels of grouping (8 parent groups). Loading with everything open or using expand all only took 4.4s to load the content. This script had almost no impact on load or render times.
You’ll need to use a tool like Net for Firebug to see what’s taking a long time.
Hi Paul,
I appreciate you looking into this. I’ll take a look and see what I can find out.
Thanks,
Erich
I am not sure if this is a bug or not, but the headers are always visible right below the grouped header row. I am not sure if right after I loaded the code they were or were not visible, but every vists back to the page they are always showing. Is there a way to make them only visible when the grouped item is expanded? I am on WSS 3.0 if that make a difference.
@Larry,
Try setting the groups to collapsed by default.
I am sorry I did not mention that. I do have the groups collapsed
@larry
In the view settings, double-check that under Groups it says:
By default, show groupings:
X Collapsed
I found the issue, it was a script conflict running with the hideheaders script. Part of this script hid the column headers. I changed that line from display=none to display=inline, duh! once I put that back, worked perfectly.
thanks
I put this code in a CEWB on my site, but it doesn’t seem to have any affect on my list. Do I need to have the jquery library on my site in order for this code to work?
@Lauren,
If you copied the script as is, and use it on an SSL site, you’ll need to allow the cross-site access to google.com. In IE7 or less, you need to click ‘Yes’ at the security warning, in IE8 it’s ‘No’.
I demonstrate how to use a local version of jQuery in the Toolbox series.
This is also similar and could be helpful for some users: http://kjellsj.blogspot.com/2009/06/sharepoint-jquery-scrolling-view-with.html
Hi, I have found this JQuery useful, however I noticed this doesn’t work with libraries. Seeing as how a library is still a list should this work the same?
Hi
Please explain me how can I implement this on SharePoint list. Please let me know the process.
Venu