What’s on the Menu?
It’s an understandable desire — to have a easy way for visitors to your website to navigate its many directories and pages. But simple HTML coding won’t give you a usable navigation menu except for the most shallow of sites. Imagine Amazon.com with just a long column of links replacing the top-left navigation menus.
No, if you’ve got even a modestly complex site, you’re going to need a drop-down menu system. Luckily, there are lots of examples on the web showing how to create drop-down menus, but most of them share a common fault, and that fault is obvious if you just look at the elegant examples of drop-down menus that you can see on your computer everyday.
Warning: I’m about to rant
Forgive me a second, I’m about to rant to the many website designers who have implemented these common menu solutions. Have you never looked at your system menus, the ones for your operating system and applications? Have you never noticed that these menus drop down on click, not on hover? What the hell am I talking about? you might ask.
It’s simple. Application menus on the Mac and Windows are invoked when the user clicks the menu heading. Internet Explorer 6 (and 7 if you have the menubar visible) will drop down the Tools menu when the user clicks Tools, not when the mouse just happens to stray over the heading. And, the menu will stay dropped down until the user clicks another item in the menu, clicks off the menu or hits ESC. As far as I can remember, Windows has worked this way since the beginning. Macs have worked this way since about System 6
.
I think if this method is good enough for your operating system, it’s good enough for your website. I really hate when I’m navigating a drop-down menu, my mouse momentarily strays off the menu because I haven’t had enough coffee (or too much) and the menu disappears, usually when I’m on the second sublevel.
Evil roots
Why did this almost criminal behavior arise? Simple: a:hover. Some very clever (and I’m not being sarcastic) developer realized that by combining unordered lists and the a:hover pseudo class that you can create a quick and dirty menu that needs no JavaScript programming to make it go. The only drawback (and it’s debatable whether this was ever questioned): the menu drops down on hover.
It’s an admirable goal to have a menu that doesn’t require JavaScript, but you don’t want a menu that disappears just because the user can’t stay within the lines. So for this current incarnation of the Virtualight website, I created a menu that uses the basic unordered list structure, drops down on click and gets its content from an XML file because I wanted to be able to easily and universally change the menus.
The XML file
OK, let’s work backwards and look at the XML file (yes, I know I don’t have a Doctype, but I don’t think it matters, but I could be wrong and often am). The root element is <menu> and under that I have <folder> and <document>. Folders, of course, can contain other elements, including other folders and documents. Folders have an id tag that correspond to the directories of this website and tells the JavaScript code which menu item (that is, the nested <ul> structure) to display when clicked. Documents, of course, have a url tag that sends the user to that document when clicked.
There’s very little tricky to the XML file, other than I believe the lack of doctype means I can't use HTML entities like ’ but instead have to code in the decimal equivalent. (Since I’m a little vague on creating a Doctype and DTDs — in other words, I’ve never done one — I put up with having to type ’ to get a real apostrophe.)
The JavaScript
The JavaScript file has two jobs: format the XML into a list and display and hide menu items (and sub-level menu items) according to user interaction. It’s pretty simple code and relies heavily on recursion.
The heavy lifting is done by the iterate() function. An XML document is really just a family tree, with siblings, children of the siblings and their children, and so on and so on. So, the iterate function is sent the variable menu, which on the first go round contains all the children of the root element (I know, it’s also called menu) of the XML file. If those children have children, they’ll be sent back to the iterate function at line 162. Eventually, all the children, grandchildren and great grandchildren of the original siblings will have been sent to iterate and evaluated.
Now, if a node that’s been sent to the iterate function has a tagName corresponding to “folder,” well, then it’s a folder and treated as such, and if the tagName equals “document,” then it’s a document
. Remember that all this has to be turned into an unordered list, so all those folders and documents will look something like:
<li onmouseup="dropDown('topNav','articles',event,this)">
if it’s a folder and something like:
<li onclick="location.href='/articles/redesign.html'" style="width: 180px; ">
if it’s a document. Of course, if it’s a folder, there will be another whole <ul><li></li></ul> structure inside.
What the heck is setULWidths()?
I’m glad I pretended you asked that. When I was developing this menu, I got everything working quite nicely with Safari 3.0 on the Macintosh, which is the worst browser for me to test my code because everything always works the first time. I’m sure Windows developers have the same experience with Internet Explorer, but I write all my HTML and code on the Mac and then test on Windows.
Anyway, the width of submenus was never an issue for Safari or FireFox, but Internet Explorer needs to know how wide each menu item is
or else those nice drop shadows will never line up. So I created a menu item width tester and figured out the width of the longest item in each submenu and added that style definition to each <LI> (see the preceding HTML snippet).
The width tester is basically just a visibility:hidden anchor tag
on my page, set to position: absolute so it won’t affect the rest of the layout. The innerHTML of the anchor tag is set to the text of the menu item and the offsetWidth of the widest menu item in that submenu is used to set the width of all the items. I also use this trick to set the height of a menu item.
Drop down on click
OK, back to my original rant. Submenus are exposed only when the user clicks on a menu item. Because each folder <LI> has an onmouseup handler that calls the function dropDown(). It sends the function these arguments: 'topNav' to indicate the user has actually clicked a menu item (rather than just clicked on the page itself to dismiss the menu), the id of the submenu <ul> structure to display, the window event and the keyword this, which I never actually used, but I’m too lazy to get rid of it. (I like to think of it as the appendix, a part of the code that I thought I might use but ultimately wasn’t needed.)
You have no idea how many different versions there were of dropDown(). The function had to do so many different things: drop down a menu when clicked, but hide the menu if it’s already been dropped down. Or hide menu item A after you’ve clicked on menu item B. Unless, of course, A is the parent of B, and since the display/visibility of an item is inherited … well, let’s say it’s like going back in time and killing your own parents.
What I forgot is the simplest way to handle any kind of menu structure: always hide everything, then just show what you need to show. That’s why the first thing the dropDown function does is closeMenus().
Now, as far as the menu is concerned, there are two kinds of mouse clicks: those on the menu, or those not on the menu. If a user clicks somewhere other than the menu, dropDown is sent the argument 'document.' That’s because I set up a document.onmouseup inline function that calls dropDown.
Of course, the menu is also part of the document, so if the user clicks on a menu item that represents a submenu, it gets sent to the dropDown function twice: once for the click on the folder item and again because of the document.onmouseup. So, the dropDown function has to stop the propagation of the event to prevent the function from being called twice. (You don’t need to worry about menu items that represent documents, because the second event never gets called; loading the page you clicked wiped out that second event.)
The pretty stuff
Obviously I love the Macintosh OS X look and feel. My menus look very much like Apple’s menus, using PNG alpha channels to get the drop shadow effect. Unfortunately, you can’t just create a graphic with a drop shadow and use it as the background graphic for your <ul> because the proportions will distort. So you’re forced to do the old rounded box trick of nesting divs in your <ul>
.
The menus themselves are transparent, using a PNG background graphic that’s 97% opaque. I’m including my css file so you can see how the a:hover effect is created.
Shortcomings
I admit my menus aren’t perfect and I made choices that some people will find annoying. For instance, even the Apple menus will display submenus on hover, however, if you drag your mouse off the menus, they will remain dropped down. I should probably add the ability to dismiss menus by hitting the ESC key, but I’ve had some problems getting that to work. And the ledge the menus sit on need some work, but hey, that’s version 2.0.

