An accordion effect is a nice way to efficiently use the limited space available for menus.

In this simple example the staring HTML is a nested <ul> list.

The HTML is:

  <nav>
    <ul>
       <li class="top">item 1
       <ul>
       	<li>item 1 Sub 1</li>
        <li>item 1 Sub 2</li>
        <li>item 1 Sub 3</li>

       </ul> 
       </li>
       <li class="top">item 2
       <ul>
       	<li>item 2 Sub 1</li>
        <li>item 2 Sub 2</li>
        <li>item 2 Sub 3</li>
       </ul> </li>

       <li class="top">item 3
       <ul>
       	<li>item 3 Sub 1</li>
        <li>item 3 Sub 2</li>
        <li>item 3 Sub 3</li>
        <li>item 3 Sub 4</li>
        <li>item 3 Sub 5</li>

       </ul> </li>
       <li class="top">item 4
       <ul>
       	<li>item 4 Sub 1</li>
        <li>item 4 Sub 2</li>
       </ul></li>
    </ul>  
   </nav> 

The CSS for this is as follows:

nav ul{
	margin:0;
	padding:0;
}
nav ul li{
	margin:0;
	padding:0;
	list-style:none;
    cursor: pointer;
    font-weight: bold;
    font-size : 14px;
    line-height: 30px;
    background: #e3e3e3;
    border-bottom: 1px solid #c5c5c5;
    border-top: 1px solid white;
	text-indent:15px;
}
nav ul li a{
	text-decoration:none;
	color:#666666;	
	text-indent:15px;
}
nav ul li:first-child{
	border-top: none;
}
nav ul li:last-child{
	border-bottom: none;
}
nav ul li ul{
	display:none;	
}
nav ul li ul li{
	font-weight:normal;	
	color:#666666;
}

The key line here is the display:none set for the child <ul> elements.

The jQuery is incredibly compact.

	   var menuItem = $("nav ul li.top");
	   menuItem.on("click", function(){  
		  var clickedItem = $(this).index();
		  menuItem.children("ul").each(function(i){
			 if(clickedItem != i){
					 $(this).slideUp(200);
			 }
		  });
		  $(this).children().slideToggle(400);
	   });

View the demo of this.

Explanation

Still here? Then you must want the explanation.

First up we create a jQuery selector for the top level menu items – those marked as class=”top” in the HTML.

An ‘on’ event is attached to this selector listening out for the ubiquitous click.

The goal is to have any menus that are already rolled down, roll up, and have the clicked menu roll down.

The roll down is easy to do and will be done last. To roll up any menus currently rolled down we’ll loop around the list and use the slideUp() method. The loop is performed with a jQuery each. To prevent the clicked item from rolling down before the loop is executed we find the index() of the clicked element.

The index() of the clicked element is an integer representing the order of the element amongst its siblings – that is all the other ‘top’ level menu items. With this valued stored as the variable clickedItem, we use an if statement inside the loop to prevent the slideUp() on the clicked list item.

Finally the children of the clicked element, that is all the child <li> elements are feed to the slideToggle() method to slide Up or Down the nested list dependent on its starting state.

Read more about animation methods.

Leave a Comment