Description

This script provides a non-JavaScript dependent tree menu. In the absense of JavaScript, the menu will automatically start minimized, thus remaining accessible to those without JavaScript enabled.

Demonstration

Implementation

The trick that makes this menu work is the fact that it uses JavaScript to minimize the menu as soon as it loads. Basically, it is set up like this. Each item is contained in a <div>. The subitems need to be contained in their parent's <div>. So, if you want a menu that has the first level item with one submenu item, and then one other top level item, the code would look like this:

<div id="menu_container">
	<div class="item"><span><img src="images/minus.gif" width="9" height="9"> one</span>
		<div class="item"><a href="#">two</a></div>
		<div class="item"><a href="#">three</a></div>
	</div>
	<div class="item"><a href="#">four</a></div>
</div>
<script type="text/javascript">
if (document.getElementById) { //if DOM compliant
	init();
}
</script>

Note how the two subitems are contained inside their parent item's <div>. Also, the bit of JavaScript that comes right after the menu is what tells compliant browsers to minimize the menu.

If you need certain items to start in the down position, you can use the startup variable at the top of the script to do this. There is a comment in the script directly below this variable that explains how it works.

Images Needed (right click -> save as)

Code

<style type="text/css">
#menu_container {
	color: #527FA8;
	background: #f5f5f5;
	border: 1px solid #819ebb;
	width: 200px;
	padding: 3px;	
}
.item {
	cursor: default;
	margin-left: 15px;
	display: block;
}
#menu_container a {
	color: #527FA8;
	background: transparent;
	text-decoration: none;
}
</style>

<script type="text/javascript">

/*#####################################################
# This script is Copyright 2003, Infinity Web Design  #
# Written by Ryan Brill - ryan@infinitypages.com      #
# All Rights Reserved - Do not remove this notice     #
#####################################################*/

startup = ""; //Comments on this below

/*
 * COMMENTS ABOUT THE startup VARIABLE -
 * Leave the startup variable blank to run as normal, enter 1 to start with the entire menu maximized. 
 * Additionally, each item can be either be displayed or not displayed in the following fashion:
 * 1|1|1|1|0|0|1|1 where 1's mean to display, and 0's mean not to display. So, in that
 * example, the first, second, third and fourth items would be displayed, the fifth and
 * sixth would not be displayed, and the seventh and eigth would be displayed on startup. Be sure
 * you set all top level items (items without submenus) to 1, or they will never be displayed!
*/

function init() {
	menu = document.getElementById("menu_container"); //get the container element
	spans = menu.getElementsByTagName("span"); //grab all the spans
	for (i=0; i<spans.length; i++) {
		spans[i].onclick = showhide; //set the onclick to run the showhide funcion for each span
	}
	/*Initialize the menu. For browsers without JavaScript enabled, it will remain in it's down state*/

	divs = menu.getElementsByTagName("div"); //grab all the divs
	
	str = "";
	
	allcookies = document.cookie; //get the cookies
	
	if (allcookies) {
		pos = allcookies.indexOf("menu="); //get the position of the start of our cookie
		if (pos != -1) {
			start = pos + 5; //set start to the beginning of our cookies value
			end = allcookies.indexOf(";", start); //get to the end of our cookie
			if (end == -1) {
				end = allcookies.length; //or get to the end of all cookies (if it is the last one)
			}
			str = allcookies.substring(start, end);	//grab our cookies value (from start to end)
		}
		else if (startup != "") { //if startup is not blank
			str = startup; //grab the values out of startup
		}
	}
		
	if (str.length > 0) { //if we found the cookie to set display to block or none
		str = str.replace(/1/g, "block"); //replace 1 with block
		str = str.replace(/0/g, "none"); //replace 0 with none
		str = str.split("|"); //split at the pipe
		while (str.length <= divs.length) { //if we have more divs that display (block|none) to fill it with
			str[str.length] = "block"; //fill array with blocks
		}
		for (i=0; i<divs.length; i++) { 
			if (str[i] == "block") { //if it needs to be block
				divs[i].style.display = str[i]; //display
			}
			else {
				divs[i].style.display = str[i]; //hide
			}
			if (divs[i].childNodes[0].childNodes[0].src != undefined && str[i+1] == "none") { //if div contains an image, and if the next div's display is none
				divs[i].childNodes[0].childNodes[0].src = "images/plus.gif"; //set image for down state
			}
		}
	}
	else {
		for (i=0; i<divs.length; i++) {
			child = divs[i].getElementsByTagName("div"); //grab all the child divs
			for (j=0; j<child.length; j++) {
				child[j].style.display = "none"; //hide
			}
			if (divs[i].childNodes[0].childNodes[0].src != undefined) { //if the div contains an image
				divs[i].childNodes[0].childNodes[0].src = "images/plus.gif"; //set image for down state
			}
		}
	}
}
function showhide() {
	obj = this.parentNode; //get parent element (div in our case)
	elems = obj.childNodes; //get child nodes
	for (i=0; i<elems.length; i++) {
		if (elems[i].tagName == "DIV") { //if child node is a div
			if (elems[i].style.display == "none") { //if elemnt is
				elems[i].style.display = "block"; //display
				this.childNodes[0].src = "images/minus.gif"; //set image for down state
			}
			else {
				elems[i].style.display = "none"; //hide
				this.childNodes[0].src = "images/plus.gif"; //set image for up state
			}
		}
	}
	menu = document.getElementById("menu_container"); //get the container element
	divs = menu.getElementsByTagName("div"); //grab all the divs
	val = "";
	for (i=0; i<divs.length; i++) {
		display = (divs[i].style.display == "" || divs[i].style.display == "block") ? 1 : 0; //set display to 1 or 0 depending on the display value of the div
		val += display+"|"; //concatenate the value
	}
	val = val.substring(0,val.length-1); //strip off the final pipe (|)
	document.cookie = "menu="+val;
}
</script>

</head>
<body>

<div id="menu_container">
	<div class="item"><span><img src="images/minus.gif" width="9" height="9"> one</span>
		<div class="item"><a href="#">two</a></div>
		<div class="item"><span><img src="images/minus.gif" width="9" height="9"> three</span>
			<div class="item"><span><img src="images/minus.gif" width="9" height="9"> four</span>
				<div class="item"><span><img src="images/minus.gif" width="9" height="9"> four</span>
					<div class="item"><a href="#">five</a></div>
				</div>
			</div>
		</div>
		<div class="item"><a href="#">six</a></div>
	</div>
	<div class="item"><a href="#">seven</a></div>
</div>
<script type="text/javascript">
if (document.getElementById) { //if DOM compliant
	init();
}
</script>
Comments:

Tested in: IE6, IE5.5, IE 5, IE 4, Netscape 7, Netscape 6 Netscape 4.7, Mozilla Firebird, Mozilla 1.4, and Opera 7.
Works or degrades well in: IE6, IE5.5, IE 5, IE 4, Netscape 7, Netscape 6 Netscape 4.7, Mozilla Firebird, Mozilla 1.4, and Opera 7.
Doesn't work or doesn't degrade work in: N/A

Copyright© 2003, Infinity Web Design