Creating a jQueryUI Accordion with a Data View Web Part

We can do all sorts of fun stuff that makes the user experience (UX) in SharePoint far better than it is out of the box. One of those things is to use jQueryUI widgets like an accordion to put a large amount of content on the page, but broken into categorized, consumable chunks. Most people are very accustomed to using things like accordions from other Web sites they visit regularly, and even if they aren’t things like accordions seem to make sense quickly.

I had forgotten about it, but I did a post earlier this year where I showed how Carlos Nunes-Ueno approached this. It turns out that our XSL looks similar, but here is another example based on an accordion I recently set up myself.

To start building this, we created two lists. The first list, which we called Accordion Headers, contains the names of the accordion panels plus a column we can use to adjust the order of the panels.

clip_image002

The other list, called Accordion Content, contains the accordion panel content, and has a Lookup column into the Title in the Accordion Headers list. We also pulled the Order column from the Accordion Headers list in with the header name. There are several other columns in this list that we used to construct the content for each accordion panel.

clip_image002[4]

Once the lists were in place and populated, I just added a Data View Web Part (DVWP) to a page with the Accordion Content list as its DataSource.

If you look at the jQueryUI documentation page for the accordion widget, you can see that it wants the markup to look like this:

<div id="accordion">
 <h3><a href="#">First header</a></h3>
 <div>First content</div>
 <h3><a href="#">Second header</a></h3>
 <div>Second content</div>
</div>

The XSL is simple, but you have to roll your own; SharePoint Designer knows nothing about how to structure the rendered markup for this. To do this with the Accordion Content list, the XSL looked like this. Our panels contain unordered lists of links.

<xsl:template match="/" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
  <xsl:call-template name="AccordionSetup"/>
</xsl:template>

<xsl:template name="AccordionSetup">
  <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
  <div id="left-accordion" style="width: 300px;">
    <xsl:for-each select="$Rows">
      <xsl:sort select="@AccHeading_x003a_Order" data-type="number"/>
      <xsl:sort select="@Order" data-type="number"/>
      <xsl:call-template name="AccordionPanels">
        <xsl:with-param name="Rows" select="$Rows"/>
      </xsl:call-template>
    </xsl:for-each>
  </div>
</xsl:template>

<xsl:template name="AccordionPanels">
  <xsl:param name="Rows"/>
  <xsl:variable name="NewHeading" select="ddwrt:NameChanged(string(@AccHeading.), 0)"/>
  <xsl:if test="string-length($NewHeading) &gt; 0">
    <h3>
      <a href="#{substring-before(@AccHeading., ';#')}">
        <xsl:value-of select="substring-after(@AccHeading., ';#')"/>
      </a>
    </h3>
    <div>
      <ul>
        <xsl:variable name="PanelRows" select="$Rows[@AccHeading. = current()/@AccHeading.]"/>
        <xsl:for-each select="$PanelRows">
          <xsl:call-template name="AccordionPanelContent"></xsl:call-template>
        </xsl:for-each>
      </ul>
    </div>
  </xsl:if>
</xsl:template>

<xsl:template name="AccordionPanelContent">
  <li>
    <xsl:value-of select="@Title"/> - <a href="{@ContentLink}"><xsl:value-of select="@ContentLink.desc"/></a>
  </li>
</xsl:template>

It’s pretty simple, really, and what we ended up with is a nicely organized, list-driven accordion which is easy to use and easy to maintain. No developer required to make changes whatsoever. It’s a win-win all around.

clip_image002[6]

<UPDATE dateTime=”2011-11-03T09:26″>

It occurred to me, though no one complained, that I hadn’t shown the jQuery to activate the accordion. Getting the markup right is really the hard part, as the jQuery can be as simple as this:

$("#left-accordion").accordion();

In this particular case, we set a few options as well:

$("#left-accordion").accordion({
  active:false,
  autoHeight: false,
  collapsible: true
});

I’ll leave it to you to check the jQueryUI docs to see what each option does.

</UPDATE>

48 Comments

  1. Mark, great post – I have created very similar navigation in the past – your post highlighted a couple new ideas!!

    p.s. I think you may have a typo in your code example – in the AccordianSetup template you pass operation to AccordianRows template (but your exaple does not show this template – should this be AccordianPanels?

    Reply
  2. Marc, great stuff, thanks.

    Quick question on what this sentence means:

    We also pulled the Order column from the Accordion Headers list in with the header name

    How does the Order column in the Accordian Content list relate back to the Order column in the Accordian Header list? I understand that the AccHeading Column is a lookup, but the Order column in the Content list seems to be unrelated to the Order column in the Header list. Am I missing something?

    Reply
    • David:

      We had two Order columns: one in the Accordion Header list and one in the Accordion Content list. We used the former to order the accordion headers and the latter to order the content within each accordion panel.

      M.

      Reply
      • Thanks for the quick response Mark.

        So is the Order column from the Header list used as the ID column for the AccHeading lookup column?

        I’m not clear on how you were able to get this value in the DVWP if the AccordianContent list is the data source:
        @AccHeading_x003a_Order

        Reply
        • David:

          In SharePoint 2010, you can pull additional column values along with the Lookup column value. In this case, the AccHeading Lookup column is a lookup value from the Accordion Heading list’s Title column. In the list settings, we also ticked the box for Order, so it comes along with the Lookup column value as AccHeading:Order.

          Hopefully this makes it clearer.

          M.

          Reply
  3. I am very inexperienced so you may need to walk me through this. I have set up the DVWP using the Accordion Content list as a date source. Next I highlight the webpart and insert the above XSL where exactly in the code? After that I create a js file referencing the jquery library and jquery ui files, with the above code to call the accordion function, and link that file through a content editor on the page? Thanks for the help.

    Reply
  4. Hi Marc! This was excellent. I am having some difficulty wrapping my head around the XSL. I was able to get what you have done working, but I would like to take it a step forward and format it either as a table with single rows or where each entry has borders.

    Is there a decent way to do this?

    Thanks so much!

    Reply
  5. Hi Marc

    I’ve tried to implement your code and its nearly working I’m getting the heading and the content but the links do show and hide the content, any help would be appreciated. I’ve added the jquery links on the page using a cqwp could this be causing my links not to work?

    Reply
  6. Mark, thanks for this posting. I’m almost there, and have a stupid question. Where bout do I put the jquery trigger? … sorry new to the (much needed accordion AND DVWP)

    /RDN

    Reply

Have a thought or opinion?