Choose a season:

State-Saving jQuery Accordion Menu Without Reloading the Page

Intro

After finding jQuery, I fell in love with it's ease of use and relatively small code footprint. When I set out to create this site, I wanted something that: used the entire page, scaled well, looked nice, degraded gracefully and was SEO friendly. What I found was jQuery's accordion menu. You may recognize the second example (#list1b) as a smaller version of my Portfolio; however, I wanted to tweak it (of course) and make it so each section housed my page copy and could be bookmarked and re-accessed without having to re-click on that section. I also completely re-styled the menu with a CSS file but that is beyond this tutorial.

What You'll Need

You'll first need the jQuery library found here. Next you'll need the UI found here. For the UI, you can customize what you put in the package, but for this tutorial you will at least need the UI Core and the Accordion widget.

jQuery Accordion Menu

The first thing you will need to do is create your accordion menu. You can copy and paste the following code into the body section of your HTML. For this example I'm using the ID "testMenu" as my container and the CLASS of "testMenuItem" to specify each menu item. Also, you may notice that in the HREF attribute, there is a hash mark followed by a number and under the name attribute there is that same number. This is needed for bookmarking and SEO and will come into play later; however, as long as the hash mark is there and whatever follows it is the same as what is in the name attribute i.e. href="#Yay" and name="Yay", you'll be fine.


<div id="testMenu">
	<a class="testMenuItem" href="#1" title="First Menu Item" name="1">First Menu Item</a>
	<div>
		<p>First menuItem text</p>
	</div>
	<a class="testMenuItem" href="#2" title="Second Menu Item" name="2">Second Menu Item</a>
	<div>
		<p>Second menuItem text</p>
	</div>
</div>

Next well stylize the menu. We do this by adding the following CSS into the head section of your HTML or an external style sheet. You can of course add more styling to the menu and text, but it isn't required.


<style>
#testMenu a.testMenuItem {
	cursor:pointer;
	display:block;
	margin-top: 0;
	text-decoration: none;
	outline:0;
	clear: both;
}
</style>

As of right now, this will just look like each section has a heading with a title and text. This is what it will look like without Javascript which is important to keep in mind for graceful degradation. The HREF links will even let you bookmark that section and when the page is reloaded you will be taken to that section. However, to make it into an accordion you will need to add javascript. You can do this very simply by adding the following javascript code into the head section of yourHTML. What this is telling jQuery is to make an accordion within the element with the ID of "testMenu" that is dynamic in height (autoHeight: false) uses navigation (navigation: true *this will come into play later*) and uses elements with the class of "testMenuItem" as a new menu item.


<script type="text/javascript">
jQuery().ready(function(){
	$('#testMenu').accordion({
		autoHeight: false,
		navigation: true,
		header: '.testMenuItem'
	});	
});
</script>

As I said before I'm using the ID "testMenu" as my container and the CLASS of "testMenuItem" to specify each menu item. You can easily change this, just make sure you change both the HTML, CSS and the Javascript. You should have something like this:

First Menu Item

First menuItem text

Second Menu Item

Second menuItem text

Making it State-Saving and SEO Friendly

Now you have a working jQuery accordion menu. What we'll do now is add some simple jQuery code to make sections bookmarkable and let us point users to a specific section. In order to do this we'll use a trick that lets you change the URL without the browser refreshing the page (which would ruin the cool animation). Normally you could use the Javascript code:


window.location.href="http://www.yourpage.com/section";

This would redirect your browser to yourpage.com and open the section you want. Of course, like I said earlier, this would require the browser to refresh the page which we don't want. Luckily, you can change the "hash" of the URL without reloading the page. Usually what the hash is used for is going to a specific part of the page, which is useful to us if the user doesn't have Javascript enabled. So what we'll do instead is use:


window.location.hash

So to put this to use we'll use jQuery to rewrite every element with a class of "testMenuItem" i.e. each menu item link, to have an onClick function that replaces the hash of the page with that of the current section. To do this all you have to do is copy the following Javascript code and paste it right under the end of the accordion script. No clue where that is? No problem, there is a finished example at the bottom to show you how everything is put together. So that code is:


	$(".testMenuItem").click(function(event){
		  window.location.hash=this.hash;
	 });

Without going too in-depth into how jQuery works, this will add an onClick (.click) function that changes the current URL hash (window.location.hash) into the sections hash (this.hash). Now, whenever you click on a menuItem, it will change the hash in the URL, then you can bookmark the page or reload it, and that same section will be the one selected! So for a finished version you can use this code:


<html>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="jquery-ui-personalized-1.6rc2.1.js"></script>
<script type="text/javascript">
jQuery().ready(function(){
	$('#testMenu').accordion({
		autoHeight: false,
		navigation: true,
		header: '.testMenuItem'
	});

	$(".testMenuItem").click(function(event){
		  window.location.hash=this.hash;
	 });	
});
</script>
<style>
#testMenu a.testMenuItem {
	cursor:pointer;
	display:block;
	margin-top: 0;
	text-decoration: none;
	outline:0;
	clear: both;
}
</style>
<body>

<div id="testMenu">
	<a class="testMenuItem" href="#1" title="First Menu Item" name="1">First Menu Item</a>
	<div>
		<p>First menuItem text</p>
	</div>
	<a class="testMenuItem" href="#2" title="Second Menu Item" name="2">Second Menu Item</a>
	<div>
		<p>Second menuItem text</p>
	</div>
</div>

</body>
</html>