Introduction
There are a number of strategies and techniques for implementing a common navigational device like drop-down menus. Most of these solutions are standards compliant and work perfectly fine. But there are some issues with the two types of techniques I linked to above. The CSS-only implementation is not fully supported by IE versions prior to 7, and you have to have a potentially long series of nested lists in your markup. On the other hand, many JavaScript only implementations tend to have more features than you may need, resulting in bloated code that you're not taking advantage of. That's why I decided to create my own drop-down menu technique. My goal with this technique is to take the best of both worlds and create a simple solution that can be easily implemented, degrades gracefully, and works with most active browsers.
Before we get started, I want to lay down some ground rules. My own view on drop-down menus is that they should be an enhancement, not a necessity. If a user can't access underlying content without the drop-down menu, then you need to re-think your site architecture and navigation design. Secondly, drop-down menus should not be multi-tiered. Forcing your user to try to mouse carefully over to a second tier is putting too much work on the user. If you have to choose between easier navigation and easier use, always choose easier use. Now lets get started.
The Markup
Here is the prototype of the dropdown menus. The main markup couldn't be simpler. Just a basic unordered list with some id attributes set for each list-item.
<ul id="dyMenu"> <li id="mHome"><a href="#">Home</a></li> <li id="mDesign"><a href="#">Design Studio</a></li> <li id="mArt"><a href="#">Art Studio</a></li> <li id="mBytes"><a href="#">Snake Bytes</a></li> <li id="mArticles"><a href="#">Articles</a></li> </ul>
The CSS
The CSS is also relatively simple. First, some reset styles to override the browser defaults. Then some styling for the body, H1 tag and the ul itself. If you want to learn more about styling lists, see the article, "Taming Lists" at A List Apart. In this case I have styled the list to appear as a horizontal bar across the top of the page.
The Script
The script is where the real work happens. After we do a little compatability detection, we start with a function that creates and builds the sub-menus that will become nested unordered lists. This function starts with the creation of a two-tier associative array that contains the submenu items for each main list-item that requires a submenu:
// Check for browser compatiility
var isDom = (document.getElementById && document.createElement && document.getElementsByTagName);
//initiate the main createSubnav script
function initHeaderScripts() {
// first make sure the script can run on the client browser
if (!isDom) return;
// load the function below
createSubNav();
}
function createSubNav() {
// create the submenus based on the parent li id's
var menus =
[
{
itemId:'mDesign', // id name of the parent li
menuItems: // array of sub menu items
[
{text:'Web Design', href:'#'}, // text is displayed, href is the path
{text:'Flash Design', href:'#'},
{text:'Graphic Design', href:'#'}
]
},
{
itemId:'mArt',
menuItems:
[
{text:'Photography', href:'#'},
{text:'Ad Art', href:'#'},
{text:'Web Art', href:'#'}
]
}
];
Next, a little element creation of a UL, LI, and A to be used later:
// create elements that will be cloned later
var newUl = document.createElement('ul');
var newLi = document.createElement('li');
var newA = document.createElement('a');
Finally a for loop to go through all the items in the array, in order to clone and append each anchor, list-item and unordered list to the appropriate parent LI:
// cycle through the menus array to create the submenus
for(var i=0; i< menus.length; i++) {
// clone the ul for the first submenu
var u = newUl.cloneNode(false);
u.className="subMenu"; // append a className to the ul
var pl = document.getElementById(menus[i].itemId); // get the parent li by id
// cycle through the submenu array
for (var j=0; ji< menus[i].menuItems.length; j++) {
var l = newLi.cloneNode(false); // clone the li for the submenu li
var a = newA.cloneNode(false); // clone the a for the submenu link
// append the text to the a
a.appendChild(document.createTextNode(menus[i].menuItems[j].text));
a.href=menus[i].menuItems[j].href; // set the href for the a
l.appendChild(a); // append the a to the li
u.appendChild(l); // append the li to the u
}
u.lastChild.className="last"; // append a className to the last li of the nested ul
pl.appendChild(u); // append the nested ul to the parent li
}
}
The next step involves adding listener events so that something happens when the user mouses over the parent and child list-items:
function activateMenu(){
if (!document.all && !document.getElementById) return false;
var navRoot = document.getElementById('dyMenu');
var node = navRoot.getElementsByTagName('LI');
for (var i=0; i < node.length; i++){
node[i].oldClass = node[i].className;
node[i].onmouseover = function(){
addClass(this,"over");
}
node[i].onmouseout= function(){
this.className = this.oldClass;
}
}
}
Savvy, sharp-eyed readers will note that we are using an addClass function (courtesy of DOM Scripting by Jeremy Keith) to apply the class "over" to the list-items on mouse over. On mouse out, we re-apply the previous class name. I'm using class name switching because IE6 does not support the :hover pseudo-class on elements other than anchor tags. So rather than apply a work-around for IE6, I decided to ensure that the script works for all supported browsers (IE 5-7, FF, Safari and Opera).
Finally, we use an addLoadEvent function (also courtesy of DOM Scripting) to load the intiating function initHeaderScripts. That's all you need. Add a little CSS tweaking to get your drop-downs positioned and looking just the way you want it and you're done.
But Wait a Minute!!!
I know. I know. You tried the script and it works fine except when the drop-downs appear under Flash movies or form controls. What gives? Well, the short answer is that you are using a version of IE6 or earlier. Those browsers have decided to give media plugins and form controls an infinite z-index. So no matter how much you try to move your drop-downs above those elements, you can't. But there is a fix. Take advantage of the IFRAME shim technique (the setHover function is a modified version of Tanny O'Haley's iehover-fix script) to wrap your sub-menus in IFRAMES when the client is using IE6 or earlier. IFRAMES have an even higher z-index than form and media controls (who knew you could exceed infinite?), so they overlay them.
Final Notes
So that's my drop-down menu technique. If you are industrious, I'm sure you could modify it to add second and ever third levels of menus, or perhaps add some visual flare and effects as seen on TwinHelix.com.
Follow any responses to this entry through the RSS 2.0 feed.
Both comments and trackbacks are currently closed.






