bg-sharepoint.gif

Transform a SharePoint Web Part Zone Into a jQuery Accordion

I've developed another sandbox web part for SharePoint 2010! This time I'm tackling jQuery UI Accordions. I've again added some customization to the web part so you have more control over how the accordion displays. Most of the parameters are made available through the web part properties panel:

  1. active
  2. collapsible
  3. disabled
  4. event
  5. heightStyle

A few that are missing or incomplete:

  1. active - boolean option to collapse all panels by default
  2. animate
  3. header
  4. icons

Overview of the Code

I'll include the code below just as I did with the tabs web part from earlier. We add a few custom web part properties that can be edited, and use them to output some data on to the page. Most of the real coding is still found in the jQuery function further down the page. You'll notice this is 95% similar to the last project.

Web Part

using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;




namespace jQuery_UI_Accordion_Zone.jQuery_UI_Accordion_Zone
{
	[ToolboxItemAttribute(false)]
	public class jQuery_UI_Accordion_Zone : WebPart
	{




		#region custom web part properties
		[WebBrowsable(true),
		Category("JQuery Options"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Include jQuery"),
		WebDescription("Check to include reference to jQuery. * Google CDN")]
		public bool includejQuery { get; set; }




		[WebBrowsable(true),
		Category("JQuery Options"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Include jQuery UI"),
		WebDescription("Check to include reference to jQuery UI. * Google CDN")]
		public bool includejQueryUI { get; set; }




		[WebBrowsable(true),
		Category("JQuery Options"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Include jQuery UI CSS"),
		WebDescription("Check to include reference to jQuery UI CSS. * Google CDN")]
		public bool includejQueryUICSS { get; set; }




		[WebBrowsable(true),
		Category("JQuery Options"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Include SPAccordions Script"),
		WebDescription("Check to include the custom spaccordions() jQuery function.")]
		public bool includeSPAccordionsScript { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Width (px)"),
		WebDescription("If set a min and max width will be applied to the tab container. (integer)"),
		DefaultValue(0)]
		public int dataWidth { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Active Panel"),
		WebDescription("Which panel should be open? (integer)"),
		DefaultValue(0)]
		public int dataActive { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Collapse All Panels"),
		WebDescription("When set to true, the accordion will initiate with all panels closed.")]
		public bool dataCollapsed { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Keep within Viewport"),
		WebDescription("When set to true, the accordion will stay in view after switching headers.")]
		public bool dataScrollToTop { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Collapsible"),
		WebDescription("When set to true, the active panel can be closed.")]
		public bool dataCollapsible { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Disabled"),
		WebDescription("If set the accordion will be disabled.")]
		public bool dataDisabled { get; set; }




		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Event"),
		WebDescription("The type of event that the headers should react to activate the panel. (event in quotations and comma seperated)")]
		public string dataEvent { get; set; }




		public enum dataHeightStyleOptions { auto, fill, content }
		[WebBrowsable(true),
		Category("JQuery Accordion() Properties"),
		Personalizable(PersonalizationScope.Shared),
		WebDisplayName("Height Style"),
		WebDescription("Controls the heigh of the accordion widget and each panel.")]
		public dataHeightStyleOptions dataHeightStyle { get; set; }
		#endregion




		protected override void Render(HtmlTextWriter writer)
		{




			if (includejQuery)
			{
				writer.WriteLine("<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js\"></script>");
			}
			if (includejQueryUI)
			{
				writer.WriteLine("<script src=\"//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js\"></script>");
			}
			if (includejQueryUICSS)
			{
				writer.WriteLine("<script language=\"javascript\">$('head').append('<link rel=\"stylesheet\" href=\"//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css\" />');</script>");
			}
			if (includeSPAccordionsScript)
			{
				writer.WriteLine("<script language=\"javascript\">$(document).ready(function(){if((document.getElementById(\"MSOLayout_InDesignMode\").value==1)?false:true){$(\".spaccordions\").spaccordions()}});(function(a){a.fn.spaccordions=function(){var d=a(this).data();if(d.collapsed){d.active=false}else{d.active=(d.active)?d.active:0}options={active:d.active,collapsible:(d.collapsible==1)?true:false,disabled:(d.disabled)?d.disabled:false,heightStyle:(d.heightstyle)?d.heightstyle:\"auto\",event:(d.event)?d.event:\"click\"};if(d.scrolltotop){options.activate=function(f,k){var j;if(k.newHeader.length==1){j=a(k.newHeader[0])}else{j=a(this).find(\".ui-accordion-header\").eq(0)}var h=a(window).height();var n=(a(window).scrollTop()==0)?a(\"#s4-workspace\").scrollTop():a(window).scrollTop();var g=j.height();var i=j.offset().top;var l=a(\"#s4-ribbonrow\").height();var m=i+n-l;console.log(\"elementScrollY \"+i);console.log(\"screenY \"+h);if(i>=l&&i<=h){}else{a(window).scrollTop(m);a(\"#s4-workspace\").scrollTop(m)}}}var e=a(this).parents(\"table\").eq(1);var c=a('<div class=\"accordion\">');if(d.width>0){c.width(d.width)}var b=a(\"\");a(this).parents(\"tr\").eq(1).remove();e.find(\".s4-wpTopTable\").each(function(g){var f=a(this).find(\" > tbody > tr > td\");b=b.add(\"<h3>\"+a(f[0]).find(\".ms-WPTitle\").text().trim()+\"</h3>\");b=b.add(\"<div>\"+a(f[1]).html()+\"</div>\")});c.append(b);e.before(c);e.hide();c.accordion(options);return this}})(jQuery);</script>");
			}




			writer.WriteBeginTag("div");
			writer.WriteAttribute("class", "spaccordions");




			// build custom jquery Accordion() options
			if (!isEmptyorNull(Convert.ToString(dataWidth.ToString())))
			{
				writer.WriteAttribute("data-width", dataWidth.ToString());
			}
			if (!isEmptyorNull(Convert.ToString(dataActive)))
			{
				writer.WriteAttribute("data-active", dataActive.ToString());
			}
			if (!isEmptyorNull(Convert.ToString(dataCollapsed)))
			{
				writer.WriteAttribute("data-collapsed", Convert.ToString(dataCollapsed).ToLower());
			}
			if (Convert.ToString(dataCollapsed).ToLower() == "true")
			{
				// if dataCollapsed is set to true, the accordion must also be collapsible
				dataCollapsible = true;
			}
			if (!isEmptyorNull(Convert.ToString(dataCollapsible)))
			{
				writer.WriteAttribute("data-collapsible", Convert.ToString(dataCollapsible).ToLower());
			}
			if (!isEmptyorNull(dataDisabled.ToString()))
			{
				writer.WriteAttribute("data-disabled", dataDisabled.ToString().ToLower());
			}
			if (!isEmptyorNull(dataEvent))
			{
				writer.WriteAttribute("data-event", dataEvent);
			}
			if (!isEmptyorNull(Convert.ToString(dataHeightStyle)))
			{
				writer.WriteAttribute("data-heightstyle", dataHeightStyle.ToString());
			}
			if (!isEmptyorNull(Convert.ToString(dataScrollToTop)))
			{
				writer.WriteAttribute("data-scrolltotop", Convert.ToString(dataScrollToTop).ToLower());
			}




			writer.Write(HtmlTextWriter.TagRightChar);
			writer.WriteEndTag("div");




			writer.WriteLine("<p>Use the web part properties menu to configure options for <a href=\"http://api.jqueryui.com/accordion/\" target=\"_blank\">jQuery UI Accordion</a>.</p>");




			base.Render(writer);




		}




		public bool isEmptyorNull(string value)
		{
			if (string.IsNullOrEmpty(value) || value == "0")
			{
				return true;
			}
			return false;
		}




	}
}

JavaScript (jQuery)

Note that the web part itself includes this code in it's minifed form (line 122).

$(document).ready(function (){
	if((document.getElementById('MSOLayout_InDesignMode').value == 1) ? false : true)
	{
		$('.spaccordions').spaccordions();
	}
});




(function($){
	$.fn.spaccordions = function () {




		/* read-in data from web part */
		var data = $(this).data();
		/* determine correct active property */
		if(data.collapsed){
			data.active = false;
		} else {
			data.active = (data.active)? data.active : 0;
		}
		options = {
			active: data.active,
			collapsible: (data.collapsible == 1)? true : false,
			disabled: (data.disabled) ? data.disabled : false,
			heightStyle: (data.heightstyle) ? data.heightstyle : 'auto',
			event: (data.event) ? data.event : 'click',
		};
		if(data.scrolltotop)
		{
			options.activate = function(event, ui){
				var element;
				if(ui.newHeader.length == 1) {
					// tab is being activated
					element = $(ui.newHeader[0]);
				} else {
					// tab has been collapsed
					element = $(this).find('.ui-accordion-header').eq(0);
				}




				var screenY = $(window).height();
				var scrollY = ($(window).scrollTop() == 0) ? $('#s4-workspace').scrollTop() : $(window).scrollTop();
				var elementY = element.height();
				var elementScrollY = element.offset().top;
				var ribbonRowY = $('#s4-ribbonrow').height();




				var absTopPost = elementScrollY+scrollY-ribbonRowY;
				console.log("elementScrollY "+ elementScrollY);
				console.log("screenY "+screenY);
				if(elementScrollY >= ribbonRowY && elementScrollY <= screenY)
				{
				} else {
					$(window).scrollTop(absTopPost);
					$('#s4-workspace').scrollTop(absTopPost);
				}




			}
		}




		/* web part zone */
		var webpartzone = $(this).parents('table').eq(1);




		/* create tab structure */
		var tabelement = $('<div class="accordion">');
		if(data.width > 0) {
			tabelement.width(data.width);
		}
		var html = $('');




		/* remove old content */
		$(this).parents('tr').eq(1).remove();




		/* create tabs and panels */
		webpartzone.find('.s4-wpTopTable').each(function(i){




			var tdelements = $(this).find(' > tbody > tr > td');
			html = html.add('<h3>'+$(tdelements[0]).find('.ms-WPTitle').text().trim()+'</h3>');
			html = html.add('<div>' + $(tdelements[1]).html() + '</div>');




		});




		/* add to DOM */
		tabelement.append(html);
		webpartzone.before(tabelement);




		/* hide old content */
		webpartzone.hide();




		/* create accordion */
		tabelement.accordion(options);




		return this;
	};
})(jQuery);

Keep within Viewport

There is one option above that is worth mentioning alone. The "Keep within Viewport" option is a usability feature I've built into the accordion. If you've ever used an accordion on a smaller device, like a cell phone, you've probably ran into the issue when activating a panel that your content slides off the page. This isn't a friendly behavior, so this adds a check to make sure the accordion is visible on the page.

Download the Web Part

Want to use it in your own environment?

You'll need the uploadable WSP file.

Want to make changes or explore the code?

You'll need the Visual Studio Project.

Add to the Gallery, Use in Your Page, All Done!

Please checkout the video I posted on YouTube for the jQuery Tabs web part. This web part functions exactly the same, with a few more options. If somebody requests a new video covering this specific web part, I'll record one and replace it. For now watch the previous how-to:

Help Me Improve It!

Here are some of the limitations as of March 27th 2014:

  1. Inclusion of jQuery and jQuery UI scripts are inline with the web part itself.
    • I could not find a way to programmatically add these references to the <head> of the document.
    1. The SharePoint ScriptLink control is not allowed in Sandbox
    2. Adding a Custom Action doesn't allow me to programmatically decide whether it should be on the page or not
  2. Missing boolean option for jQuery Accordions active - to collapse all panels by default.
  3. Missing options for jQuery Accordions animate property.
  4. Missing options for jQuery Accordions header property.
  5. Missing options for jQuery Accordions icons property.
  6. Missing options for jQuery Tabs Show method.
  7. I'd love to remove the Appearance, Layout, & Advanced groups and also have the jQuery group open by default.
Return Home