How To Code a Vertical Accordion Nav Menu with jQuery

Website navigation is an important aspect to any functioning layout. Users will often be looking for methods to traverse over your pages, and sometimes this requires a bit of creativity. I love the idea of vertical navigations especially with sub-menu links.

In this tutorial I will demonstrate how we can build a simple vertical navigation accordion menu using CSS3 and jQuery techniques. We can build custom styles and format the links to slide down & up on each click. Using this method we can also build sub-menu links, splitting up headers by ID or class names. Follow along with the ideas below and feel free to download a copy of my source code.

How To Code a Vertical Accordion Nav Menu with jQuery

Live DemoDownload Source Code

Document Structure

As with most of my tutorials, I am starting off with the typical HTML5 doctype and other extraneous scripts. These 3rd party docs include the latest jQuery library, the html5shiv document, and a custom Google Webfont used in the page heading.

<!doctype html>
<html lang="en-US">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  <title>Vertical jQuery Accordion Nav Menu</title>
  <meta name="author" content="Jake Rocheleau">
  <link rel="shortcut icon" href="http://www.vandelaydesign.com/favicon.ico">
  <link rel="icon" href="http://www.vandelaydesign.com/favicon.ico">
  <link rel="stylesheet" type="text/css" href="styles.css">
  <link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Merienda:400,700">
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
  <script type="text/javascript" language="javascript" charset="utf-8" src="nav.js"></script>
<!--[if lt IE 9]>
  <script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>

The other local files I am including are named styles.css and nav.js. All the page styles are fitted inside our stylesheet while I have separated the jQuery code into a new file as well. The actual page HTML is easy to follow since we are building a nav element using unordered list items.

<nav>
  <ul id="nav">
    <li><a href="#">Animation</a>
      <ul>
        <li><a href="http:/www.google.com/search?q=design+cartoons+animation">Cartoons</a></li>
        <li><a href="http:/www.google.com/search?q=design+comic+strips+inspiration">Comic Strips</a></li>
        <li><a href="http:/www.google.com/search?q=how+to+clip+video+footage">Video Clips</a></li>
        <li><a href="http:/www.google.com/search?q=design+create+animated+gifs">Web GIFs</a></li>
      </ul>
    </li>
    <li><a href="#">Graphic Design</a>
      <ul>
        <li><a href="http:/www.google.com/search?q=photoshop+tutorials+graphics+design">Adobe Photoshop</a></li>
        <li><a href="http:/www.google.com/search?q=digital+branding+graphics+logos">Branding & Logos</a></li>
        <li><a href="http:/www.google.com/search?q=graphics+design+marketing">Digital Marketing</a></li>
        <li><a href="http:/www.google.com/search?q=graphic+design+illustrations">Illustrations</a></li>
        <li><a href="http:/www.google.com/search?q=infographics+inspiration">Infographics</a></li>
        <li><a href="http:/www.google.com/search?q=product+design+inspiration">Product Design</a></li>
      </ul>
    </li>

The whole block is wrapped inside a <nav> element which is fitted using an unordered list. Each top-tier list item is given a link along with another internal <ul>. This secondary list is the actual list of links which will be displayed. The first set of list items are your navigation labels which behave as containers.


After clicking on each header we will show/hide the internal navigation links. This situation can get tricky when trying to implement sub-navigation and sub-sub-navigation if you want to show/hide those as well. A better solution is to nest a third <ul> element which is also displayed immediately with all the other links, but rendered using additional padding.

Styling Page Elements

The default CSS styles I follow are based on Eric Meyer’s CSS resets with some other custom properties. I always include the box-sizing property along with the vendor-specific prefixes defined using border-box. This forces all margins and padding to be limited at the maximum width and not to add any additional pixels to box containers.

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
  outline: none;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
html { height: 101%; }
body { font-size: 62.5%; line-height: 1; padding-bottom: 65px; font-family: Arial, Tahoma, sans-serif; background: #c5bde5 url('images/bg.png'); }

article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; }
ol, ul { list-style: none; }

blockquote, q { quotes: none; }
blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; }
strong { font-weight: bold; }

table { border-collapse: collapse; border-spacing: 0; }
img { border: 0; max-width: 100%; }

h1 { font-family: 'Merienda', 'Trebuchet MS', Verdana, sans-serif; font-size: 2.95em; line-height: 1.7em; margin-bottom: 20px; font-weight: bold; letter-spacing: -0.03em; color: #675d90; text-shadow: 2px 2px 0px rgba(255,255,255,0.65); text-align: center; }
Also I have setup each h1 header element to use our custom "Merienda" font family. Along with some cool text shadows, we can see a very unique effect in the page typography. I have applied similar text shadows onto the accordion text so the links are readable at a glance.

/* nav menu styles */
#nav {
  display: block;
  width: 280px;
  margin: 0 auto;
  -webkit-box-shadow: 3px 2px 3px rgba(0,0,0,0.7);
  -moz-box-shadow: 3px 2px 3px rgba(0,0,0,0.7);
  box-shadow: 3px 2px 3px rgba(0,0,0,0.7);
}

#nav > li > a {
  display: block;
  padding: 16px 18px;
  font-size: 1.3em;
  font-weight: bold;
  color: #d4d4d4;
  text-decoration: none;
  border-bottom: 1px solid #212121;
  background-color: #343434;
  background: -webkit-gradient(linear, left top, left bottom, from(#343434), to(#292929));
  background: -webkit-linear-gradient(top, #343434, #292929);
  background: -moz-linear-gradient(top, #343434, #292929);
  background: -ms-linear-gradient(top, #343434, #292929);
  background: -o-linear-gradient(top, #343434, #292929);
  background: linear-gradient(top, #343434, #292929);
}
#nav > li > a:hover, #nav > li > a.open {
  color: #e9e9e9;
  border-bottom-color: #384f76;
  background-color: #6985b5;
  background: -webkit-gradient(linear, left top, left bottom, from(#6985b5), to(#456397));
  background: -webkit-linear-gradient(top, #6985b5, #456397);
  background: -moz-linear-gradient(top, #6985b5, #456397);
  background: -ms-linear-gradient(top, #6985b5, #456397);
  background: -o-linear-gradient(top, #6985b5, #456397);
  background: linear-gradient(top, #6985b5, #456397);
}

The nav container itself is given a small box shadow and fitted to a maximum width of 280px. You can obviously adjust this container to fit your layout, which defines the purpose for using the extra nav element. But since we are using CSS3 gradient backgrounds the whole nav block is very fluid.

All the anchor links are targeted using parent-child relationships. The direct parent selectors arrow syntax follows that we only want anchor links directly inside an <li> contained inside the #nav element. This is also true on hover and when the navigation menu is open (via the applied CSS class .open).

Sub-Navigation CSS

One last key point to our stylesheet deals with the internal anchor links. Since each li element contains another <ul> for the links, we need to hide these by default. Then after the user clicks on a container link we display all the internal elements.

#nav li ul { display: none; background: #4a5b78; }

#nav li ul li a {
  display: block;
  background: none;
  padding: 10px 0px;
  padding-left: 30px;
  font-size: 1.1em;
  text-decoration: none;
  font-weight: bold;
  color: #e3e7f1;
  text-shadow: 1px 1px 0px #000;
}
#nav li ul li a:hover {
  background: #394963;
}

This is easily accomplished using the display: none; property on any ul element inside the navigation list items. For adding another level of navigation you wouldn’t need to hide anything else, because it will show/hide at the same time as the top links. But these can be targeted as another internal ul element like this: #nav li ul li ul a.

jQuery Accordion Effects

We have the entire block element displaying properly, so now we need to create the JavaScript animations. I have created a new document nav.js and opened the typical jQuery DOM function, checking the page document has finished loading before running any codes.

$(document).ready(function(){
  $("#nav > li > a").on("click", function(e){
    if($(this).parent().has("ul")) {
      e.preventDefault();
    }

My jQuery selector is targeting only specific anchor links found directly inside the #nav container. If the anchor has a sibling <ul> element then we know it has a navigation to display, and so we don’t want to load the href value when clicked. Then we need to check if the current link is already open, and if so arrange the other elements appropriately.

    if(!$(this).hasClass("open")) {
      // hide any open menus and remove all other classes
      $("#nav li ul").slideUp(350);
      $("#nav li a").removeClass("open");

      // open our new menu and add the open class
      $(this).next("ul").slideDown(350);
      $(this).addClass("open");
    }

    else if($(this).hasClass("open")) {
      $(this).removeClass("open");
      $(this).next("ul").slideUp(350);
    }
  });
});

The first if/else logic check determines if the current anchor does not have the class .open. If so we need to hide any already open menus on the page and then open our newest one. Otherwise we check if the link already has the class and the user clicked it to close. In this scenario we run .removeClass() and .slideUp() on the targeted list element.

You can actually customize the duration values inside the sliding functions. jQuery’s documentation page explains how you can setup the current duration value in milliseconds, and also set a custom easing profile via jQuery UI. For this demo I am using the basic “linear” easing effect. But the jQuery UI library is very small and you may consider using a different easing function in future projects. You can try out examples of these effects to determine if custom easing is something you would want to include.

How To Code a Vertical Accordion Nav Menu with jQuery

Live DemoDownload Source Code

Final Thoughts

I hope this tutorial can offer a solution for web developers who need to build relatively simple accordion-style widgets. Any typical website layout is often crammed with important content and lacking in additional space. Sometimes you just cannot fit a horizontal navigation into a fitted place. However you can easily implement this code into a sidebar or floating alongside the page content.

Definitely check out my live demo above and see how this menu works in modern browsers. If you have the spare time download a copy of my source code as well. You are free to play around and customize this to your own liking, and it should work beautifully within any project. Additionally let us know your thoughts or questions on the tutorial in our comments discussion area.

About the Author:

Jake is a freelance writer and frontend web developer. He can be found writing in many blogs on topics such as mobile interfaces, freelancing, jQuery, and Objective-C. Check out his other articles throughout Google and follow his tweets @jakerocheleau. Jake’s Google+ profile.

Looking for hosting? WPEngine offers secure managed WordPress hosting. You’ll get expert WordPress support, automatic backups, and caching for fast page loads.

41 Responses

Comments are now closed on this post.

  • Leo Ari Wibowo, December 19, 2012

    Bookmarked. Could use this for something. Thank you for sharing Jake.

  • harsha, December 22, 2012

    Neat tutorial , Thanks i got what im looking for !!

  • Steve Kent, December 30, 2012

    Thanks for posting, currently designing a website that needs something like this, will be giving it a try.

  • Toby, January 1, 2013

    Sweet tutorial!
    I’m only missing tab navigation. Which isn’t that hard to code myself but maybe it’s something you can add in the future.

    Keep up your awesome work!

  • Lisa, January 24, 2013

    This is really great- in your code, how would you have the sub-menu display on-hover, rather than on-click? So in your demo, I would want to hover over “Animation” to see its submenu, rather than click it first?

    Thanks!

  • Lisa, January 24, 2013

    Nevermind- just changed it in nav.js – duh. Thanks for this tutorial!

  • Marcy, January 31, 2013

    Love this accordion menu! Thanks for publishing it!

    This is going to sound like a dumb question but I’m new to this and I’ve never used jquery codes. Where do I insert the nav.js code?

    Also, for some reason I’m getting a bullet next to each header box. For example: to the left of “animation” (outside of the blue header).

    Hope this makes sense. Any help would be greatly appreciated!

    • Steven Snell, February 1, 2013

      Hi Marcy,
      If you download the source code from the link within the post you can see how everything should be set up. You can also compare your code to the demo to see what is different, and that should show why you are getting the bullet. You can use a tool like http://prettydiff.com to compare the code.

  • Arris, February 1, 2013

    Thank you for the Vertical Accordion Navigation Menu. I’ve been playing with the nav but was wondering if it is possible to have an arrow pointing to the right next to each main category then when its selected have an arrow pointing downwards?

    I have arrows to use but not sure how to code them for the categories.

    Thanks and I would greatly appreciate feedback.

  • Marcy, February 1, 2013

    Thanks Steven!

    I figured it out, thanks for the info! I’m now having issues with this not working properly (or at all) in IE8. Not sure about IE7 or 9. It is only showing bulleted lists here.

    Also has issues with FF in that the entire body of the website jumps or stretches when clicking on some of the nav headers (the ones with the most submenus)

    …. It works the perfectly in Chrome. Any ideas on this?

    my url is: http://www.springcreekrusticoutfitters.com

    Thanks!
    Marcy

    • Steven Snell, February 4, 2013

      Hi Marcy,
      I’m not sure if Jake intended it to be compatible with older browsers like IE7 or 8. Our demo is working fine for me in IE9 and FF. Unfortunately, I don’t have the time to troubleshoot your site. When the nav menu (or any code snippet for that matter) is added to a live site it could be some type of code conflict between the snippet and the code of your site.

  • Nigel, February 6, 2013

    Hi

    This is a nice little script. I have one small issue with it.

    If a link does not have a sub menu then it doesn’t do anything.

    Ideally if there are no sub menu items it should just link direct to the link address.

    I know it the code below but have no idea how to make it stop the link only if there is a child menu.

    if($(this).parent().has(“ul”)) {
    e.preventDefault();
    }

  • tyler, February 19, 2013

    love it but im going to need to take all the gradiance colors off it for my site i just want the txt.

  • Julie, March 5, 2013

    I love your script! I’m trying to add another level to this. Ex: #nav > li > ul > li > ul > li. No matter what I try, it either does not show the links at all on click or else displays all of them as open. The nav.js first line has $(“#nav > li > a”).on(“click”, function(e), I am learning (trying) to learn this and wondering if there maybe needs to be a level added to this to work? A push in the right direction would be very appreciated. Thanks!

  • Lou, March 17, 2013

    The script is amazing! but i have the same problem as Nigel. I only have one li item with a sub menu and 5 li item with a direct link.

    I think we need to change the nav.js!

    Thanks alot m8!

  • Axel, March 27, 2013

    Great code! Thanks for sharing.
    For Lou & Nigel, I resolve the issue remplacing

    if($(this).parent().has(‘ul’)) {
    e.preventDefault();
    }

    with

    var elem = $(this).next();
    if(elem.is(‘ul’)){
    e.preventDefault();
    }

    ++

  • Siourox, March 27, 2013

    Hi, love your script but indeed what happens for sub sub menus, they just don’t appear

    ie: li ul li ul li

    what modification code do we need to add to the nav.js

  • Matty, March 27, 2013

    Hi,

    I am having some issues with the code, it looks great in your example but on my site, when I click on a header it reverts back to the top of the page, please can you let me know what I need to change so the options open up in a dropdown?

    Kind Regards

  • Nigel, April 3, 2013

    Thanks Axel.. That did the trick.. I had to change the (‘ul’) to (“ul”) though. I think this page changed the ‘s to slanty ones, which broke it.

  • Juan, April 4, 2013

    Thanks you very much in advance Alex.
    After being playing with it, it results very helpful for me, now I’m trying to open the accordion from an external link. I mean, I have the accordion fixed in the leftside of my page, and once I select a link in the frontpage, I’d link to it links to another page where depending on the element selected, displays the new page with that leftside accordion already expanded on that category.
    Sorry for my explanation, cheers :)

  • Guus, April 8, 2013

    This script works well, but I cannot get it to work together with slimbox2!?

    Any ideas what the conflict between these two scripts is?

  • Guus, April 8, 2013

    How to keep an accordion menu state after link is clicked?

  • kelly watts, April 18, 2013

    I am unable to add submenus and sub-sub menus for some reason. The text implies that it works but I can’t make it happen. Seems that Siourox had the same issue. Is there any information on this. I love the menus but this feature is essential or I can’t use it. Thanks much

  • Matt, May 7, 2013

    Hi – is there a way to keep the menu open at a certain level? For example, if I have the menue on multiple pages and I land on an internal page (like if I landed on “Cartoons” in the example above), can I have the menu load with “Animation” open and “Cartoons” highlighted?

  • Guus, May 12, 2013

    Everything works well local, but on the server the sub-menu’s don’t stay open?

  • Guus, May 13, 2013

    I discovered the RewriteRule is causing the problem, but I don’t know why!?

    Options +FollowSymLinks -MultiViews
    # Turn mod_rewrite on
    RewriteEngine On
    RewriteBase /

    ## hide .php extension
    # To externally redirect /dir/foo.php to /dir/foo
    RewriteCond %{THE_REQUEST} ^[A-Z]{3,}s([^.]+).php [NC]
    RewriteRule ^ %1 [R,L,NC]

    ## To internally redirect /dir/foo to /dir/foo.php
    RewriteCond %{REQUEST_FILENAME}.php -f
    RewriteRule ^ %{REQUEST_URI}.php [L]

  • Guus, May 15, 2013

    Can accordion header act as a link to a page?

  • Nicolas, May 17, 2013

    Guus, check above for Alex answer from March 27th, 2013, it will answer your question and works well for me.

    Can someone tell me how to have a category open by default to see the sub-categories? (I think my question is the same as Matt)

  • Roque, May 18, 2013

    Right to the point! Thanks!

  • Lee, May 26, 2013

    Hi,

    Nice one…but I am trying to get the first accordion to open automatically when the page loads…how do i modify the code to accommodate this?

    Any help will be greatly appreciated :)

    -Lee

  • Guus, May 30, 2013

    I have a strange problem, there is one link on my site in the left menu that opens the right menu for no reason!?

    http://www.gerardkeune.nl/winkelkasten1

    Someone any idea how to solve this?

    Thanks,

    Guus

  • vicky, June 4, 2013

    hi,
    thanks for your good work just wanted to ask how can you put it such that when you click a particular sub menu all the other sub menu remain visible

  • Stacey Smith, June 5, 2013

    This is a whole lotta awesome!! Exactly what I was looking for! And thanks for answering the issue regarding having no submenu items – worked perfectly.

  • Blurry Eyes, June 6, 2013

    Hi,

    Nice compact script.

    I have been trying to work out how to activate one of the accordion drop downs from a link on different page on the same website, just can’t get it though.

    Any help would be appreciated.

  • Niranjan, June 8, 2013

    Great work
    worked nicely and smoothly

  • Stacey Smith, June 13, 2013

    Is there a way once an item is clicked to remember that state on the new page that is opened?

  • Khauz, June 28, 2013

    Same question of Stacy Smith…
    Done anyone know how to do it?! Please D:

  • Anni Davenport, July 6, 2013

    Yup I have the same question as Khauz and Stacey Smith, would be very handy!

  • Adam, August 9, 2013

    This is great! Is there an easy way to implement one of the list items to be open by default? That would be perfect for what I’m looking for.

  • nick, August 15, 2013

    this looks great, is it possible to have two background image icons for example have plus sign icon to expand a list and it then changes to a minus background icon to close?
    I have seen this done before just need to work out how :) thanks

  • nick, August 15, 2013

    cool just test this we can easily add plus minus icons as background images. just add the minus icon to the CSS tag #nav li a.open and the plus icon to the CSS tag #nav li a
    then align the right center. viola.

Close