How to build persistent expandable/collapsible navigation with jQuery and the jQuery Cookie plugin

June 26th, 2009. Posted in CSS, Navigation, Plugins, jQuery

If you have any experience with jQuery, one of the first things you probably learnt to do was show and hide content. jQuery makes this very simple with it’s built in show() and hide() methods, but sometimes it can be useful for the show/hide state of your items to persist from page to page. This allows the user to have a more personal experience of your site or web application by refining the information they are presented with to what is important to them.

In this tutorial I will build a jQuery plugin which remembers the closed/open state a side navigation menu, which conveniently follows the structure of the sidebar navigation in Wordpress.

View finished example.

The finished plugin will work with any list that follows this structure, begin by creating a new HTML page with the following markup in it:

  • Archives

    • Nav item
    • Nav item
    • Nav item
    • Nav item
    • Nav item
  • Categories

    • Nav item
    • Nav item
    • Nav item
    • Nav item
    • Nav item

This plugin requires the jQuery cookie plugin, download it and save it as in your root folder.

Create a new file called collapsibleNav.js and inlcude the following files in the document <head> tag:

	
	
	

Open up your new collapsibleNav.js file, start the plugin and give it some default variables:

jQuery.fn.collapsibleNav = function(options) {

	var defaults = {
		hideByDefault: 'categories,archives', //ids of elements you want hidden by default
		clickController: 'h2', //your heading elements
		slideSpeed: 150 //speed of animation
	};

	//initial variables
	var opts = jQuery.extend(defaults, options),
		$list = jQuery(this), //reference list passed to plugin
		$heading = $list.find(opts.clickController), //cache heading element
		$listItems = $list.children(), //get each top level list item
		closedItems; //define var for items to be stored in cookie

Now create the following function. This will be called each time a list heading is clicked and will add the id of the list item to the cookie, or remove it if it is already in the cookie:

	//function that is called when slidable is clicked
	function updateArray(e) {

		//get id of element clicked for adding to closed items array
		var eId = jQuery(e).attr('id');

		//look for element in cookie array
		var arrPos = jQuery.inArray(eId, closedItems);

		if (arrPos == -1) {

			//element is not in array, so add it
			closedItems.push(eId);

		} else {

			//element is in array, so remove it
			closedItems.splice(arrPos, 1);

		}

		//update cookie
		jQuery.cookie('closedItems', closedItems, { path: '/', expires: 365 });
	}

We now need to check if the jQuery Cookie plugin has been included, and if so do the following: if there is no cookie with our closed items in it: create a new cookie with the elements you want hidden by default in, if there is a cookie present: apply a “closed” class to all of the list-items with their id in the cookie, then hide the child ul element of list items with a “closed” class.

	//remember closed/open state of nav items

	//check that the cookie plugin is available
	if (jQuery.cookie) {

		/*
		  if no cookie called "closedItems" exists, then create one with
		  elements you want hidden by default.
		  '1' is given as first value to prevent cookie from being
		  deleted if it contains no ids
		*/
		if (!jQuery.cookie('closedItems')) {
			jQuery.cookie('closedItems', '1,'+opts.hideByDefault, { path: '/', expires: 365 });
		}

		//if cookie called "closedItems" exists
		if (jQuery.cookie('closedItems')) {

			//split cookie into array
			closedItems = jQuery.cookie('closedItems').split(',');

			//iterate through array and apply "closed" class to each element within it
			for (var i = 0; i < closedItems.length; ++i) {
				jQuery('#' + closedItems[i]).addClass('closed');
			}

			//hide child ul for list items that have a class of closed
			jQuery('.closed').children('ul').hide();
		}
	}

We will now add an icon to each heading to indicate that it can be clicked to show/hide content. It is important to add this with JavaScript and not write it directly into the markup, as we only want people with JavaScript enabled to know that the headings can be clicked.


	//add span to each heading
	$heading.append(' ');

We now need to wire up each heading so that when it is clicked it hides or shows the content and sends the element clicked on to our updateArray function. We will finish by returning the jQuery object so that our list can be used again via chaining.

	$heading.click(function(){

		var $target = jQuery(this),
			$parent = $target.parent();

		//toggle closed class
		$parent.toggleClass('closed');

		//show/hide content
		$target.next().slideToggle(opts.slideSpeed);

		//update cookie with current list-item
		updateArray($parent);

		return false;

	});

	return this;

};

Finally we need to trigger the plugin on our list, add the following javascript between script tags in your HTML document after all of the other script tags:


	jQuery(function(){
		jQuery('#aside').collapsibleNav();
	});

I have added some basic css in the example, but I'm sure you can come up with something more creative!

Thats it!

View finished example.

I hope you enjoyed this post, if you did, please share it with others!

Bookmark and Share

11 Responses to “How to build persistent expandable/collapsible navigation with jQuery and the jQuery Cookie plugin”

  1. Martin says:

    hey thanks!

    this proved helpful.

  2. zac says:

    hi. Thanks for writing this.. it is the closest I have found to help me figure out how to use the cookie plug-in but still I am a bit lost. I am trying to achieve much simpler functionality.. just have a message box that is on by default but if someone x’s out of it then it disappears (at least until cookies are cleaned). You have much greater functionality with the toggle but mine is just a one time deal so I know it will be much simpler code…having trouble whittling yours down… How would I create the closedItems cookie when someone uses a button with the .close class? If I had that then I guess all I would need is this snippet of your code : /remember closed/open state of nav items

    Thanks for sharing and any help to get me moving along would be much appreciated.

  3. Jim says:

    Very helpful! Thanks!!!

  4. Excellent, thanks for the clear explanation and examples.

  5. Jason Ryan says:

    Thanks for the example. It was easy to implement and customize. I am however have a slight issue. One of my lists are getting hung open upon entering the site, even after deleting cookies. Any thoughts? Thanks.

  6. Jason Ryan says:

    lol, pay no mind to the previous comment. I figured it out. Thanks again for the tutorial, it’s much appreciated!

  7. Jason Ryan says:

    Actually….sorry. I just have one question. Is there a way to make list items close as you click others? I am using this with multiple lists and they have gotten pretty long and it would be a little more user friendly if each list closed as another opened. I’ve tried to mess with it a little but to no avail. Any guidance would be greatly appreciated.

  8. John Fox says:

    Scott, many thanks for your post. Really helped me with a rather simple version of your script.

    Posted linkback to your post from here:
    http://web-kreation.com/index.php/tutorials/nice-clean-sliding-login-panel-built-with-jquery/ comment #281

  9. Jammie says:

    Scott, great tutorial! One of the best I’ve found, hands down. I do have a question. While it seems the code works on every browser I’ve thrown at it, except one aspect in IE.

    When a user has ‘Compatibility View’ mode on in IE it seems it places the “plus/minus” span on the next line below the tag.

    Is there any work around to that? Thanks.

  10. Caroline says:

    Hi Scott, Thanks for the tutorial, I’m using it to set up a menu for wordpress using the wp_list_pages, and I have changed the clickController to a, to make it work with this. The only problem is that when the page is reloaded if both lists are shut they open, or if only one is shut it reopens. I’m trying to understand how this works, and I think it may be because the lists also have links in them, is there a simple way to work around this?

    Many thanks,

    Caroline

  11. andi says:

    Hello Scott, fantastic tutorial, thanks for writing it.

    is there any way to hide by default all IDs of the Nav, rather than declare the individual IDs in the JS file?
    hideByDefault: ‘categories,archives, etc, etc’,

    regards,
    andi

Leave a Reply

Recent Posts

Categories

Archives