Doing a dhtml menu for your browser applications.
This tutorial will show you how to do a stylish menu bar like those in typical desktop applications. This will be done with surprisingly few Javascript, some <div> and <span> tags and cascading stylesheets (CSS). I've been in the need of doing my own simple men¸ system for my home-grown CMS and will describe its menu technique here in detail. The menu system I'm featuring here offers a dropdown menu, image buttons, separators, mouse-over effects and simple event-handling. The only drawback is that there are no sub-menus. I'll add them any time.
To show you what we are going to accomplish, fig. 1 shows the menu. Notice the opened "Artikel"-menu. If we move the mouse cursor over the menu entries they will become inverted. To obtain this effect, we won't need any scripting. It's hidden in the stylesheets.
(Note: Some of the icons and stylesheets were stolen from Microsoft and tinyMCE. Thank you! You may use different icons / styles / effects duitable for your needs.)

(fig. 1: The complete menu bar.)
Part 1: menu structure
Think about the structure of your menu. What commands can be grouped together? Do you want to illustrate some commands using icons? Do you want to provide some shortcuts / icons to make commands accessible via one click? If you consider the "New" command in the "Article" menu you'll notice the shortcut on the right side for immediate access. Both menu entry and menu button will target the same command.
So what's the structure of our menu bar? There are two main sections: the drop-down menus and the various buttons and other form elements (notice the dropdown control for selecting different views). Let's start with this basic construction:
<div id="TOEMenu" class="TOEMenu" align="left">
<!-- article menu -->
<span id="ARTICLE" onclick="showMenu('divMenuArticle');" target="_self">
<span id="MenuArtikelLabel" class="TOEMenuTextNormal">Artikel</span>
:
:
:
</span>
<!-- view menu -->
<span id="ANSICHT" onClick="showMenu('divMenuView');" target="_self">
<span id="MenuArtikelLabel" class="TOEMenuTextNormal">Ansicht</span>
:
:
:
</span>
<!-- extras menu -->
<span id="EXTRAS" onClick="showMenu('divMenuExtra');" target="_self">
<span class="TOEMenuBlock" id="divMenuExtra">
:
:
:
</span>
<!-- help menu -->
<span id="HILFE" onClick="showMenu('divMenuHelp');" target="_self">
<span id="MenuArtikelLabel" class="TOEMenuTextNormal">?</span>
:
:
:
</span>
<!-- help menu -->
<span id="HILFE" onClick="showMenu('divMenuHelp');" target="_self">
<span id="MenuArtikelLabel" class="TOEMenuTextNormal">?</span>
:
:
:
</span>
<img width="2" height="20" class="TOESeparatorLineV" src="images/separator.gif">
<span id="LabelAnsicht" class="TOELabel">Ansicht</span>
<select id="Generation" name="l_View" onmousedown="" onchange="alert(this.options[this.selectedIndex].value);" class="SelectList">
<option>Öffentliche</option>
:
</select>
<a id="ARTICLE_NEW" href="" onclick="alert('new doc');return false;" onmousedown="return false;" class="TOEButtonNormal" target="_self"><img src="toe/images/newdoc.gif" title="Neuer Artikel"></a>
:
:
:
<img width="2" height="23" class="TOESeparatorLineV" src="toe/inc/tinymce/jscripts/tiny_mce/themes/advanced/images/separator.gif">
<a ID="MYONES" TITLE="Meine Artikel" href="" onclick="return MENU_ANSICHT_MYONES_onclick();" onmousedown="return false;" class="TOEButtonNormal" target="_self"><img src="toe/images/userprops.gif"></a>
:
:
:
</div>
<div id="Content"><br><br><br><br><br><br>Content</div>
(codeblock 1: basic menu structure.)
We'll discuss the empty spaces later. As you might expect, the whole structure is placed within a <div>:
<div id="TOEMenu" class="TOEMenu" align="left">
This is to separate it from the "content" of the page that is placed into another <div>:
<div id="Content">
Both <divs> have their unique IDs. Unlike names that you are used to work with HTML forms, ids are unique identifiers for arbitrary elements in your page. What we have done so far is declaring two separate blocks and giving them unique names. Unique because we want both of them treat as objects that will respond to various events (i.e. mouse clicks) and we somehow have to address them and tell them what to do and which event to handle.
Our dropdown menu consists of further information blocks, each one per menu. There are four menus overall: "Article", "View", "Extras", and "Help". Within the TOEMenu <div> block, each one is defined this way:
<span id="ARTICLE" onclick="showMenu('divMenuArticle');">
<span id="MenuArtikelLabel" class="TOEMenuTextNormal">Artikel</span>
<span class="TOEMenuBlock" id="divMenuArticle">
:
:
:
</span>
</span>
(codeblock 2: dropdown menu structure.)
Codeblock 2 shows the structure of the "Article" menu as an example. We're using <span>s because <div>s may result in a carriage return in the browser, <span>s won't behave this way. Again, each <span> gets its own unique ID. The outer <span> means the menu "Article" in its entirety; it consists of a <span> containing the menu label and a <span> for the actual menu content. "MenuArtikelLabel" is the first <span>, "divMenuArticle" the latter. Note that each span is attributed with a css class. We'll discuss this in part 2 of this tutorial.
The "Article" <span> has an onClick event handler. This is no surprise because we want the menu to handle a mouseclick in order to open the menu. This will be done by calling a JavaScript function "showMenu" with the id of the menu block "divMenuArticle" inmidst the ARTICLE-<span>. The <span> with id "MenuArtikelLabel" is just for displaying the menu label that contains the word "Artikel" in this case. Since event handling for mouse clicks is dealt with in the "ARTICLE" <span> there is no need to install an event handler for the label <span>. The "divMenuArticle" <span> holds the information for each menu entry. Please note that with the exception of the "ARTICLE" <span> each <span> has a different style sheet attribute.
Let's take a look at the menu block:
<span class="TOEMenuBlock" id="divMenuArticle">
<div class="TOEMenuItem" ID="ARTICLE_MENUNEW" onclick="return MENU_ARTICLE_NEW_onclick()">
<img class="TOEIcon" src="toe/images/newdoc.gif">
<span class="TOEMenuTextNormal">Neu...</span>
</div>
<div class="TOEMenuItem" ID="ARTICLE_ONLINE" onclick="return MENU_ARTICLE_ONLINE_onclick()">
<img class="TOEIcon" src="toe/images/publish.gif">
<span class="TOEMenuTextNormal">Artikel publizieren</span>
</div>
<div class="TOESeparatorLineH"></div>
<div class="TOEMenuItem" ID="ARTICLE_MENULOGOUT" onclick="return MENU_ARTICLE_LOGOUT_onclick()">
<img class="TOEIcon" src="toe/images/logout.gif">
<span class="TOEMenuTextNormal">Logout</span>
</div>
</span>(codeblock 3: menu commands.)
This is the complete <span> we touched in the previous paragraph. Since we want our menu lines one beneath the other we're using <div> blocks (instead of <span>). Each of them has a css class attribute "TOEMenuItem", an onClick mouse event handler, and, of course, an id. Within each <div> is an image tag for displaying an appropriate icon. Note that even if we don't want to display an icon we'll provide the space for it (by using a transparent image) in order to make the menu entries left-aligned. Please note that all images have to be of the same size. The <span> for the menu entry text is just for providing a css class information, so we don't need an id nor an event handler here.
If we would put all these fragments together we would have enough code to build up a simple drop down menu. But since we want kind of an icon bar there's still some work to do. First, we'll add a separator behind the last <span> of the menu construction:
<img width="2" height="20" class="TOESeparatorLineV" src="images/separator.gif">
This is just an image to separate menus, buttons etc. from each other. No ids and no event handlers here. The next code adds a label and a dropdown control to our menu bar:
<span id="LabelAnsicht" class="TOELabel">Ansicht</span>
<select id="Generation" name="l_View" onmousedown="" onchange="alert(this.options[this.selectedIndex].value);" class="SelectList">
<option>Öffentliche</option>
:
</select>
Since the label is just an additional information that has nothing real to do we just connect it to a css class. The id isn't really important here. Also the dropdown control (the <select> block) is without surprises: depending on your needs you may put in an onChange event handler to be able to react on user interaction and specify a css class.
Icons for shortcuts are implemented by:
<a id="ARTICLE_NEW" href="#" onclick="alert('new doc');return false;" class="TOEButtonNormal" target="_self"><img src="images/newdoc.gif" title="New Article"></a>
Basically, this is an image ("newdoc.gif") within a link. Besides the omnipresent id, that link has an onClick mouse event handler, an (almost) empty href attribute and a target "_self". The href attribute just contains a "#". This is to prevent the browser from reloading the whole page. By putting the action into the onClick handler it is guaranteed that just the event is handled without reloading the page and thus resulting in confusion about your application's state. It must be a "#"; if href would equal "" then there would be a page reload.
Part 2: style information
So far we have provided a basic menu structure that is as simple as possible using minimal HTML code. Now it comes to a handy design. There is common sense that a "menu systems" somehow resembles a MS Windows desktop application's menu. That means that you have to click on a menu's label to open it and that the menu entries are inverted when the mouse cursor hovers above it. A single click on a menu entry (or an image button) evokes further actions. A menu also may contain some icons to help the user memorize the divers actions. Of course you may change all settings due to your needs and preferences.
Let's get started with the "TOEMenu" class. As you might remember from the previous chapter the outermost <div> area embraces all further menu structures. So it needs some basic design settings like border, width and the like:
.TOEMenu {
POSITION: ABSOLUTE;
CURSOR: default;
margin-left:5px;
BACKGROUND-COLOR: buttonface;
BORDER-BOTTOM: buttonface solid 1px;
BORDER-LEFT: buttonface solid 1px;
BORDER-RIGHT: buttonface solid 1px;
BORDER-TOP: buttonface solid 1px;
PADDING-TOP: 4;
PADDING-BOTTOM: 2;
TOP: 5px;
WIDTH: 800px;
FONT-FAMILY: Tahoma, MS Sans Serif;
FONT-SIZE: 11px;
}
(codeblock 4: TOEMenu class.)
We want an absolute window position for the menu, hence the 'position' attribute. The width of 800 pixels is okay for our menu. For mouse movements we want a standard (arrow-shaped) mouse cursor: no need to convince the user that she might click everywhere. The various padding settings mix up the whole structure a little bit.
.TOEMenuTextNormal {
margin-left: 5px;
margin-right: 5px;
font-family: arial, helvetica, monaco;
font-size: 10pt;
color: #000000;
text-decoration:none;
line-height:18pt;
vertical-align:top;
}
(codeblock 5: menu label class.)
The "TOEMenuTextNormal" class is used for menu labels and just contains some margin and font attributes. If you wonder about "monaco": this is a system font on the Mac.
A click on a menu label will open the appropriate menu section. This section, containing the lines of each menu has a css class "TOEMenuBlock":
.TOEMenuBlock {
position:absolute;
visibility:hidden;
BACKGROUND-COLOR: buttonface;
BORDER-BOTTOM: buttonface solid 5px;
BORDER-LEFT: buttonface solid 5px;
BORDER-RIGHT: buttonface solid 5px;
BORDER-TOP: buttonface solid 5px;
}
(codeblock 6: TOEMenuBlock class.)
Among some color and border attributes the visibility attribute hides the menu block by default. This is easy understandable because we want the menu just to be visible when the appropriate label is clicked. This visibility switch will be done in the JavaScript code we'll discuss in the next chapter.
Each menu block contains several menu items. These are defined in the TOEMenuItem class; there's nothing spectacular, just settings comparable to menu labels.
.TOEMenuItem {
CURSOR: default;
FONT-FAMILY: Tahoma, MS Sans Serif, Monaco;
FONT-SIZE: 11px;
PADDING-TOP: 4;
PADDING-BOTTOM: 4;
}
(codeblock 7: TOEMenuItem class.)
The CSS class "TOEMenuTextNormal" is used for any menu label, so we can re-use it for all menu entries. All images in a menu have a class "TOEIcon", that defines their position and size:
.TOEIcon {
LEFT: -1;
TOP: -1
float:left;
width:25px;
}
(codeblock 8: TOEIcon class.)
Are we ready now? Not yet. We'll need to cover the hover effects. Fortunately, this can be accomplished solely by using CSS.
.TOEMenuBlock div:hover {
background-color: #CCCCCC;
}
(codeblock 9: hover effect for TOEMenuBlock)
This just tells the browser to emphasize the menu line the mouse is hovering above. The last menu element is the horizontal separator, a <div> area defined by: .TOESeparatorLineH {
BORDER-TOP: #BBBBBB solid 1px;
FONT-SIZE: 2px;
HEIGHT: 1px;
WIDTH: 100%;
}
(codeblock 10: TOESeparatorLineH class.)
IE doesn't like the 100% that much.
That's it for the dropdown menu! Now we're heading towards the image buttons and vertical separators.
.TOESeparatorLineV {
border: 0;
padding: 0;
margin-left: 4px;
margin-right: 2px;
width: 2px;
height: 23px;
}
(codeblock 11: TOESeparatorLineV class.)
In contrast to the horizontal separator the vertical separator is basically a 1 pixel image whose settings are defined in the CSS. After all, it's longer than broader.
The image buttons are more interesting. Let's recall their composition discussed in the last chapter:
<a id="ARTICLE_NEW" href="#" onclick="alert('new doc');return false;" class="TOEButtonNormal" target="_self"><img src="images/newdoc.gif" title="New Article"></a>
The image doesn't have a CSS class at all. What we want is to attach CSS classes to nested structures like the above one. If there's a link and an image within it then it's called a TOEButton. A button may have various states: normal, selected, and disabled. We're using the "cascading" in CSS to accomplish this.
All three types of buttons (we noticed them already) have some basic properties in common:
a.TOEButtonDisabled img, a.TOEButtonNormal img, a.TOEButtonSelected img {
width: 23px;
height: 22px;
cursor: default;
margin-top: 1px;
margin-left: 1px;
}
(codeblock 12: basic properties of the three button instances.)
Additionally, a "disabled" button gets a light border. Since it is tagged as "important", this setting will overwrite possible defaults:
a.TOEButtonDisabled img {
border: 0 !important;
}
(codeblock 13: disabled button.)
Normal and selected buttons also get their borders. If a menu bar button will ever change its state (e.g. from "normal" to "disabled" then the border settings will be overwritten, respectively.
a.TOEButtonNormal img, a.TOEButtonSelected img {
border: 1px solid #F0F0EE !important;
}
a.TOEButtonSelected img {
border: 1px solid #C0C0BB !important;
}
(codeblock 14: button borders.)
We use background colors to point up the button's meaning. For establishing a mouseover effect, the "hover" event handling adds this nice box effect. This realizes the "flat" menu button effect as seen on MS Windows systems. If a button gets "disabled" or inactive, it will get a gray tone (this is done by setting the opacity attribute).
a.TOEButtonNormal img:hover, a.TOEButtonSelected img:hover {
border: 1px solid #0A246A !important;
cursor: default;
background-color: #B6BDD2;
}
a.TOEButtonDisabled img {
-moz-opacity:0.3;
opacity: 0.3;
border: 1px solid #F0F0EE !important;
cursor: default;
}
(codeblock 15: hover and disabled button effects.)
Since Mozilla/Firefox and IE handle parts of CSS in different ways, there is still need to do some fine-tuning for the different browser types. Below you'll find special IE rule that are using the "Star HTML hack", hence the * html selectors. No other browser than IE will accept these CSS rules.
There's one drawback for IE: hover effects are (up to now) only available for links (<a> tags). I'll provide a solution as soon as possible.
/* MSIE specific rules */
* html a.TOEButtonNormal img, * html a.TOEButtonSelected img, * html a.TOEButtonDisabled img {
border: 0px !important;
margin-top: 2px;
margin-bottom: 1px;
}
* html a.TOEButtonDisabled img {
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30);
border: 0px !important;
}
* html a.TOEButtonDisabled {
border: 1px solid #F0F0EE !important;
}
* html a.TOEButtonNormal, * html a.TOEButtonSelected {
border: 1px solid #F0F0EE;
cursor: default;
}
* html a.TOEButtonSelected {
border: 1px solid #C0C0BB;
}
* html a.TOEButtonNormal:hover, * html a.TOEButtonSelected:hover {
border: 1px solid #0A246A;
cursor: default;
background-color: #B6BDD2;
}
(codeblock 16: various button classes.)
Part 3: script code
Fortunately, there's not that much JavaScript to make all these tags and stylesheets run. We need a function to open a menu, one to close a menu and some helper functions. To make the buttons work, we don't need script code at all.
function showMenu(divName) {
closeAllMenus();
var obj = document.getElementById(divName);
var posL = 0;
var posT = 0;
if (obj.style.visibility != 'visible') {
if (document.all) { // IE
posL = absLeft(obj) -15;
posT = absTop(obj);
} else { // else
posL = parseInt(document.defaultView.getComputedStyle(obj,null).getPropertyValue("left"));
posT = parseInt(document.defaultView.getComputedStyle(obj,null).getPropertyValue("top"));
}
obj.style.left = posL - 50; // best guess
obj.style.top = posT 30;
obj.style.visibility = 'visible';
}
}
(codeblock 13: JavaScript code for opening a menu.)
The function showMenu will be called when we try to open a menu by clicking at the appropriate label. showMenu gets a parameter divName, that will be translated into an object by using the getElementById function. As you may recall, the call to this function is defined by the onClick event handler of every menu span:
<span id="ARTICLE" onclick="showMenu('divMenuArticle');" target="_self">
If the object (in the example above, "divMenuArticle") is hidden, we have to open it. Depending on the used browser, we compute divMenuArticle's left and top position, correct these positions due to our needs and make divMenuArticle visible. The hard-coded numbers for positioning are not the best technique, but for our purposes, it will suffice. The function call to closeAllMenus() will close all menus (not really surprising!), because before opening a new we'll close any menu that might be open. We'll look at this in a few moments.
function closeMenu(divName) {
var obj = document.getElementById(divName);
if (obj.style.visibility == 'visible') {
obj.style.left = '';
obj.style.top = '';
obj.style.visibility = 'hidden';
}
}
(codeblock 14: JavaScript code for closing a menu.)
Similar to showMenu, closeMenu gets a parameter that will be used for retrieving the appropriate object and this object's visibility will be set to "hidden". It seems to be a good practice to clear all position settings (left and top) because they may influence the computing of object position in showMenu.
function closeAllMenus() {
closeMenu('divMenuArticle');
closeMenu('divMenuView');
closeMenu('divMenuExtra');
closeMenu('divMenuHelp');
}
(codeblock 15: JavaScript code for closing all menus.)
This is a no-brainer: we're closing all menus by subsequently calling closeMenu on every menu object we know. Note that the don't call closeMenu or closeAllMenus from any <span> or <dÌv> directly: this only happens for showMenu. showMenu is the only function responsible for opening and closing menus.
There are some things that may be worth a look: the position calculation is handled in different ways for IE and Mozilla/Firefox. According to W3C syntax, width, height, top and left poition of an element are calculated using this way (presumed there's an object "myDiv"):
docObj = document.getElementById("myDiv");
objHeight = document.defaultView.getComputedStyle(docObj, "").getPropertyValue("height");
objWidth = document.defaultView.getComputedStyle(docObj, "").getPropertyValue("width");
objTop = document.defaultView.getComputedStyle(docObj, "").getPropertyValue("top");
objLeft = document.defaultView.getComputedStyle(docObj, "").getPropertyValue("left");
Note that this "absolute" position of an element in Mozilla/Firefox/W3C stanndard means the relative position to the document. IE knows element properties like offsetLeft and offsetTop that delievers coordinates relative to the offSetParent. Therefore the different calculations you could observe in showMenu(). Here's the appropriate IE way to do the computation: (http://www.faqts.com/knowledge_base/view.phtml/aid/7157 and http://www.dcljs.de/faq/antwort.php?Antwort=dhtml_koordinaten)
function absLeft(el) {
return (el.offsetParent) ? el.offsetLeft absLeft(el.offsetParent) : el.offsetLeft;
}
function absTop(el) {
return (el.offsetParent)? el.offsetTop absTop(el.offsetParent) : el.offsetTop;
}
(codeblock 16: left and right position (IE).)
Part 4: put it together
We're almost done! We defined a content area <div> below the menu. It would be a fine idea that if a user clicks somewhere at this content area all menus would be closed (so long, this only happens if the user clicks at another menu). By adding this script block
<script language="javascript">
if (document.all)
document.getElementById('Content').onclick = closeAllMenus;
else
document.getElementById('Content').addEventListener("click", closeAllMenus, false);
</script>
(codeblock 17: event handlers for clicking on content.)
at the end of the html code this will be done. That's it! Here's the complete code to play with.