Introduction
I was about running of ideas in tutorials, picking my own brain, and finally, I've almost forgotten the awesomeness of accordion. Yes, we will be creating a Accordion! Accordion has several characteristics:
- Normally, the menu is displayed vertically (I have seen a horizontal one though)
- Click on an item, it will expand its submenu and hide other submenu
- Usually, an Accordion has indicators to show the state of the menu
So, yes, we will do that with the minimal amount of code, clean html and good looking images.
1. HTML
In this section, we use UL List to form the structure. The first level UL will be the navigation menu, and the second level UL that resides inside each first level UL's LI will be the submenu.
There is some rules over here about the classes and rel attribute, let me explain:
- .popular, .category, .comment Classes in the anchor element (first level) are used for the styling of the menu.
- Rel atribute in the anchor element (first level) is used by javascript to add and remove "selected state" aka change the menu image after it was clicked
- .item class is required for each heading item
- .last Class is used to remove border bottom for the last item
<ul id="accordion"> <li> <a href="#" class="item popular" rel="popular">Popular Post</a> <ul> <li><a href="#">Popular Post 1</a></li> <li><a href="#">Popular Post 2</a></li> <li><a href="#" class="last">Popular Post 3</a></li> </ul> </li> <li> <a href="#" class="item category" rel="category">Category</a> <ul> <li><a href="#">Category 1</a></li> <li><a href="#">Category 2</a></li> <li><a href="#" class="last">Category 3</a></li> </ul> </li> <li> <a href="#" class="item comment" rel="comment">Recent Comment</a> <ul> <li><a href="#">Comment 1</a></li> <li><a href="#">Comment 2</a></li> <li><a href="#" class="last">Comment 3</a></li> </ul> </li> </ul>
2. CSS
CSS is pretty simple, we are using two UL Lists. So, what we do is, style the first level UL, skin it with images, and after that, style the second UL List and hide it.
Have you heard about CSS Sprite? CSS Sprites are the preferred method for reducing the number of image requests. Combine your background images into a single image and use the CSS background-image and background-position properties to display the desired image segment. Yes, this is the image we are using for this tutorial:
And, if you wish to learn more about CSS, you can read my previous posts:
/* First Level UL List */ #accordion { margin:0; padding:0; list-style:none; } #accordion li { width:267px; } #accordion li a { display: block; width: 268px; height: 43px; text-indent:-999em; outline:none; } /* Using CSS Sprite for menu item */ #accordion li a.popular { background:url(menu.jpg) no-repeat 0 0; } #accordion li a.popular:hover, .popularOver { background:url(menu.jpg) no-repeat -268px 0 !important; } #accordion li a.category { background:url(menu.jpg) no-repeat 0 -43px; } #accordion li a.category:hover, .categoryOver { background:url(menu.jpg) no-repeat -268px -43px !important; } #accordion li a.comment { background:url(menu.jpg) no-repeat 0 -86px; } #accordion li a.comment:hover, .commentOver { background:url(menu.jpg) no-repeat -268px -86px !important; } /* Second Level UL List*/ #accordion ul { background:url(bg.gif) repeat-y 0 0; width:268px; margin:0; padding:0; display:none; } #accordion ul li { height:30px; } /* styling of submenu item */ #accordion ul li a { width:240px; height:25px; margin-left:15px; padding-top:5px; border-bottom: 1px dotted #777; text-indent:0; color:#ccc; text-decoration:none; } /* remove border bottom of the last item */ #accordion ul li a.last { border-bottom: none; }
3. Javascript
There are two major sections in this javascript click event:
- First section: Reset everything back to default. What it does, hide all the submenus and also reset all the arrow to default position.
- Second section: Display the selected item and change the arrow direction
It gets a little bit tricky in resetting the arrow back to default position. I'm using for each method to loop thorugh the menu, grab its REL and and remove the classes. I think there are different ways to accomplish it. If you do know a better way, please let me know and I will ammend it.
$(document).ready(function () { $('#accordion a.item').click(function () { /* FIRST SECTION */ //slideup or hide all the Submenu $('#accordion li').children('ul').slideUp('fast'); //remove all the "Over" class, so that the arrow reset to default $('#accordion a.item').each(function () { if ($(this).attr('rel')!='') { $(this).removeClass($(this).attr('rel') + 'Over'); } }); /* SECOND SECTION */ //show the selected submenu $(this).siblings('ul').slideDown('fast'); //add "Over" class, so that the arrow pointing down $(this).addClass($(this).attr('rel') + 'Over'); return false; }); });
Conclusion
Like this tutorials? You can express your gratitude by visiting my sponsors on the sidebar, bookmark it and help me to spread this tutorial to our friends! :) Thanks!
Comments will be moderated and
rel="nofollow"
will be added to all links. You can wrap your coding with[code][/code]
to make use of built-in syntax highlighter.add that line to your css file and put selected in the level that you want it to be displayed by default.
<ul id="accordion">
<li class="selected">
.......
try that
add this to the last line of css:
#accordion ul.selected {display:block !important}
then, put it to the first ul.
<ul class="selected">
if ($(this).children('ul').is(':visible')) $(this).children('ul').slideDown('fast');
and i see that you said :oops, line 21.
But replacing line 21 with the code mentioned just breaks it. Can you list the full <script /> section with it included so that when you click on an already open section it will close? Or give a bit of instruction on doing so? Cheers! great post by the way!
var $target = $(event.target);
if ( $target.is('ul li a') ) {
return true;
}else{
return false;
};
This allows a link to work if its a lower ul, if its an upper li however it causes the opening of the accordion. also had to add this to the beginning to make them all close:
$('#accordion li').children('ul').hide();
then, put it to the first ul.
<ul class="selected">
But not seems to be working
<li class="popular">
<a href="#" rel="popular">Popular Post</a>
<ul>
<li><a href="#">Popular Post 1</a></li>
<li><a href="#">Popular Post 2</a></li>
<li><a href="#" class="last">Popular Post 3</a></li>
</ul>
</li>
li.popular a {all the style for the label}
li.popular li a {you will need to clear all the style from the first line, it will get inherited}
$(document).ready(function () {
$(' #accordion li').children('ul,p').hide();
$('#accordion li').click(function () {
$(this).children('ul,p').slideUp('normal');
$('#accordion li > a').each(function () {
if ($(this).attr('rel')!='') {
$(this).removeClass($(this).attr('rel') + 'Over');
}
});
if($(this).children('ul,p').is(':hidden') == true) {
$(this).children('ul,p').slideDown('normal');
$(this).children('a').addClass($(this).children('li a').attr('rel') + 'Over');
return false;
}
});
});
$('#accordion a.item').click(function (e) {
//remove all the "Over" class, so that the arrow reset to default
$('#accordion a.item').not(this).each(function () {
if ($(this).attr('rel')!='') {
$(this).removeClass($(this).attr('rel') + 'Over');
}
$(this).siblings('ul').slideUp("slow");
});
//showhide the selected submenu
$(this).siblings('ul').slideToggle("slow");
//addremove Over class, so that the arrow pointing downup
$(this).toggleClass($(this).attr('rel') + 'Over');
e.preventDefault();
});
I use it with mouseover:
$(document).ready(function () {
$('#accordion a.item').mouseover(function () {
//slideup or hide all the Submenu
$('#accordion li').children('ul').slideUp('slow');
//remove all the "Over" class, so that the arrow reset to default
$('#accordion a.item').each(function () {
if ($(this).attr('rel')!='') {
$(this).removeClass($(this).attr('rel') + 'Over');
}
});
//show the selected submenu
$(this).siblings('ul').slideDown('slow');
//add "Over" class, so that the arrow pointing down
$(this).children('a').addClass($(this).children('li a').attr('rel') + 'Over');
return false;
});
});
Can anybody tell me how i can slideUp all when i go outside with the mouse from the #accordion ?
Just wondering if it's possible to keep the menu open while I click through the sub menu links.
Currently what happens when I click a sub menu link is it takes me to the page, but on that new page the menu is closed and I have to reopen it.
Thanks
I'm not a programmer so I'm having a hard time trying to figure out how to make the menu item that is displayed at a time, maintain the "on" look you get from the :hover function.
Meaning, that if you click on it, for instance in the example you have here, the arrow will stay down, on click. Then go back to it's initial state when you click on another menu item.
I've seen the "Over" function on the js, but not the "active" or anything like that.
I hope I made myself clear.
It's ok that the different list items stay opened when clicking on another one. But I would like to at least have the list items that are "opened" to keep their "hover" state. So they look as if they're "turned on" or "active".
i was wondering how i might make it so that not only do the main menu items have their open state when you are on that particular page, but also when you are on any of the submenu pages... i'd like the main menu item to stay "on" and the submenu items to stay expanded as long as i'm in that section - either on the main menu or any of it's sub-pages... how would i do this...
i would also like to have "on" states for my submenu items...