Apr 3, 2013

Accordion "Left Navigation" (Quick Launch) for SharePoint 2013

[Edit 7.3.2016: Since posting this, a lot has changed both in the browser and SharePoint world. This still works, at least in IE, but there have been a whole lot of issues with it. Thus I would urge you to consider the solution by MaxYakovenko instead of implementing this one (I am not attempting to solve the issues of this one anymore).]

One of my customers is working on their new SharePoint 2013 intranet site. They needed the Current Navigation (Foundation: Quick Lauch) to be an accordion. We tried a couple different jQuery code bits, but whereas they used to work in SharePoint 2010, in 2013 they only flashed the subnavigation instead of leaving it open.

Googling for one that would work with SharePoint 2013, I found a code snippet  in http://joao-pinho.blogspot.de/2012/11/sharepoint-2013-accordion-quicklaunch.html, but it did not do everything as intended (s.o. the links did not function anymore as the click was completely captured by jQuery). So, with a couple modifications:

$(function(){
 /*set dynamic css logic*/
 if($('#sideNavBox .menu-item.selected').length){
  //propagates the selected class, up the three.
  $('li.static').removeClass('selected');
  $('#sideNavBox .menu-item.selected').parents('li.static').addClass('selected');

  //collapses top siblings of selected branch
  $('#sideNavBox .menu-item.selected').parents('li.static').last().siblings()
   .find('> ul').hide();
 }
 else $('#sideNavBox .root.static > li.static > ul').hide();

 /*set accordion effect*/
 $('#sideNavBox .root.static > li.static').each(function(){
  if($(this).find('ul').length){
   $(this).addClass('father').click(function(){
    if($(this).children('ul').css('display') != 'none'){
     $(this).removeClass('selected').children('ul').slideUp();
    }
    else {
     /*collapse-siblings*/
     $(this).siblings().removeClass('selected').children('ul').slideUp();

     /*expand*/
     $(this).addClass('selected').children('ul').slideDown();
    }

    /*added: stop event propagation to link nodes*/
    $('a.static').click(function(event) {
        event.stopPropagation();
    });

    /*added*/
    return false;
   });
  }
 });
});

This piece of code assumes that the SharePoint navigation levels in the MasterPage are set to 3 static ones and no dynamic levels, the SiteMapProvider is CurrentNavigation, and the navigation settings in the sites are set to:

- SITE WHOSE CHILDREN FORM THE ACCORDION: Structural Navigation: Display only the navigation items below the current site, Show subsites

- ACCORDION HEADING LEVEL SITES (PARENTS):  Structural Navigation: Display the current site, the navigation items below the current site, and the current site's siblings, Show subsites

- ACCORDION SUB LEVEL SITES (CHILDREN):  Display the same navigation items as the parent site

35 comments:

Anonymous said...

Awesome thanks. I was struggling with this for a while and I am glad I found your solution.

Leo

Andrés said...

Hi, i'm using the seattle theme provided by sharepoint 2013, please, can you tell me where i should put the code, thanks :D

SM said...

Andrés, I added the code to a custom master page, but I assume that it would be possible to achieve the same by using the Script Editor Web part (http://borderingdotnet.blogspot.fi/2013/01/script-editor-web-part-in-sharepoint.html) but then that only affects one page.

Other than these two options, you should create a delegate to inject the javascript to the pages. There are some projects in CodePlex that might help you out.

Anonymous said...

Where in the masterpages do you add this code

SM said...

You can add it simply to the head section of the MasterPage.

Unknown said...

Great script. Implemented it in 30 seconds and it just worked.

Harikrishna Yadanaparti said...

Do we need to refer any jquery files? i was getting an error at $(funciton()) saying object expected.

SM said...

Yes, this code snippet uses jQuery, so it needs to be referenced somehere on the page

Unknown said...

I have an issue with the above given accordion, when you click on root link all the sub links are getting slide down, while clicking on the sub menu link, the respective page is getting loaded.

Till this point every thing is working as expected.

But if you click for the second submenu ( in the newly opened page) instead of opening the respective page on the second submenu, it was collapsing.

let say..

we have a root link called A, and have a1,a2,a3 are the links under that A. on clicking on A, a1,a2,a3 are got slide down, clicking on a1 respective page got loaded along with a1,a2,a3 are showing.

if click on a2, instead of taking it to respective page, a1,a2,a3 are getting collapsed .

Please give me what is going wrong on this script.

or what are the changes that i need to place in the script to meet my requirement.

Arun said...

I have the same issue mentioned above. Any help

SM said...

@Siva RamaKrishna and @Arun, this behavior may be due to the navigation settings of the children SharePoint sites (e.g. the a2). They need to be set to show the same navigation as their parent.

Another explanation may lie within the SharePoint cache - I noticed that the script was working a bit quirkily at first, then after lunch break, it was fine. JavaScript generally gets stuck in the cache quite heavily.

Hope this helped!

Arun said...

@SM I have set the navigation to Display the same navigation items as the parent site.

Current page menu chnaged and showing root menus...?? Any help

Mel Colón said...

Awesome! I love the fact that it maintains expanded where the current page is. Quick, easy and works with different browsers :) Thanks!

Unknown said...

This works great except for one part. If I click a heading to expand it, but then decide I didn't want to click that and try to click it again to collapse it, it takes me to the Site Contents page. Is it like that for everyone? Please help!

Melissa said...

Testing all the links in the left navigation I experienced what Siva RamaKrishna had posted earlier:

"But if you click for the second submenu ( in the newly opened page) instead of opening the respective page on the second submenu, it was collapsing."

If I click on another sub-menu in the same category that the "selected" page is in, the parent collapses intead of taking me to the page I wanted. I replaced the code with the original in this document to make sure I didn't mess with it, but it does the same thing. Any suggestions?

SM said...

@zach welding, your problem sounds a lot like ones I encountered with SP2010&jQuery when there were more than one reference to the jQuery file on one page.

@Melissa and @Siva RamaKrishna, I had that problem _before_ I added the stop event propagation part to the script. I'm sorry don't have a ready solution to fix it if it still resides for with this script I have been using with success.

Melissa said...

It's just really weird. It makes no difference if I comment that part out (the click function where you added the stopPropagation event). My "father" elements aren't links, just headings with no URL. So they don't have a tags, just span tags with the text. I tried making them links but it didn't make a difference. I'm trying to replace the "a.static" with other things in the stop event propagation part, but nothing seems to work.

Anonymous said...

This is just freakin awesome. I can't believe it just works and so little code. Thank you!!!

Anonymous said...

Hello, i had the same issue as Zach's "Blogger zach welding said...
This works great except for one part. If I click a heading to expand it, but then decide I didn't want to click that and try to click it again to collapse it, it takes me to the Site Contents page. Is it like that for everyone? Please help!".
At first click on first levels item it expands, but on second click it does not collapse but links to destination url !

I'm not developer but is it possible to add a behavior on plus.gfi/minus.gif from : /_layouts/images/plus.gif wich would have been added to firt level items inner html so that the collapse/expand function act on them rather than on link ?

Anonymous said...

What is I wanted to add an up and down arrow to this. Down when its closed up when its open?

Thanks

Brendan Horner said...

For those with issues related to clicking submenus and it auto-collapsing: try adjusting this script so that instead of $(this).addClass('father'), make it $(this).children('span').addClass('father') and add .parent() right after $(this) inside everything in the IF statement. This causes the click event to not be on the submenu at all (so you can remove the event prevent propogation lines).

Anonymous said...

Brendan Horner

"add .parent() right after $(this) inside Everything in the IF statement"

But that will only be in one place in the code or am I wrong? If we go to the modified row we have:

$(this).Children('span').addClass('father').click(function () {...

In the if-statement (inside the brackets {}, not the statement), we only have $(this) once.

Or do you mean even outside the brackets? Please post the code :)

Anonymous said...

I was wondering if you had given any thought to how to have a collapsible tree when the left navigation is using managed meta data?
It is very surprising that Microsoft did not add this. Their example was a product tree. Without a collapsible tree the managed metadata navigation will only work if you have less than 20-30 items in the term set.

HELP!!!

Savin

Unknown said...

Hi, trying to add this using Script Editor on a page. The script doesn't seem to work. I copied / pasted the script in the script editor window, saved it, but the page just displays the text itself. Any ideas on how to make this work?

SM said...

@Francisco, this usually happens if you paste the script only, but not the script -tags.

@Anonymous asking about collapsible tree view, I haven't tried that so for the time being am not able to help you.

Unknown said...

I have followed and implemented all/most of the code discussed. In fact, I had implemented accordion code in SP 2010 for the quicklaunch and began to tweak my code to adhere to new tags for 2013. Most of the postings here do not adequately address all the requirements. The following code:
$(function qlNavAccordian() {
// Adds separator to bottom of each group
$("#sideNavBox ul.root>li.static").css("border-bottom","1px Solid #eaeaea");
$("#sideNavBox ul.root>li.ms-listMenu-editLink").css("border-bottom","0px Solid #eaeaea");
// added line below so no headers are intially selected - DOES NOT WORK AT THIS TIME
$('#sideNavBox ul.root>li.static').removeClass("selected");
// var to capture index value of parent items (menu item headers) used later for determining first header
var thisheader=-1
// traverse each parent item
$("#sideNavBox ul.root >li.static >a").each(function()
{
// Does this parent item have child ul tag elements ?
if($(this).parent().children("ul").size() > 0)
{
// Gets the index of the parent items to identify first header e.g. first header's index = 0
thisheader = $(this).parent().index();
// initial load hide - collapse all child items under parent items
$(">ul", $(this).parent()).hide();
$($(this).parent()).addClass("cssClosed");
$($(this).parent().selected).addClass("cssOpen");
// add default expand logic for first header - e.g. first header always is expanded when page renders
// comment out if code block below to remove functionality
if (thisheader == 0)
{
$(">ul", $(this).parent()).show("slow");
$($(this).parent()).css("display", "block");
$($(this).parent()).removeClass("cssClosed");
$($(this).parent()).addClass("cssOpen");

}
// end default expand logic first header
$(this).toggle(function()
{
$(">ul", $(this).parent()).show("slow");
$($(this).parent()).removeClass("cssClosed");
$($(this).parent()).addClass("cssOpen");
},
function()
{
$(">ul", $(this).parent()).hide("slow");
$($(this).parent()).removeClass("cssOpen");
$($(this).parent()).addClass("cssClosed");
}
);
// end toggle functions
}
});
});

CSS is:
.cssClosed {
BACKGROUND: url(/_layouts/images/Menu-right.gif) no-repeat 185px 7px
/* BACKGROUND: url(/_layouts/images/plus.gif) no-repeat 140px 7px */
}
.cssOpen {
BACKGROUND: url(/_layouts/images/Menu2.gif) no-repeat 175px 7px
/* BACKGROUND: url(/_layouts/images/minus.gif) no-repeat 130px 7px */
}

Anonymous said...

Amazing - you're making me look like a genius. thank you

Anonymous said...

Hi,

I added this to the master page header, and it worked, however on the pages that I have another accordion in the body it doesnt show the collapsible left navigation. I already tried changing the id of the accordion div so those 2 accordions can differentiate. But still nothing. Help?

Marin said...

In IE is possible that it wont work on standard page. But at the same time it works on system page.

Workaround is to wrap accordian with setTimeout().

Bill Branch said...

@mark des jardins and @SM
I have tried so many different suggestions and code...really hoping you can help:
There is a section under the code that states: "This piece of code assumes that..."
There are six items to configure but I have no idea where these exist in a master page and I am assuming that the master page for me is seattle.master???
For example...I search the seattle.master for the term SiteMapProvider and I get several results...which one do I change?
So, I hope you can help me with each of the six...thanks
bill.branch@nau.edu I would love to chat with someone to help me walk through it...thanks in advance :-)
Bill Branch

Thales Chemenian said...

Hey my friend, thanks for this post, really helpfull and make me earn alot of time.

AshwinSh said...

for testing just added a Script Editor web part and added the script. I worked as awesome. Thank you very much.

MarkD said...

Still works in SharePoint Online!

Thank you so much!

Steve said...
This comment has been removed by the author.
QuiTec said...

Thank you for sharing such a useful article. I had a great time. This article was fantastic to read. Continue to publish more articles on, keep it up. SharePoint: Configure Navigation Links to Open in a New Tab