Arrange webparts in tabs in webpart page

04.07.2010 Updated the code for the file “WebpartTabs.js” to fix an issue with grouped webparts being expanded by default. Thanks to Bullvan for noticing.

02.02.2010 Update to add possibility to set the tab titles in an array. Useful when viewing multiple views of the same list in a list view.

12.01.2010: Updated the function “getWPTitle” in the code for the file “WebpartTabs.js”.

11.01.2010: Updated the code to allow multiple instances in one page. Please read trough the article as the approach has changed from the code posted two days ago.


In this article i will describe a method for adding webparts in a webpart page as tabs somewhere in the page.

Christophe at PathToSharePoint has already made a solution like this, but i got a request for a method a bit different than his solution:

tecrms Says:
I really like your Tabs in SharePoint form you posted. Is there any chance that you might create a tab container that places webparts on the page into it? For maximum user could they be selected by webpart ID and not by the zone. This way more than one tab container could be placed anywhere are the page.

Alexander Says:
Hi,
Take a look at “Easy Tabs” from Christophe at PathToSharepoint.

No need to invent it again if his solution does what you want…

If this is not what you need, let me know and i will look at it.

Alexander

tecrms Says:
Christophe’s Easy Tabs is an excellent webpart but it grabs everything within a webpart zone. What would be nice is to allow the selection of which webparts are to be placed in the tabs from the webpart zone. This will give one more ability to have more than one “Eays Tab” type webpart within a webpart zone.

I decided to make my own solution:

  • Multiple instances in one page.
  • Optional arrays to specify which webpart’s to include and which to exclude from being “tabbed”.
  • If there are no titles specified in the array’s, the code “consumes” all webparts rendered before the CEWP containing the script
  • Preserves selected tab on page load – for filtering columns etc.
  • Works in webpartpages and in list views
  • …requests anyone?

Images:
IMG

IMG

IMG


As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

A folder named “jQueryUI” containing the scripts and the “images”-folder for the selected theme:
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The jQuery UI-library is found here. Find the theme of your choice – mine is “smootness” – and download the files (the only necessary files are “UI Core” and “Tabs”).

The file “sessvars.js” is found here.

The code for the file “WebpartTabs.js” is provided below.

The way this script works is that you put a CEWP containing the code below the webparts you want to add to the tabs. If you want to add four webparts to the top of a webpart-page, you add the webparts from the top down, and places the CEWP with the script as webpart number five.

The code then (if the webparts has not been excluded) consumes all the webparts stacked above, and adds them as tabs in the order of appearance.

Add a CEWP below the webparts you want to “tab”, and insert this code
[javascript]
<link type="text/css" href="/test/English/Javascript/jQueryUI/jquery-ui-1.7.2.custom.css" rel="stylesheet" />
<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/jQueryUI/jquery-ui-1.7.2.custom.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/sessvars.js"></script>
<script type="text/javascript" src="/test/English/Javascript/WebpartTabs.js"></script>
<script type="text/javascript">
// In a webpart-page: Specify whether to hide the title – it must be present initially to use as "tab title"
hideWPHeader = true;
// Specify webparts to exclude
arrOfTitlesToExclude = [‘Announcements’];
// Specify webparts to include – if empty it consumes all webparts rendered before this CEWP
arrOfTitlesToInclude = [];
// Specify tab title – useful in a view of a list where webpart titles cannot be used
arrOfTabHeadings = [];
// Call the script with an unique identifier of the "tabs-collection"
initBuildTabs(‘one’);
</script>
[/javascript]

If you want another instance in the same page, modify the code like this (this webpart must be placed after the “primary” CEWP):
[javascript]
<script type="text/javascript">
// In a webpart-page: Specify whether to hide the title – it must be present initially to use as "tab title"
hideWPHeader = true;
// Specify webparts to exclude
arrOfTitlesToExclude = [‘Announcements’];
// Specify webparts to include – if empty it consumes all webparts rendered before this CEWP
arrOfTitlesToInclude = [];
// Specify tab title – useful in a view of a list where webpart titles cannot be used
arrOfTabHeadings = [];
// Call the script with an unique identifier of the "tabs-collection"
initBuildTabs(‘two’);
</script>
[/javascript]

Parameters explained:

  • hideWPHeader: If in a webpart-page – true – hides the webpart title
  • arrOfTitlesToExclude: Array of webpart-titles to exclude from tabs
  • arrOfTitlesToInclude: Array of webpart-titles to include in tabs. If left empty, it consumes all visible webparts rendered before this CEWP (that are not actively excluded)
  • arrOfTabHeadings: Array of webpart titles. If this array contains a value for the current tabID, it is used in stead of the default title pulled from the webpart. To specify the title for tabID 2 you must set the array like this: [”,”,’This is the new title’]. Note that tabID 0 and 1 is not altered as the value for those are empty strings.

The code for the file “WebpartTabs.js” looks like this:
[javascript]
/* Add webparts to tabs
* ———————————————
* Created by Alexander Bautz
* alexander.bautz@gmail.com
* http://spjsblog.com
* Copyright (c) 2010 Alexander Bautz (Licensed under the MIT X11 License)
* v1.4
* LastMod: 04.07.2010
* ———————————————
* Include reference to:
* jQuery – http://jquery.com
* jQuery UI – http://jqueryui.com
* sessvars.js – http://www.thomasfrank.se/sessionvars.html
* ———————————————
* See this blog post for instructions:
http://spjsblog.com/2010/01/09/arrange-webparts-in-tabs-in-webpart-page/
*/

function initBuildTabs(uniqueID){
if(typeof(arrOfTitlesToExclude)==’undefined’)arrOfTitlesToExclude = [];
if(typeof(arrOfTitlesToInclude)==’undefined’)arrOfTitlesToInclude = [];
if(typeof(arrOfTabHeadings)==’undefined’)arrOfTabHeadings = [];
// Insert the tabs placeholder
document.write("<div id=’skipThisCEWP’></div><div style=’padding:5px;width:100%’ id=’MyTabs_" + uniqueID + "’><ul></ul></div>");
var myDiv = $("#MyTabs_"+uniqueID);
// Code inactive in "edit page mode"
if($(".ms-WPAddButton").length==0){
var tabID = 0;
// Loop trough all webparts on the page
$(".ms-bodyareaframe td[id^=’MSOZoneCell_WebPartWPQ’]").each(function(){
var thisWP = $(this);
if(!thisWP.data(‘done’)){
thisWP.data(‘done’,true)
// Is it hidden or skipped?
tabTitle = includeThisWP(thisWP,tabID);
if(tabTitle!=false){
// Hide the webpart title?
if(hideWPHeader){
thisWP.find(".ms-WPHeader").hide();
}
// Build the tabs
myDiv.find(‘ul’).append("<li><a href=’#tabContent-" + uniqueID + tabID + "’ onclick=’preserveTab(" + tabID + ","" + uniqueID + "")’>" + tabTitle + "</a></li>");
var wrappedDiv = $("<div id=’tabContent-" + uniqueID + tabID+"’></div>");
wrappedDiv.append(thisWP.find(‘table:first’));
myDiv.append(wrappedDiv);
tabID+=1;
// Remove the empty placeholders to prevent them from taking up space
if(thisWP.parents(‘table:first’).find(‘tr’).length>1){
thisWP.parents(‘tr:first’).remove();
}else{
thisWP.parents(‘table:first’).remove();
}
}
}
});
// Make tabs
myDiv.tabs();
// Fix CSS
$(".ui-tabs-panel").css({‘padding’:’0px’,’text-align’:’left’,’margin’:’0 auto’});
// Select the active tab on page reload
if(sessvars[uniqueID]!=’undefined’){
myDiv.tabs(‘select’, sessvars[uniqueID]);
}
}else{
document.write("<div style=’height:75px;background-color:#FFCC11;text-align:center;font-size:16px;padding-top:30px’>This is the Tabs CEWP</div>");
}
}

function includeThisWP(obj,tID){
// Is it skipped or hidden?
if(obj.find("#skipThisCEWP").length>0 || obj.find(‘table:first’).css(‘display’)==’none’){
return false;
}
if(arrOfTabHeadings[tID]!=undefined && arrOfTabHeadings[tID]!=”){
wpT = arrOfTabHeadings[tID];
}else{
wpT = getWPTitle(obj);
}
// Is it excluded
if($.inArray(wpT,arrOfTitlesToExclude)>-1){
return false;
}else if(arrOfTitlesToInclude==” || $.inArray(wpT,arrOfTitlesToInclude)>-1){
return wpT;
}else{
return false;
}
}

function getWPTitle(obj){
// Lists and librarys
if(obj.html().match(/ctx.ListTitle = "(.+)"/)!=null){
var tRaw = obj.html().match(/ctx.ListTitle = "(.+)"/)[1];
return unescape(tRaw.replace(/\u/g,’%u’))
}
// Other webparts
if(obj.find(".ms-WPHeader h3").length>0){
return obj.find(".ms-WPHeader h3").text();
}else{
return "<span title=’Webpart title must be visible under "Chrome Type" in webpart settings.’ style=’color:red’>No title</span>";
}
}

// Used to preserve the current tab when page loads (column filtering etc.)
function preserveTab(tabID,parentID){
sessvars[parentID]=tabID;
}
[/javascript]
Save as “WebpartTabs.js” – mind the file extension, and upload to the scriptlibrary as shown above.

This script may need a bit more testing so please let me know if you have some comments or finds a bug.

Regards
Alexander

62 thoughts on “Arrange webparts in tabs in webpart page”

    1. Hi,
      I will update the code this evening as i have found some bugs/enhancements so please wait until the updated code is in place before implementing…

      Regards
      Alexander

  1. I know you hate hearing from me lol. I see you have requests anyone in red.
    How about having the tabs in 2 rows, but dynamic, or maybe a setting to choose one or two rows.
    also how about a way to style the tabs, either matching the style of the site theme or ability to apply our own style. Maybe even rounded corners.

    see I know you hated to hear from me. 🙂

  2. Hi Alexander:
    I want to use this tab arrangement with 3-4 views of the same List Web Part. Because the tab pulls the web part name, all the tabs would read the same.

    Perhaps, is there a way to pull the List View Appearance Title?… the title that I can manually type when I click Edit Page in “….aspx?PageView=Shared” mode?

    Thanks-

    Charlie

    1. Hi,
      The webpart-titles cannot be used when using this feature in a “standard list page” and not a webpart page. As you noticed, the list name is used for all tabs containing a view of the list, regardless of the title.

      I have met this limitation myself and have updated the script with an optional array of tab-titles. I will post the updated code later tonight.

      Alexander

  3. Hia,

    This is a great improvement on the pathtosharepoint webpart as it doesn’t consume all the webparts on a page.

    It provides a solution for many pages on my companies intranet.

    However, I still have the limitation of not being able to use the web part ‘appearance’ titles for the tab headings. I think you suggest this can now be done but can you explain how as I can’t see what to change to effect it?

    Many thanks!

    1. Hi Julie,
      You should be able to use the web part appearance title, both in a web part page and in a list view. Just leave the array “arrOfTabHeadings” empty.

      To override one specific tab title (web part number three in this example), set the array like this:

      arrOfTabHeadings = ['','','This is the tab-title'];
      

      Please let me know if this helps.

      Alexander

  4. Hi Alexander,

    I’ve just noticed something else that is limiting! Can you email me as I’d like to see if you can solute it, we have a budget for paid webparts so I’m not after a freebie lol

    I can’t find your email address on here?

    Thanks, J

  5. I get an error when I try to use the solution.

    Error: Uncaught TypeError: Object # has no method ‘tabs’
    it’s related to this line of code:
    $(“#MyTabs_”+uniqueID ).append(“” + tabs + “” + tabsContent).tabs();

    Any ideas on what might be causing the issue?

  6. Ignore my last comment, that issues regarding the error with MyTabs seems to be resolved. Unfortunately, I’m not finding any errors, but it’s not working. I’ll keep plugging away

    1. Hi,
      Please double check that your scripts and CSS are loaded correctly.

      Are you sure that the jQuery UI library you downloaded includes the “Tabs-widget”?

      If you have access to SharePoint designer you can put the CEWP code in a “javascript.aspx” file in the lists directory and use the “Content link” functionality of the CEWP to load the aspx-file from there.

      By using this method you get the some “intellisense”, as the ability to “Control+Click” to check the path of the referred scripts.

      Alexander

  7. If you have many webparts on one page, the tabs will continue on the next row, which is fine. However, the tabs on the bottom will shift depending on which tab is selected, leaving a grey space. The farther position the tab is to the right, the more the following row of tabs with shift, it’s almost like they are indenting (which will also trail to the next row).

    Any fixes?

    1. I see, the tabs themselves will break off from the bottom right of the selected tab, to show the hierarchy, but there is no hierarchy.

      News|Design|Info|Contact
      [—greyspace—]Location|
      History|Clients

    1. Hi,

      I’ve been playing around with SP 2010 and the above solution but just can’t get it working.

      I have gone with your suggested method of using SPD and Ctrl+Click to check the paths – all good.

      When the page is in edit mode the CEWP is yellow and displays ”This is the Tabs CEWP”

      Have you tried SP 2010 yet?

    2. Carolee,
      As a start 2010 doesn’t use “ms-bodyareaframe” anymore, but if you search the css you will find “.ms-bodyareacell” which works
      Then you can carry on and should be fine to adapt it.
      I just used getWPTitle() and changed
      $(“.ms-bodyareaframe td[id^=’MSOZoneCell_WebPartWPQ’]”).each(function(){
      TO
      $(“.ms-bodyareacell td[id^=’MSOZoneCell_WebPartWPQ’]”).each(function()
      and worked just fine.

  8. Hi,

    Thanks for all the great scripts mate. I noticed one thing that bugs me – when in Tabs, list and library GROUPED views load as expanded although they should not (view settings are set to collapsed in initial state). With larger lists it may be a little uncomfortable.
    In which JS file should I look for the code responsible? TIA and thanks again for Your work.

    Bullvan

    PS (WSS 3.0 SP2, jquery-1.4.2.min.js, jquery-ui-1.8.2.custom.min.js)

  9. Hi Alex
    Nice one, but I have custom masterpage and I have observed that the tabs doesn’t show/work when I switch to this master.

    Am I missing anything?

    Thanks

    1. Hi,
      Look at how this script remembers the selected tab during page refresh. The random “generator” must be made separately, but the code in line 64 shows you how to select a tab.

      Alexander

    1. Sorry, but i do not have any plans on implementing that – there is not enough time to handle all requests… Go with Christophe’s solution if you need that functionality.

      Alexander

  10. Hey this is really great! I was wondering how this handled individual web part refreshing? I currently have web parts that use update panels but I found this solution is insufficient towards my performance needs as update panels actually do an invisible full postback…does your web part handle true asynchronous refreshing?

    1. Let me calrify this, I am currently looking for a good way to display chart controls that run complex SQL Queries returning large amounts of results. A tabbed menu acting as a sort of reports dashboard would be ideal, but I am trying to limit the bandwidth on filtering one chart to only refresh that chart itself. Being somewhat of a novice with AJAX/JQuery/and Web Services, I am assuming that I will need to implement my own asynchronous refresh for these charts? Thanks!

    2. Hi,
      This is no more than a “client side rearrangement” of the existing web parts in the page. It adds no extra functionality other than the visual tabbed look. Each web part in the page behaves as it would do on a “non-tabbed” page – including full page reload on filtering.

      Alexander

  11. Hello Alexander,

    looks great. I will try the new solution next days.
    I hope you will find the time to “update” the code for webpart tabbing.
    Can you give us a hint where the problem with webpart tabbing maybe is? So we can try to find problem with the webparts tabbing.

    Regards

  12. Alexander,

    I have been looking for a solution just like this. Sadly though we use SP2010 and this doesn’t work. You mentioned earlier about modifying the code. Have you had a chance to look at this yet? Would love to use this on our Intranet site.

    Thanks,

    Dan

  13. Hey Alexander, great work! I have this working in 2010, but am not able to see the SPRibbon when an item or document are selected. Have you run into this?

    Thanks,

    -Frank

    1. Hi,
      This code has not been tested in SP2010 earlier. I did a quick test now by changing line 32 from “ms-bodyareaframe” to “ms-bodyareacell”

      I have confirmed your findings, but have no fix for them now, sorry.

      Alexander

  14. I have SQL Reporting Service Report Webparts (charts/graphs) that I want to display 2-3 on each tab. How can I achieve this with your sample code? Im not much of a developer but this task was given to me. I appreciate any advice or help you can give.

    Thanks

    1. Hi,
      There are no quick fix for adding multiple web parts on one tab, sorry.

      I might update this later on as i have another way of addressing this issues that may support SP2010 (it is not tested) , but I do not have the time right now.

      Alexander

  15. your blog is pretty awesome…
    I am using it in my site .
    but i got stuck in a problem
    i need to show 5 web parts in tab structure among i want to show three web parts together in one single tab and rest 2 web parts in different tabs.
    how can i achieve it????

  16. Nice Post! Thanks.

    The tabs are left aligned here and not utilizing the entire width available. I tried to get them center aligned and give them max possible width, but I am not able to do so, can you please help.
    I basically want to tabs to be of same width and consume the entire width available to the tab web part.

    1. Hi,
      I do not have this solution active anywhere and cannot look at it live. Try using the developer console (f12) to identify the container you want to give 100% width and then add some CSS to the CEWP to address it and set width:100%.

      Alexander

  17. This solution has some excellent features that I can’t get from Easy Tabs, like Multiple instances in one page, Optional arrays to specify which webpart’s to include and which to exclude from being “tabbed”, Works in webpart pages and in list views.

    Any chance upgrading this for 2010/13 is on your roadmap?

Leave a Reply