Monday, October 26, 2015

Sayonara Content Query jQuery Accordion. Hello Script Editor jQuery Accordion!

There was a time when the content query web part was king. If you wanted to do something cool in SharePoint, you'd have to export a web part, crack open content query's XSL file, write some spaghetti code, import a content query web part, and try to stay sane throughout this process. Even Richard III, a mad man by most accounts, would say that process is crazy. But that's how it was back in those dark days.

Thanks to many advancements in SharePoint over the years, your days of exporting/importing web parts and spaghetti coding XSL are over. Because of that, I find myself rethinking tried and true solutions I've used over the years in ways to make them better and easier to reuse.

One content query solution I've found myself using several times over the years was this jQuery accordion. This works well for FAQ lists which are pretty common on intranets. On a particular project, we wanted a reusable accordion web part baked into the page layout for department pages. Because Design Manager in SharePoint 2013 is a POS and systematically derailed any effort of mine to use a pre-configured content query web part in a HTML page layout, I decided to use a different approach: JavaScript.

If you're familiar with my posts and the typical solutions I document, then you're aware that I identify as a “no-code" solutioner first and foremost. While I'm not afraid to make API and web service calls, it can be a little intimidating if you don’t do it every day. I think this solution is way less intimidating than XSL. So if you’re a “no-code” developer, don't panic. This solution is pretty straight forward and I'll walk you through it so you can get it right the first time.

An assumption is that you already have jQuery referenced on the current site.

Line 7 contains the list name I want to query. In this example it’s FAQs.

var oList = web.get_lists().getByTitle('FAQs');

Starting at line 10 is the CAML query to get the content. On line 14 I have the name of the column I want to sort by. In this example it’s SortOrder. This is the column’s internal name.

var camlQuery = new SP.CamlQuery();

    camlQuery.set_viewXml(

        '<View>' +

            '<Query>' +

                '<OrderBy>' +

                    '<FieldRef Name=\'SortOrder\' Ascending=\'TRUE\' />' +

                '</OrderBy>' +

            '</Query>' +

            '<RowLimit>10</RowLimit>' +

        '</View>');

    collfaqs = oList.getItems(camlQuery);

In lines 31 and 32 I set variables where I get values from the current item. Since this is a FAQ list I only want to set the variables for my question and answer. Again, this uses the field’s internal name, not the display name.

var Q = oListItem.get_item('Question');

var A = oListItem.get_item('Answer');

Those are the basics of this solution. Let me briefly explain the rest. On Line 33, I start building the list items of the items retrieved by the query, and line 37 constructs the UL. After the UL, I'm adding this footer where you can click a button to view all the FAQs. The accordionload function begins on line 47 and this contains all of the magic we need to expand and collapse items from our list.

Once you've made your updates, just paste the code into a script editor web part and you're good to go.

var collfaqs;



function retrieveDepartmentFaqs () {

    

    var clientContext = SP.ClientContext.get_current();

    var web = clientContext.get_web();

    var oList = web.get_lists().getByTitle('FAQs');



    var camlQuery = new SP.CamlQuery();

    camlQuery.set_viewXml(

        '<View>' + 

            '<Query>' + 

                '<OrderBy>' + 

                    '<FieldRef Name=\'SortOrder\' Ascending=\'TRUE\' />' + 

                '</OrderBy>' +

            '</Query>' + 

            '<RowLimit>10</RowLimit>' +

        '</View>');

    collfaqs = oList.getItems(camlQuery);



    clientContext.load(collfaqs);



    clientContext.executeQueryAsync(onDepartmentFaqsQuerySucceeded, onDepartmentFaqsQueryFailed);

}

  

function onDepartmentFaqsQuerySucceeded (sender, args) {

    var faqs = '';

    var listItemEnumerator = collfaqs.getEnumerator();

    while (listItemEnumerator.moveNext()) {

        var oListItem = listItemEnumerator.get_current();

        var Q = oListItem.get_item('Question');

        var A = oListItem.get_item('Answer');

        faqs += '<li class="accordion-item">'+'<div class="accordion-header">' + Q + '</div>' + '<div class="accordion-content" style="display: none;">' + A + '</div>'+'</li>';

   

    }



    $('#faqs-list').html('<ul class="accordion">' + faqs + '</ul>' + '<a class="show-all-link btn btn-default btn-block" href="' + _spPageContextInfo.webServerRelativeUrl + 'Pages/all-faqs.aspx"><span class="title">SHOW ALL FAQs</span><span class="icon">&gt;</span></a>');

  

    accordionLoad();

  

}

  

function onDepartmentFaqsQueryFailed (sender, args) {

    console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());

}



function accordionLoad() {



    $(".accordion-header").removeClass("expanded");

    $(".accordion-content").hide();



    $(".accordion-header").bind("click", function(){

        $(this).toggleClass("expanded");

        $(this).siblings(".accordion-content").slideToggle();

    })



}



$(document).ready(function(){



    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', retrieveDepartmentFaqs);



}); 



<div id="faqs-list"></div>

The end result will look something like the image below. You can expand and collapse items by clicking on the title of the question. I removed the expand all and collapse all that the original solution has - only because they were not in the design for this particular site – but would be easy to implement. You may also want to grab some of the styles from the original post and add those to your CSS file.

clip_image001