1,804 articles and 15,137 comments as of Sunday, May 15th, 2011

EndUserSharePoint has combined resources with NothingButSharePoint.com. You can now find End User (Mark Miller), Developer (Jeremy Thake) and IT Pro SharePoint content all in one place!

This site is a historical archive and is no longer being updated. Please update your favorites, bookmarks and RSS feeds.

NothingButSharePoint.com
Wednesday, February 25, 2009

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:

  1. 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.
  2. 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>

Paul Grenier

View all entries in this series: PaulGrenier-JQuery for Everyone»
Entries in this series:
  1. JQuery for Everyone: Accordion Left Nav
  2. JQuery for Everyone: Print (Any) Web Part
  3. JQuery for Everyone: HTML Calculated Column
  4. JQuery for Everyone: Dressing-up Links Pt1
  5. JQuery for Everyone: Dressing-up Links Pt2
  6. JQuery for Everyone: Dressing-up Links Pt3
  7. JQuery for Everyone: Cleaning Windows Pt1
  8. JQuery for Everyone: Cleaning Windows Pt2
  9. JQuery for Everyone: Fixing the Gantt View
  10. JQuery for Everyone: Dynamically Sizing Excel Web Parts
  11. JQuery for Everyone: Manually Resizing Web Parts
  12. JQuery for Everyone: Total Calculated Columns
  13. JQuery for Everyone: Total of Time Differences
  14. JQuery for Everyone: Fixing Configured Web Part Height
  15. JQuery for Everyone: Expand/Collapse All Groups
  16. JQuery for Everyone: Preview Pane for Multiple Lists
  17. JQuery for Everyone: Preview Pane for Calendar View
  18. JQuery for Everyone: Degrading Dynamic Script Loader
  19. JQuery for Everyone: Force Checkout
  20. JQuery for Everyone: Replacing [Today]
  21. JQuery for Everyone: Whether They Want It Or Not
  22. JQuery for Everyone: Linking the Attachment Icon
  23. JQuery for Everyone: Aspect-Oriented Programming with jQuery
  24. JQuery for Everyone: AOP in Action - loadTip Gone Wild
  25. JQuery for Everyone: Wiki Outbound Links
  26. JQuery for Everyone: Collapse Text in List View
  27. JQuery for Everyone: AOP in Action - Clone List Header
  28. JQuery for Everyone: $.grep and calcHTML Revisited
  29. JQuery for Everyone: Evolution of the Preview
  30. JQuery for Everyone: Create a Client-Side Object Model
  31. JQuery for Everyone: Print (Any) Web Part(s) Plugin
  32. JQuery for Everyone: Minimal AOP and Elegant Modularity
  33. JQuery for Everyone: Cookies and Plugins
  34. JQuery for Everyone: Live Events vs. AOP
  35. JQuery for Everyone: Live Preview Pane
  36. JQuery for Everyone: Pre-populate Form Fields
  37. JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
  38. Use Firebug in IE
  39. JQuery for Everyone: Extending OWS API for Calculated Columns
  40. JQuery for Everyone: Accordion Left-nav with Cookies Speed Test
  41. JQuery for Everyone: Email a List of People with OWS
  42. JQuery for Everyone: Faster than Document.Ready
  43. jQuery for Everyone: Collapse or Prepopulate Form Fields
  44. jQuery for Everyone: Hourly Summary Web Part
  45. jQuery for Everyone: "Read More..." On a Blog Site
  46. jQuery for Everyone: Slick Speed Test
  47. jQuery for Everyone: The SharePoint Game Changer
  48. JQuery For Everyone: Live LoadTip
 

Please Join the Discussion

21 Responses to “JQuery for Everyone: AOP in Action – Clone List Header”
  1. Mike says:

    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…

  2. AutoSponge says:

    @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.

  3. Tiffany Maddux says:

    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!

  4. i5 says:

    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…

  5. AutoSponge says:

    @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

  6. Erich O'Donnell says:

    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

  7. i5 says:

    Paul, I have made screenshots to illustrate missing filter functionality. Please have a look here: http://www.flickr.com/photos/35892858@N07/

  8. AutoSponge says:

    @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.

  9. Erich O'Donnell says:

    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

  10. AutoSponge says:

    @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.

  11. Erich O'Donnell says:

    Hi Paul,

    I appreciate you looking into this. I’ll take a look and see what I can find out.

    Thanks,
    Erich

  12. larry says:

    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.

  13. AutoSponge says:

    @Larry,

    Try setting the groups to collapsed by default.

  14. larry says:

    I am sorry I did not mention that. I do have the groups collapsed

  15. AutoSponge says:

    @larry

    In the view settings, double-check that under Groups it says:

    By default, show groupings:
    X Collapsed

  16. larry says:

    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

  17. Lauren says:

    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?

  18. AutoSponge says:

    @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.

  19. The Wire says:

    This is also similar and could be helpful for some users: http://kjellsj.blogspot.com/2009/06/sharepoint-jquery-scrolling-view-with.html

  20. Crystal says:

    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?

  21. Venu says:

    Hi

    Please explain me how can I implement this on SharePoint list. Please let me know the process.

    Venu


Notify me of comments to this article:


Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!