Developer's manual

Creating menus

Now you have the basic navbar, we can look at adding some menus.

Two methods

There are two ways to create menus:

The most obvious reason why you might want dynamically-generated menus is if you don't use any kind of server-side or template-based includes - having all the menu data in one file reduces the amount of HTML you'd have to copy-and-paste between pages - only the top list, instead of the whole structure.

Beyond that, there are arguments on both sides:

dynamically-generated menus

There's a usability case for saying that only the main navbar should be available to browsers which don't support styling or have dynamic behaviors; for example, screenreaders, text-only browsers or PDAs. A large tree of links can be some effort to tab through, and is not so at-a-glance easy to comprehend.

With a very complex list-structure it might be quite intimidating, particularly if it's at the very top in a small-screen device.

hard-coded menus

On the other hand - you can mitigate the usability issue by adding headings inside the navbar list, and by putting the list HTML at the end of the source code. Or perhaps you're building an intranet where your user-base is predictable.

Hard-coded menus are also easier to maintain (providing you have some kind of includes or template system), are included in server-processes such as transient session handling, and are useful from a marketing perspective, because the menus as well as the navbar will be accessible to search-engine robots and generate click-through information in statistical packages.

You can also have a combination of hard-coded and dynamically-generated menus. Combinations are no problem, as we'll see:

Hard-coded menus

The simplest configuration, hard-coded menus are simply nested lists inside the navbar list-items. Please keep an eye on well-formedness - nested lists go before a closing </li> not after it:

<li><a href="/menu/">About us</a>
		<li><a href="/products/">Products</a></li>
		<li><a href="/services/">Services</a></li>

You can put further nested lists inside menu items, and so on ad infinitum.

There's no limit to how many nested levels you can have, and no need to be concerned about their visibility either, providing you use menu repositioning; however there is a certain amount of drag inherent in enormous structures - each link is processed onload to bind event handlers to it, so if you have dozens and dozens of menus, the script will begin to slow down.

It's also a good idea to avoid building child menus where every single item has a further child menu, to prevent a potentially confusing situation for people who are navigating with the keyboard. If it's viewed in a small window and those menus are dynamically repositioned, a menu which opened on the right will now open on the left, and so the left/right keys are mapped accordingly. If they're all repositioned to the left, in a menu which itself opened on the right, how do you get back to the parent? You can of course - the keys are mapped, so pressing the right-key moves left - but that's confusing, and better to avoid if possible.

If we ensure that each menu has at least one link which doesn't itself have a child menu, a user can always navigate down to that, from which the left-key predictably moves back to it's parent.

Dynamically-generated menus

Dynamically-generated menus won't work within XHTML served as XML. However there are two extensions available which offer compatible functionality: The Load XML extension allows you to use an external XML document as your menu data source, while the Import HTML extension allows you to use an embedded HTML document.

Dynamically-generated menus are the same HTML, but instead of being hard-coded to the page, they're kept inside arrays in your udm-custom file, and written into the navbar at load-time using innerHTML.

They're supported with both client-side and server-side configurations, and in all cases result in menus which are generated with javascript - dynamic menus in server-side configs are not generated as pure HTML - to do so would defeat the point.

For each menu you want to create, code a <ul> and then turn it into a string, which you add to a name-indexed array called um.menuCode (or $um['menuCode'] in PHP).

Here's an example:

um.menuCode['about'] = ''
+ '<ul>'
  + '<li><a href="/products/">Products</a></li>'
  + '<li><a href="/services/">Services</a></li>'
+ '</ul>';
$um['menuCode']['about'] = ''
. '<ul>'
  . '<li><a href="/products/">Products</a></li>'
  . '<li><a href="/services/">Services</a></li>'
. '</ul>';	

I've split the string across multiple lines because I find it easier to work with like that. You could equally do it like this:

um.menuCode['about'] = '<ul><li><a href="/products/">Products</a></li><li><a href="/services/">Services</a></li></ul>';
$um['menuCode']['about'] = '<ul><li><a href="/products/">Products</a></li><li><a href="/services/">Services</a></li></ul>';

or like this:

um.menuCode['about'] = '<ul>';
um.menuCode['about'] += '<li><a href="/products/">Products</a></li>';
um.menuCode['about'] += '<li><a href="/services/">Services</a></li>';
um.menuCode['about'] += '</ul>';
$um['menuCode']['about'] = '<ul>';
$um['menuCode']['about'] .= '<li><a href="/products/">Products</a></li>';
$um['menuCode']['about'] .= '<li><a href="/services/">Services</a></li>';
$um['menuCode']['about'] .= '</ul>';

whatever you prefer. The point is that each menu should be a single string, stored as a named item in the menuCode array.

The actual name is up to you - it relates to the id of the list-item you want the menu attached to. So to attach this menu to the "About" link in our main navbar, we simply give that list-item the same id:

<li id="about"><a href="/menu/">About</a>

Dynamically-generated menus will attach themselves to whichever list-item has the correct id, and obviously each must be unique, because you can't have multiple elements with the same id. The menu "hook" doesn't have to be a main navbar item though - you can equally attach a dynamically-generated menu to a list-item within a hard-coded submenu, simply by giving that list-item the applicable id.

A menu string can also contain more than one menu - it can contain multiple nested levels within a single string, and providing the code is well-formed, the entire branch will be attached and work as expected. Have a look at the dynamically created menus demo for examples of this in action.

Escape characters in dynamically-generated menus

If you're using the client-side javascript configuration, single-quote characters in link-text must be escaped:

um.menuCode['about'] += '<li><a href="#jim">Jim "one-eye" O\'Reilly</a>';

If you're using one of the server-side configurations, double-quote characters in link-text must be double-escaped, and single-quote characters must be triple-escaped:

$um['menuCode']['about'] .= '<li><a href="#jim">Jim \\"one-eye\\" O\\\'Reilly</a>';

This is essential to avoid syntax errors in the final javascript, as the data passes through various levels of parsing.

Access considerations

Whatever method you use for creating menus, some browsers will never be able to see them; this includes partially-supported browsers such as Opera 5 and 6, and any fully-supported browser with CSS on but javascript disabled. So you need to provide duplicate links for submenu content - anything which is accessible from the menus must also be accessible without them, as documented in The need for duplicate menu links.

For browser-based screenreaders, whether or not they can access the menus is entirely unrelated to which menu creation method you use, because browser-based readers can access script-generated content just the same; providing, of course, that javascript is enabled, and that the content is displayed, visible, and needs no user-action to make it so. But that is the case with this script - the menus are initially hidden using offleft-positioning, rather than display or visibility.

But not all screenreaders are browser based - pwWebspeak is an example of one that isn't; it doesn't support scripting or CSS at all, and is therefore equivalent to a text-browser - it can only see hard-coded menus.

Further technical considerations

Dynamically-generated menus won't work within XHTML served as XML, because they're appended using innerHTML. However there are two extensions available which offer compatible functionality:


We would like your feedback! Take the UDM4 Survey!

UDM 4 is valid XHTML, and in our judgement, meets the criteria for WAI Triple-A conformance.