A simple jQuery menu with persistence using cookies

Recently I’ve been making a concerted effort to learn jQuery the JavaScript framework as opposed to just using all the wonderful plugins off the shelf.

Recently I needed a bit of code to show and hide a navigation menu, but with persistence using cookies so as you move from page to page it can remember which sections to show. I was pretty confident, using bog standard JavaScript I knew I could knock it out really quickly, but The whole point of learning something new is to learn something new so I decided to do it using jQuery. I was pretty confident, last week I wrote some quite complex form validation code in a fraction of the time I could’ve done it in without using jQuery (it’s that pretty but it works well and it was my first attempt to do anything at all complex).

Getting the menu to work has been quite a struggle and I had to spend a surprising amount of time getting it to work, and judging by the posts on various blogs and groups a lot of other people have been stumped by this one too.

Anyway here is my solution, if anybody can help me simplify it further, their help would be greatly appreciated. Obviously you need to download jQuery, you will need to include the code and you will need an unordered list to act as the menu, in this example id=”#demo-menu”.

A couple of posts have been really invaluable figuring out how to do this, so credit where it’s due, thanks:

View the working example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
$(document).ready(function() {
 
		$("#demo-menu > li > a").not(":first").find("+ ul").slideUp(1);
 
		$("#demo-menu > li > a > span").text('+'); // add an indicator to the menu items to show there is a child menu
 
		$("#demo-menu > li> a").each(function() {
			toggleMenu(this);
			checkCookie(this);
		});
 
 
 
		function checkCookie(id)
			{
				/*
 
						check if there is a cookie set for a sub menu 
						if there is then show the menu
 
				*/
 
				var cookieName = id.id;
 
				var c = readCookie(cookieName);
 
				if(c === 'show') {
 
					$(id).each(function() {
 
						$(this).children("span").text('-');
						$(this).find("+ ul").slideDown('fast');
 
					});
 
				}
			}
 
		function toggleMenu(id)
			{
				$(id).click(function() {
					/*
							toggle the +/- indicators
					*/
					togglePlusMinus(this);	
 
					/*
						toggle the menu open or closed
					*/
					$(this).find("+ ul").slideToggle("fast");
 
				});
			}
 
		function togglePlusMinus(id)
			{
 
				$(id).each(function() {
 
					if($(this).find("+ ul").is(':visible'))
						{
							$(this).children("span").text('+');
							eraseCookie(this.id);
						}
					else
						{
							$(this).children("span").text('-');
							createCookie(this.id, 'show', 365);
						}
 
				});
			}
 
});
 
// cookie functions http://www.quirksmode.org/js/cookies.html
 
function createCookie(name,value,days)
	{
		if (days)
		{
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	}
function readCookie(name)
	{
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++)
		{
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}
function eraseCookie(name)
	{
		createCookie(name,"",-1);
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<ul id="demo-menu">
	<li><a href="#">item one</a></li>
	<li><a href="#">item two</a></li>
	<li><a href="#" id="three" >item three<span></span></a> 
		<ul>
			<li><a href="#">item 3.1</a></li>
			<li><a href="#">item 3.2</a></li>
			<li><a href="#">item 3.3</a>
 
			</li>
		</ul>
	</li>
	<li><a href="#">item four</a></li>
	<li><a href="#" id="five">item five <span></span></a> 
		<ul>
				<li><a href="#">item 5.1</a></li>
				<li><a href="#">item 5.2</a></li>
				<li><a href="#">item 5.3</a></li>
		</ul>
	</li>
	<li><a href="#">item six</a></li>
 
</ul>

5 Responses to “A simple jQuery menu with persistence using cookies”

  1. Jon

    I added a third level navigation under item 3.1 but it always starts opened when item three is clicked. Can it be made to start closed? Also, my preference would be to close item three if, for example, item five were clicked. Can that be accomplished? Thank you.

  2. Flipflops

    Hi Jon

    In principle it could do both of these thing, but it would need tweaking – but I do intend to keep adding / modifying the code – particularly the multi level code. The multi level code should be easy enough – just need to make the function recursive somehow (adjusting the jquery selector to allow this I suppose)

    I think the best way to have the menu start closed is to just apply a class to the sub menu(s) so that when the page loads but before any JavaScript runs the sub menu is hidden purely by CSS.

  3. Christopher

    Have you taken a look back at your code since you wrote that last comment in January?

    I too am interested in accomplishing what Jon was asking about … having the sub menus closed on page loading, and also some way of having only one sub menu opened at a time — having it automatically close any other sub menu that is currently active.

    I’ve tried tweaking the code to no avail, and I just don’t know enough about jquery to be able to write the proper functions to accomplish this.

    Do you think you could please help?

  4. Flipflops

    Hi Christopher

    Sorry started fiddling around, found that what I wanted to do was harder than it looked and forgot about it. But I’ll have a look for you.

    Making the menu closed when the page loads basically just involves removing the cookie code, so the menu has no memory…

    Makings sure that only one sub menu opens at a time should just involve closing all the sub menus first or something like that…

  5. Flipflops

    Hi Christopher

    Have got a solution for you.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    <script langauge="javascript">
    $(document).ready(function() {
     
    	$("#demo-menu > li > a").not(":first").find("+ ul").slideUp(1);
     
    	$("#demo-menu > li > a > span").text('+'); // add an indicator to the menu items to show there is a child menu
     
    	$("#demo-menu > li> a").each(function() {
    		toggleMenu(this);
     
    	});
     
     
    	function toggleMenu(id) {
    		$(id).click(function() {
    			var flag = 1;
     
    			if($(this).find("+ ul").is(':visible')) {
    				// set flag to hide the menu we have just clicked on if it is open - as we have clicked to close it.
    				flag = 0;
    			}
     
    		$("#demo-menu > li> a").each(function() {
     
    			/*
    			* hide all the menus
    			*/
     
    			if($(this).find("+ ul").is(':visible')) {
    					$(this).find("+ ul").hide();
    					$(this).children("span").text('+');
    			}
    		});
     
    		if(flag == 1) {
    			$(this).find("+ ul").slideDown(500);
    			$(this).children("span").text('-');
    		}
     
    			});
    	}
     
    });
    </script>

    Just drop the above script in place of the cookie using code. This will have all the menus closed on startup and will only allow one sub menu to be open at a time.

    John