DHTML: Simple Menu

I needed a DHTML menu for a project I was working on, and was able to find about a jillion of them on the web. Unfortunately, they were all a bit more than I wanted to bother with. I didn't need it to jump off the page, I didn't need it to emulate a windows tool-bar, and I didn't need it to load a 200k library of support functions. I just wanted it to expand and close to display a large set of menu options in a non-cluttered manner.

I was forced to write it myself.

Consider this example menu:

It consists of three components: Content, Style, and Logic. Let's look at the "Content", first:

<table><tr><td>
<ul>
<li class="companyc" id="1"><a class="companyl" onclick="switchMenu('2','1');">Company 1</a></li>
<ul class="cblockc" id="2">
        <li class="projectc" id="3"><a class="projectlc" onclick="switchMenu('4','3');">Project 1a</a></li>
        <ul class="pblockc" id="4">
                <li class="object" id="5"><a class="objectl">Object 1a1</a></li>
                <li class="object" id="6"><a class="objectl">Object 1a2</a></li>
        </ul>
        <li class="projectc" id="7"><a class="projectl" onclick="switchMenu('22','7');">Project 1b</a></li>
        <ul class="pblockc" id="22">
                <li class="object" id="8"><a class="objectl">Object 1ab</a></li>
        </ul>
</ul>
<li class="companyc" id="9"><a class="companyl" onclick="switchMenu('10','9');">Company 2</a></li>
<ul class="cblockc" id="10">
        <li class="object" id="11"><a class="objectl">Object 2a</a></li>
        <li class="object" id="12"><a class="objectl">Object 2b</a></li>
        <ul id="13">
                <li class="projectc" id="14"><a class="projectl" onclick="switchMenu('15','14');">Project 2a</a></li>
            <ul class="pblockc" id="15">
                        <li class="object" id="16"><a class="objectl">Object 2a1</a></li>
                        <li class="object" id="17"><a class="objectl">Object 2a2</a></li>
            </ul>
        </ul>
        <ul id="18">
                <li class="projectc" id="19"><a class="projectl" onclick="switchMenu('20','19');">Project 2b</a></li>
                <ul class="pblockc" id="20">
                        <li class="object" id="21"><a class="objectl">Object 2ab</a></li>
                </ul>
        </ul>
</ul>
</ul>
</td></tr></table>

What we're doing here is building a set of nested lists with three levels. At the top, we have "Company", which can have "Projects" or "Objects" beneath it. The second level, "Projects", can have "Objects" of their own. "Objects are destinations, not groupings, and as such, have nothing below them. The whole thing is wrapped up in a table for positioning.

You'll see that ever list item has a class and an ID, which will be key in tying back to the style controls. Collapsible blocks also have classes and IDs, to control display, while blocks that only house items that are controlled by their containers have no classes.

Finally, you'll note that any controlling containers are operated with an onClick link to a function called "switchMenu".

Let's look at switchMenu - the only JavaScript used:

<script>
        function switchMenu(objx, objy) {
          var elx = document.getElementById(objx);
          var ely = document.getElementById(objy);
          var cnamex = elx.className;
          var cnamey = ely.className;
          if(cnamex=="cblocko"){
            elx.className="cblockc";
            ely.className="companyc";
          }
          if(cnamex=="cblockc"){
            elx.className="cblocko";
            ely.className="companye";
          }    
          if(cnamex=="pblocko"){
            elx.className="pblockc";
            ely.className="projectc";
          }
          if(cnamex=="pblockc"){
            elx.className="pblocko";
            ely.className="projecte";
          }
        }
</script>

In invoking this function, the user sends both the ID of the initiating element, and the ID of the encapsulating block. The function determines the current state of that element and block, and sets the style to its opposite state. So, for instance, if the current style is "pblockc" (Project Block Closed), the block is set to "pblocko" (open), and the calling element is set to the "expanded" style. You'll see the value of the expanded style in the next section.

<style TYPE="text/css">
ul{
        padding: 0;
        margin-left: 0;
}
li{
        margin-left:0px;
        cursor:pointer;
}
.companye{
    background-color: #527BAD;
    color: #ffffff;
    list-style: square inside url(images/menu-expanded.gif);
    font-size: 10;
}
.companyc{
    background-color: #527BAD;
    color: #ffffff;
    list-style: square inside url(images/menu-collapsed.gif);
    font-size: 10;
}
.companyx{
    background-color: #527BAD;
    color: #ffffff;
    list-style: square inside url(images/menu-leafw.gif);
    font-size: 10;
}
.companyl{
        color: #ffffff;
        text-decoration: none;
}
.projecte{
    background-color: #D7D4A7;
    list-style: circle inside url(images/menu-expanded.gif);
    font-size: 10;
}
.projectc{
    background-color: #D7D4A7;
    list-style: circle inside url(images/menu-collapsed.gif);
    font-size: 10;
}
.projectx{
    background-color: #D7D4A7;
    list-style: circle inside url(images/menu-leafw.gif);
    font-size: 10;
}
.projectl{
        color: #000000;
        text-decoration: none;
}
.object{
    background-color: #FEFFEF;
    list-style: disc inside url(images/menu-leaf.gif);
    margin-left:0px;
    font-size: 10;
}
.objectl{
        color: #000000;
        text-decoration: none;
}
.cblockc{
        display: none;
        padding: 0;
}
.pblockc{
        display: none;
        padding: 0;
}
.cblocko{
        display: inherit;
        padding: 0;
        margin-left: 5;
}
.pblocko{
        padding: 0;
        display: inherit;
        margin-left: 5;
}
</style>

Bear with me if some of the stuff you see here isn't displaying on this page - we're working with dueling style-sheets - but you should be able to discern the basics. The key difference between the closed and expanded links (not the blocks) is the choice of the graphic assigned to list icon. I don't use many graphics on *this* site, but this would be what would normally change the plus sign or right arrow of an unexpanded list to the minus sign or down arrow of an open one. You can also see here the "padding" values which compress the lists into a tight menu layout, "display" values which open and close the blocks.

Well, it ain't Shakespeare, but it works for me. Hope it helps you, too.