How To Code a Vertical Accordion Nav Menu with jQuery

This page may contain links from our sponsors. Here’s how we make money.

Website navigation is an important aspect of 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’ll 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

Download Source Code

Coding a Vertical Accordion Navigation Menu

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 font used in the page heading.

<!doctype html>
<html lang="en-US">
  <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="https://www.vandelaydesign.com/favicon.ico">
  <link rel="icon" href="https://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>

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.

  <ul id="nav">
    <li><a href="#">Animation</a>
        <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>
    <li><a href="#">Graphic Design</a>
        <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>

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.

UNLIMITED DOWNLOADS: 50+ Million Add-Ons & Design Assets

Envato Elements Ad - Unlimited Downloads of Creative Assets

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.

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

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

    else if($(this).hasClass("open")) {

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 set up 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.

Vertical Accordion Nav Menu Preview

Download 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 tight 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.

Get the Free Resources Bundle