site Menu

Introduction

When I first started writing the site I didn't realise how many pages I would eventually write. The old menu system consisted of two sets of links on each page, one for the various sections of the site and another set for each page for the section the user was actually in. This was repeated at the top and bottom of each page. This worked quite well but eventually some pages ended up being more menu than content. Not only that but every time I added a page to a section all the links in the pages in that section had to be rewritten. If I added a new section then every page on the site had to have the links changed. Most of the work was done by the excellent ReplaceEm - a mult-page multiple line text search and replace utility. What happened then was that every changed page had to be uploaded to the servers.

The old menu system

The old menu system

Concept, Inspiration and Sources

In June 2005, I finally got round to updating the site. I made far more use of CSS, cleaned the layout a bit and looked around for a new menu system. The menu had to consist of one page used throughout the site. Because of the number of sections and pages it should be collapsible and it should be cross-browser, cross-platform compatible. I didn't want to use frames as I want people to be able to bookmark the pages, frames make this next to impossible because the address bar always shows the frameset not the individual page. This meant that for ease of use I had to use iframes.

What I eventually came up with was based on the menus at Benefit from IT and Kryogenix and the sites listed there. One major problem I had when designing the code is that Microsoft's Internet Explorer does not understand the CSS code position: fixed; A Google search found the Tagsoup site and so that code was incorporated into my pages too.

Between the code offered by these sites and my own ideas what I came up with is the menu you see to the left which should look like the images below...

The new menu system - all closed The new menu system - all open

The new menu system - all closed and open

The Files

The menu is based on a simple unordered list. When the page is initially opened the full menu is shown, for those with JavaScript enabled the menu will close all the sub-menus. This means that even if JavaScript is disabled the menu is still usable.

Altogether, the menu requires 10 files to work properly, a hideous amount but I can't see how to reduce this number. These files consist of...

2 HTML pages - the calling HTML page like this one and the menu HTML page.
2 JavaScript files - brismenu.js which controls the main functions of the menu and brismenux.js which controls extras like what happens when the open and close all buttons are clicked.
3 CSS files - brismenu.css that controls the appearance of the menu, indentations, which of the images should be displayed and so on. brisie.css that controls the menu behaviour in Microsoft's Internet Explorer and brisall.css that controls the behaviour in other browsers.
3 GIF image files, minus.gif and plus.gif to denominate the open and closed menu items and bullet.gif to denominate individual pages.

Menu Code

The menu HTML page is a simple unordered list, the only exception to this is that the main list UL has been given the class "mmclickable" and each of the sub lists has been given the class "mopen". The BASE tag is needed so that the chosen menu item appears in the browser window and not the menu div and iframe. The simplified code for the page is...

<!DOCTYPE >
<html>
<head>
...
...
...
<link rel="stylesheet" type="text/css" href="brismenu.css">
<script type="text/javascript" src="brismenu.js"></script>
<script type="text/javascript" src="brismenux.js"></script>
<base target="_top">
</head>
<body>
<input type="button" class="mbtn" onclick=doall("mclosed","mopen"); value="Open all">
<input type="button" class="mbtn" onclick=doall("mopen","mclosed"); value="Close all">
<br><br>
<!-- Folders are LI, its items are a nested UL -->
<!-- Default is all open for non JavaScript users -->
<ul class="mmclickable">
<li><a href="..">A document</a></li>
<li><a class="mopen" href="..">Section A</a>
<ul>
<li><a href="..">A document belonging to section A</a></li>
<li><a class="mopen" href="..">A sub-menu (A1) belonging to section A</a>
<ul>
<li><a href="..">A document belonging to sub-menu A1</a></li>
<li><a href="..">A document belonging to sub-menu A1</a></li>
<li><a href="..">A document belonging to sub-menu A1</a></li>
</ul>
</li>
<li><a href="..">A document belonging to section A</a></li>
<li><a class="mopen" href="..">A sub-menu (A2) belonging to section A</a>
<ul>
<li><a href="..">A document belonging to sub-menu A2</a></li>
<li><a href="..">A document belonging to sub-menu A2</a></li>
</ul>
</li>
<li><a href="..">A document belonging to section A</a></li>
</ul>
</li>
<li><a class="mopen" href="..">Section B</a>
<ul>
<li><a href="..">A document belonging to section B</a></li>
<li><a class="mopen" href="..">A submenu (B1) of section B</a>
<ul>
<li><a href="..">A document belonging to sub-menu B1</a></li>
<li><a href="..">A document belonging to sub-menu B1</a></li>
<li><a href="..">A document belonging to sub-menu B1</a></li>
</ul>
</li>
<li><a href="..">A document belonging to section B</a></li>
</ul>
</body>
</html>

Calling Code

Each content page has certain code in common. This is the code that calls the css file, brisray.css, that contains the display code for the content pages. The rest of the code calls brisie.css and brisall.css that ensures the menu stays fixed in the browser window in IE and all other browsers. The DIV tags splits the page up so that the menu iframe can be displayed in one and the content in the other.

<!DOCTYPE>
<html>
<head>
...
...
...
<LINK rel="stylesheet" type="text/css" href="../common/brisray.css">
<!-- The code below fixes the menu bar in IE and other browsers. -->
<!-- The style absolute: fixed is not handled equally well by all browsers -->
<STYLE type=text/css media=screen>@import url( ../common/brisall.css );</STYLE>
<!--[if IE]>
<LINK media=screen href="../common/brisie.css" type=text/css rel=stylesheet>
<SCRIPT type=text/javascript>
onload = function() { content.focus() }
</SCRIPT>
<![endif]-->
</head>
<BODY>
<DIV id=menubar><iframe id=menufrm src="../common/menu.htm"></iframe></DIV>
<DIV id=content class="ctr nrmltxt">
...
...
...
</div>
</body>
</html>

brismenu.js

The code in this file collapses, if JavaScript is enabled,  the menu as soon it is opened by renaming the class "mopen" to "mclosed" and processes the images so that they are clickable along with the href's.

/*
mmclickable.js

Converts an unordered list to an explorer-style tree, with clickable
icons

To make this work, simply add one line to your HTML:
<script type="text/javascript" src="mmclickable.js"></script>

and then make the top UL of your nested unordered list of class
"mmclickable".

*/

addEvent(window, "load", makeTreesC);

function makeTreesC() {
// We don't actually need createElement, but we do
// need good DOM support, so this is a good check.
if (!document.createElement) return;

uls = document.getElementsByTagName("ul");
for (uli=0;uli<uls.length;uli++) {
ul = uls[uli];
if (ul.nodeName == "UL" && ul.className == "mmclickable") {
processULELC(ul);
}
}
}

function processULELC(ul) {
if (!ul.childNodes || ul.childNodes.length == 0) return;
// Iterate LIs
for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
var item = ul.childNodes[itemi];
if (item.nodeName == "LI") {
// Iterate things in this LI
var a;
var subul;
subul = "";
for (var sitemi=0;sitemi<item.childNodes.length;sitemi++) {
var sitem = item.childNodes[sitemi];
switch (sitem.nodeName) {
case "A": a = sitem; break;
case "UL": subul = sitem;
processULELC(subul);
break;
}
}
if (subul) {
associateELC(a,subul);
} else {
a.parentNode.className = "mbullet";
}
}
}
}

function associateELC(a,ul) {
if (a.parentNode.className.indexOf('mopen') == -1)
a.parentNode.className = 'mclosed';
a.onclick = function () {
this.parentNode.className = (this.parentNode.className=='mopen') ? "mclosed" : "mopen";
return false;
}
}

/* Utility functions */

function addEvent(obj, evType, fn){
/* adds an eventListener for browsers which support it */
if (obj.addEventListener){
obj.addEventListener(evType, fn, true);
return true;
} else if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else {
return false;
}
}

brismenux.js

The code in this file provides functionality for the open and close all buttons on the menu page. All it really does is, depending on what button was clicked, rename the class "mopen" to "mclosed" or vica versa.

function doall(currentclass, newclass){
// Open or close all menu items
// Look for all LI elements
uls = document.getElementsByTagName("li");
for (uli=0;uli<uls.length;uli++) {
ul = uls[uli];
// Check and change the class name
if (ul.nodeName == "LI" && ul.className == currentclass) {
ul.className = newclass;
}
}
}

brismenu.css

This file controls the indentation and margins of the menu items, adds the appropriate image as part of the URL so that they become clickable, shows or hides the menu items depending on the class "mopen" or "mclosed" as well as doing the usual CSS things like setting the colours.

body { background-color: #66FF99; }

/* Buttons */
.mbtn { background-color: #66FF99; }

/* Turn off list bullets */
ul.mmclickable li { list-style: none; }

/* Set the ul indents */
ul.mmclickable ul { padding-left: 10px; margin-left: 10px; }

/* Set left hand main margin */
ul.mmclickable { margin-left: 3px; padding-left: 3px; }

ul.mtreeclickable, ul.mtreeclickable ul, ul.mtreeclickable li { margin: 0; padding: 0; }

/* Provide space for our own "bullet" inside the link */
ul.mmclickable li a { padding-left: 20px; }

/* Show "bullets" in the links, depending on the class of the
LI that the link's in */
ul.mmclickable li.mopen a {
background: url(minus.gif) top left no-repeat;
}
ul.mmclickable li.mclosed a {
background: url(plus.gif) top left no-repeat;
}
ul.mmclickable li.mbullet a {
background: url(bullet.gif) top left no-repeat;
}

/* Actually show and hide sublists */
ul.mmclickable li.mopen ul { display: block; }
ul.mmclickable li.mclosed ul { display: none; }

In the preceeding code you'll notice that some elements such as the ul indents and margins have both the margin-left and padding-left set. This is because of the way in which some browsers work.

Intended spacing Default spacing

The intended and default spacing

If only margin-left is used then Windows MSIE 6.0.29 and Opera 7.54, MAC MSIE 5.2 show the intended spacing. Windows Mozilla Firefox 1 and Netscape Navigator 7.2, Mac Netscape Navigator 7.2 and Safari 1.0.3 and Linux Konqueror 3.4.1 and Linux Epiphany 1.7.1 show the default. If only padding-left is used then the situation is reversed.

brisie.css

Microsoft's Internet Explorer will not recognise the CSS code position: fixed; as other browsers do. This file ensures that it does.

BODY {
OVERFLOW: hidden; HEIGHT: 100%
}
DIV#content {
OVERFLOW: auto; WIDTH: 100%; HEIGHT: 100%
}

brisall.css

This file is for all browsers except Microsoft's Internet Explorer.

BODY {
PADDING-RIGHT: 0px; PADDING-LEFT: 200px; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px
}
DIV#menubar {
BACKGROUND: #ddd; LEFT: 0px; OVERFLOW: hidden; WIDTH: 200px; POSITION: absolute; TOP: 0px; HEIGHT: 100%
}
DIV#content {
PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 10px; PADDING-TOP: 10px
}

@media Screen
{
UNKNOWN {
POSITION: fixed
}

}

bullet.gif

This is the image for individual documents in the menu.

plus.gif

This is the image to show that the menu item can be opened to show more items.

minus.gif

This is the image to show that part of the menu tree can be closed.

Problems and To Do's

For the amount of time it's taken to get this menu to work at all there are still some problems with it.

1) Separate CSS files are needed to get around the problem that Microsoft's Internet Explorer has with position: fixed; This adds to the complexity of the project.

2) WebTV users have particular problems with the menu. This looks like because of WebTV's inability to render CSS, JavaScript and even some HTML properly.

How WebTV users see the menu and page contents

How WebTV users see the menu and page contents

3) When I decided against frames I introduced another problem. By using an iframe the frame contents are refreshed every time a visitor opens a new page. When this happens the visitor loses whatever view of the menu they had on the previous page. The Benefit from IT site gets around this by using a cookie. I should look into doing something similar. Another way of passing information from page to page is by using variables in the URL, I shall look into this as well.

This page created 23rd June 2005, last modified 3rd July 2005


GoStats stats counter