Following on from the previous Quick Tip I have another cool little snippet of jQuery code to enable collapsable WebParts on SharePoint Pages. Again like before the following code uses the OOTB v4.master so may not work if you have customised your design or used custom WebPart Control Adapters to change the formatting of WebParts.
So lets get started. Below is the standard HTML format of how SharePoint renders a WebPart. I have cut out unnecessary js that was in the actual source to make things cleaner.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td id="MSOZoneCell_WebPartWPQ4" valign="top" class="s4-wpcell" onkeyup="WpKeyUp(event)" onmouseup="WpClick(event)"> <table class="s4-wpTopTable" border="0" cellpadding="0" cellspacing="0" width="100%"> <tr> <td> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr class="ms-WPHeader"> <td align="left" class="ms-wpTdSpace"> </td> <td title="Links - Use the Links list for links to Web pages that your team members will find interesting or useful." id="WebPartTitleWPQ4" class="ms-WPHeaderTd"> <h3 style="text-align:justify;" class="ms-standardheader ms-WPTitle"> <a accesskey="W" href="/sites/test/Lists/Links"><nobr><span>Links</span><span id="WebPartCaptionWPQ4"></span></nobr></a> </h3> </td> <td align="right" class="ms-WPHeaderTdMenu" onclick="OpenWebPartMenu('MSOMenu_WebPartMenu', this, 'WebPartWPQ4','False'); TrapMenuClick(event); return false;"><span style="display:none;"></span><div class="ms-WPMenuDiv" onmouseout="this.className='ms-WPMenuDiv'" onmouseover="this.className='ms-WPMenuDivHover'"><a onclick="OpenWebPartMenuFromLink('MSOMenu_WebPartMenu', this, 'WebPartWPQ4','False'); return false;" id="WebPartWPQ4_MenuLink" onkeydown="WebPartMenuKeyboardClick(document.getElementById('WebPartWPQ4_MenuLink'), 13, 40, event)" href="#" title="Links Web Part Menu" class="ms-wpselectlink" onblur="UpdateWebPartMenuFocus(this, 'ms-wpselectlink', 'ms-WPEditText');" onfocus="UpdateWebPartMenuFocus(this, 'ms-wpselectlinkfocus', 'ms-WPEditTextVisible');" menuid="MSOMenu_WebPartMenu"><img class="ms-WPHeaderMenuImg" src="/_layouts/images/wpmenuarrow.png" alt="Links Web Part Menu" style="border-width:0px;" /></a></div></td><td class="ms-WPHeaderTdSelection"><span class="ms-WPHeaderTdSelSpan"><input type="checkbox" id="SelectionCbxWebPartWPQ4" class="ms-WPHeaderCbxHidden" title="Select or deselect Links Web Part" onblur="this.className='ms-WPHeaderCbxHidden'" onfocus="this.className='ms-WPHeaderCbxVisible'" onkeyup="WpCbxKeyHandler(event);" onmouseup="WpCbxSelect(event); return false;" onclick="TrapMenuClick(event); return false;" /></span></td><td align="left" class="ms-wpTdSpace"> </td> </tr> </table> </td> </tr> <tr> <td class="" valign="top"> //CONTENT WILL BE IN HERE </td> </tr> </table> </td> </tr> </table> |
As you can see there are alot of tables which are nested. The table that we are interested in is this one:
1 |
<table class="s4-wpTopTable" border="0" cellpadding="0" cellspacing="0" width="100%"> |
This table has two rows. The First row contains the header of the webpart which is where we are going to put our minimise handler, and the second row is the section we are going to “hide” in Javascript. So firstly we need to inject our minimise handler into the WebPart titles <h3> tag. To do this we need a single line of jQuery.
1 |
$('.s4-wpTopTable').find('tr:first h3').append('<a class=\'handler\' style=\'float:right\'><img src=\'/_layouts/images/collapse.gif\'/></a>'); |
What this line does is firstly gets all the .s4-wpTopTable instances and within that element gets the h3 tag from within the first TR. It then appends a custom div with some inline style to float it right with a class of “min”. We have also used some of SharePoints built in images to reduce the number of assets required.
The next step is to now hook into the new handler that we have created above. I will create a couple of new variables to store the images that we are going to use for the collapse and expand buttons.
1 2 3 4 5 6 |
var Collapse = "/_layouts/images/collapse.gif"; var Expand = "/_layouts/images/expand.gif"; $('.handler').click(function(){ var img = $(this).children(); $(this).closest('.s4-wpTopTable').find('tr:first').next().toggle().is(":visible") ? img.attr('src',Collapse) : img.attr('src',Expand ); }); |
As you can see above i have hooked into our dynamically created handler with the class “.handler”. When i click the item it will trigger the function and perform the following “if ” statement which i have compressed to a single line. First i find the closest .s4-wpTopTable element which will always be the parent of the handler. Then i want to get the first TR for that table and then go to the next row and toggle the state to be visible / hidden. I then check the status and if it is visible then show the collapse image otherwise show the expand one. Simple as that.
Here is the full script:
1 2 3 4 5 6 7 8 9 10 11 12 |
<script type="text/javascript" src="http://ajax.Microsoft.com/ajax/jQuery/jquery-1.7.1.min.js"></script> <script type="text/javascript"> jQuery(function($) { $('.s4-wpTopTable').find('tr:first h3').append('<a class=\'min\' style=\'float:right\'><img src=\'/_layouts/images/collapse.gif\'/></a>'); var Collapse = "/_layouts/images/collapse.gif"; var Expand = "/_layouts/images/expand.gif"; $('.min').click(function(){ var img = $(this).children(); $(this).closest('.s4-wpTopTable').find('tr:first').next().toggle().is(":visible") ? img.attr('src',Collapse) : img.attr('src',Expand ); }); }); </script> |
Hope this helps people to extend their SharePoint environments. When i have written the next stage of the drag and drop piece which covers saving the WebPart states then I will update this post to include that functionality.
Chris
Hi chris, this post is so nice and superb.
its worked for me. But, after collapse all webparts and refresh the page it is again expanding all webparts. Is there any chance to maintain state?
Thank you – it works! Could there be a simply addition that would make the transition smooth (ie. animate) I wonder? Now that would be sweet…..
I have an issue<< Help please (my email is saeedalbarhami@yahoo.com or post solution here thanks):
i am validationg webparts and application pages for sharepoint using jquery validate()
the jquery validation works fine and checks all the rules and displaying messages as well,but the issue is that:
if the rules are fine the submit button works and submits data as many times as the validation never fails but once the validation fails for any reason such required field, the user corrects the error and try to submit the data again but the button no more is submitting even if the validation is corrected
Collapse | Copy Code
$("#form1").validate({
rules: { :{ required: true }, : { required: true} },
messages: {
: { required: “* Required Field *” },
: { required: “* Required Field *” }
},
errorLabelContainer: “#errorcontainer”,
wrapper: ‘li’
});
Note: The exact same code is working fine in asp.net normal and master pages .
Ofcourse do not notice the $(“#form1″) i have changed it with aspnetForm
Thanks alot. It is really helpful resource and what I was looking for.
I modified code so that plus and minus icon is appended according to Chrome state of web part.
It works very well but still need to be improved. ^^/
$(document).ready(function()
{
//variables of link to plus and minus icon
var Collapse = “/_layouts/images/collapse.gif”;
var Expand = “/_layouts/images/expand.gif”;
$(‘.s4-wpTopTable’).each(function(){
if($(this).find(‘tr:first’).siblings(‘tr:first’).children(‘td:first’).children(‘div:first’).css(“DISPLAY”) == ‘none’ ){
$(this).find(‘tr:first h3′).append(‘‘);
} else{
$(this).find(‘tr:first h3′).append(‘‘);
}
});
//display the hand cursor over the title table rows for each webpart and hook to the “onclick” event
$(“tr.ms-WPHeader”).css(“cursor”, “pointer”).css(“cursor”, “hand”).bind(“click”, function ()
{
//get the div element nesting the webpart contents: the navigation is based on standard webpart rendering behaviour
var Visibility=$(this).parents(‘tr:first’).siblings(‘tr:first’).children(‘td:first’).children(‘div:first’).toggle().is(“:visible”);
//if webpart is visible, show minus icon, if not plus icon
if(Visibility){
$(this).find(‘a’).children(‘img:first’).attr(‘src’,Collapse);
}
else{$(this).find(‘a’).children(‘img:first’).attr(‘src’,Expand);}
}
);
}
);