Creating Menus in JavaScript

Screenshot of the completed javascript menu

Now that we've learned about how to create layers in CSS, and how to create and manipulate tag properties with JavaScript.

At this point we can start putting this knowledge together - we're going to create a menu system.

We're not going to overengineer this menu such that we demonstrate every possible function in JavaScript, but we will make a useable, flexible menu system that you'll be able to easily use in your own websites.

Once you understand how this menu works, feel free to tinker around with it and improve it!

Step 1 - Defining the menu structure

We're not going to code our menu in HTML in the normal way. We're going to create a JavaScript function which generates the HTML necessary, based upon a database of information containing the structure of the menu.

This will allow us to create a flexible menu system that can be used on multiple pages with only a small amount of extra code on each page, which will be more robust. It will also mean that if we update the menu structure, the menus on all our HTML pages will be updated automatically.

To start with we'll create the menu structure database. In this case it will be an array of links. JavaScript supports multidimensional arrays (arrays within arrays), which allow us to define our menus easily.

var MENU_ITEMS = [
['Home', 'http://www.intelligent-web.co.uk', null],
['Tutorials', '../index.html', [
    ['1. Layers in CSS', '../css_layers.html', null],
    ['2. Basic JavaScript', '../basic_javascript.html', null],
    ['3. Menus in JavaScript', '../javascript_menus.html', null],
    ['4. Clever Form Validation', '../javascript_forms.html', null],
    ['5. Javascript Games', '../javascript_games.html', null],
    ['6. AJAX', '../javascript_ajax.html', null],
    ['7. Transparency Effects', '../css_transparency.html', null],
    ['8. Paypal Integration', '../paypal_integration.html', null]
    ]],
['Links', null, [
    ['W3C', 'http://www.w3.org/', null],
    ['Essex University', 'http://www.essex.ac.uk', null],
    ['Google UK', 'http://www.google.co.uk', null],
    ['B3TA', 'http://www.b3ta.com', null]
]]
];

As you can see, the array we defined contains a number of smaller arrays. We've allocated three positions in each array the first position is for the name of the menu, the second for the actual URL to navigate to, and the third allows us to insert a sub menu, which in turn is another array of arrays. If the menu does not have a submenu, we use the null keyword to indicate this.

Take a moment to familiarise yourself with the code (the multiple brackets may be confusing at first!). It helps to arrange your code with tabs, so that it's in an easily readable fashion.

Since we want this file to be accessible to all pages, we can't define it within a particular page, otherwise the other pages would not be able to access it. This time we're going to save it as a separate Javascript file, called menu_items.js. To access it from our HTML page, we'll take advantage of the src attribute provided by the <script> tag:

<script type="text/javascript" src="menu_items.js" ></script>

Step 2 - Parsing the array to build the menu

We'll now start to build our JavaScript function to read this array and turn it into a nice looking menu that our visitors will appreciate using.

The basic idea behind our JavaScript is shown below:

readMenu(MENU_ITEMS);

function readMenu(array) {

	// loop through the items in this menu

        // extract the information we need from the array

        // print out the item

		// if the item has a sub menu {
            // call readMenu() again to print that out too.
        // }

    // end loop

}

Step 3 - Writing the first function

We'll start putting this into practice step by step. First of all, we'll create a for loop to iterate through the items in the array. We can then extract each array in turn and get out the information we need.

    // loop through the items in this menu
    for (i = 0; i < array.length; i++) {

        // Get out one menu at a time
        var menu = array[i];

        // extract the information we want from the array
        var name = menu[0];
        var url = menu[1];
        var submenu = menu[2];

    }

Step 4 - Creating the top level menus

OK. We have an idea of how to read the array and extract the data. But what we need to do is actually convert this information into HTML that the user can interact with.

We'll start by printing out the top level menus. Each top level menu will be a table. We'll use CSS absolute positioning to put the tables exactly where we want them.

Using absolute positioning means that we need to remember the different X and Y positions so that we can put each menu in the right place. To do this, we'll pass X and Y parameters to our method, and each time we add a new top level menu, we'll increase the value of the X variable so that the next menu is further to the right.

function readMenu(array, x, y) {

    ...

    // print out the item
    document.write("<table border='1' Carriage Return
    style='position: absolute; left: " + x + "; top: " + y + ";'>");
    document.write("<tr><td>");
    document.write(name);
    document.write("</td></tr>");
    document.write("</table>");

    x = x + menuWidth + xGap;

    ...

}

You'll notice we've also created a couple of variables, called menuWidth and xGap, these are defined at the top of our JavaScript function. We'll be able to change these later to tweak our menu's size and shape.

Thus far we've managed to print out (in a very ugly manner - but we'll fix that later!) the top level elements within our menu structure. Now we'll concentrate on printing out the sub menus of each of these.

Since the sub menus are different in format to the top menus (they list downwards as opposed to across), we'll create a different function to print them out.

As we proceed through each top level menu, we can check to see if it contains a sub menu. If this is the case we call the readSubMenu() function to create that menu as another layer.

Step 5 - Adding the sub menus

    // if the item has a sub menu, call readMenu() again to print that out too.
    if (submenu != null) {
        readSubmenu(submenu, x, y);
    }

The readSubMenu function is very similar to the readMenu function. (Indeed they could both be written using a single recursive function, but that would add additional complexity)

We supply a submenu name to the menu so that we can refer to it using the document.getElementById() function later. This will be important once we want to show and hide our menus.



    function readSubMenu(submenu, x, y, id) {

        // first we create a container object for this entire menu.
        //  This allows us to hide the whole menu at once.
        document.write("<table border='1' id='" + id + "' Carriage Return
        style='position: absolute; left: " + x + Carriage Return
        "; top: " + (y + menuHeight + yGap) + ";'>");


        // loop through the items in this menu
        for (var j = 0; j < submenu.length; j++) {

            // Get out one menu at a time
            var menu = submenu[j];

            // extract the information we want from the array
            var name = menu[0];
            var url = menu[1];

            document.write("<tr><td>");
            document.write(name);
            document.write("</td></tr>");

        }

        document.write("</table>");

    }

Step 6 - Making the menu work

The readSubMenu function creates a layer (in this case a table which will contain each individual menu item on separate rows. If you run this example (menu5.html) you'll see that we have now printed out all the items on the list in the correct order.

We now need to start making the menu dynamic - submenus should appear only when necessary, and the links should take us to places.

We'll start by changing the menus so that when we click on them we are taken to the appropriate URL:

    // create the onClick event. 
    var onClick;
    if (url != null) { onClick = "self.location.href=\"" + url + "\";" };

    document.write("<tr><td onClick='" + onClick + "'>");
    document.write(name);
    document.write("</td></tr>");

Step 7 - Styling our menu

At this stage we should start to think about styles for our objects. We need to make our submenus invisible at first, and we need to change the cursor when the user hovers on each menu. Additionally, it would be nice to make our menu look a little more presentable!

Below is a basic stylesheet that we'll use for our menus. Once we've completed our menu system, we'll have a play around with the stylesheet so that our menu looks good.


    td.topMenu_Over {
        cursor: hand;
        background: blue;
        color: white;
    }

    td.topMenu_Out {

    }
    td.subMenu_Over {
        cursor: hand;
        background: blue;
        color: white;
    }

    td.subMenu_Out {

    }
    table.subMenu {
        visibility: hidden;
        background: white;
        border: 1px solid gray;
    }

We can write in the class="" attributes easily into our JavaScript:

    document.write("<tr><td class='topMenu_Out' onClick='" + onClick + "'>");

Step 8. Showing and Hiding Submenus

Now that we've set our submenu tables to be invisible by default, we can no longer see them. What we need is for the relevant submenu to appear when the user puts his or her mouse over a top level menu.

We'll create another couple of events for our top level menus to highlight the menu and display the submenu.


    var onClick;
    if (url != null) { onClick = "self.location.href=\"" + url + "\";" };

    var onMouseOver;
    if (submenu != null) { onMouseOver = "showSubMenu(\"" + subMenuName + "\");";

    document.write("<tr><td class='topMenu_Out' onMouseOver='" + onMouseOver + Carriage Return
    "' onClick='" + onClick + "'>");
    document.write(name);
    document.write("</td></tr>");

We've asked the menu to run the showSubMenu method when the mouse moves over it. The showSubMenu makes the menu visible:


    function showSubMenu(id) {
        document.getElementById(id).style.visibility = "visible";
    }

This is reasonable, but has the problem that more than one submenu can be visible at once. What would be better is to have a function to close the previous menu before showing the next one. This only requires a few extra lines of code.



    // remember the id of the previously opened menu.
    var previousMenuId;

    function showSubMenu(id) {
        // if this menu is a new menu being opened...
        if (id != previousMenuId) {

            // hide the other one
            hidePreviousMenu();

            // display the new one...
            document.getElementById(id).style.visibility = "visible";
            previousMenuId = id;
        }
    }

    // hides the currently open menu
    function hidePreviousMenu() {
        if (previousMenuId != null) {
            document.getElementById(previousMenuId).style.visibility = "hidden";
            previousMenuId = null;
        }
    }

Well, maybe more than just a couple of additional lines, but the meaning should hopefully be clear. What we do is remember the id of a menu as we show it, so once we open a new submenu, we know which other menu to hide first.

If we run our menu now, it's starting to feel a little bit more like a proper menu system.

There are just a couple of extra things we need to do.

Step 9 - Hiding menus automatically.

So that the sub menus hide themselves automatically, we'll create a new method hideMe and we'll have the submenus call it upon themselves once the user moves the mouse away from them, with a little extra code onMouseOut='hideMe(this);'.

Our hideMe() function could hide the menu straightaway simply by changing the visibility property to "hidden", like so:


    // hides a sub menu
    function hideMe(submenu) {
        submenu.style.visibility = "hidden";
        previousMenuId = null;
    }

But unfortunately, this code wouldn't work very well. As soon as the user moves the mouse outside the menu, the menu would close. If there is a gap between the top menu and the sub menu, the sub menu would hide itself before the user could move the mouse over the submenu!

What we'll do instead is introduce a delay so that a menu only dissapears after the user moves the mouse away for a certain time.


    var aboutToHide;

    // hides a sub menu
    function hideMe(submenu) {
        if (aboutToHide == null) {

            // remember the object we're going to hide
            aboutToHide = submenu;

            // call the hide immediately function in 500ms.
            // if the cancelHide() function is called inbetween times,
            // the hideImmediately() function will not work.
            
            setTimeout("hideImmediately();", 500);
        }
    }

    // prevents the hideImmediately function from hiding the menu.
    function cancelHide() {
        aboutToHide = null;
    }

    // hides a menu immediately.
    function hideImmediately() {
        if (aboutToHide != null) {
            aboutToHide.style.visibility = "hidden";
            previousMenuId = null;
        }
    }

Essentially, this code updates the hideMe() function so that it does the hiding process after a short delay. In this instance the delay is 500ms, or half a second. We use the setTimeout() function in JavaScript which allows us to call a function after a given delay. We then add the ability to cancel the hide with the cancelHide() function. We add another event to the submenu, this time we'll add the cancelHide() function to the onMouseOver event.

This means that if the user moves the mouse out, the hideMe() function will hide the submenu in half a second. However, if the user moves the mouse back onto the submenu, the cancelHide() function is run, which stops the hide function from working once the half second is up.

We now have a menu system which works and behaves like a menu should. In the last section, we'll add a few finishing touches so that our menu really looks the part too.

Step 10: Finishing Touches

All that remains to do now, is to make the menus light up as the mouse moves over them. This we will do by changing the class of each menu as the mouse rolls over and out.

We've already added an onClick function to each menu item, we'll simply add a onMouseOver and onMouseOut function to each, and make use of the other CSS classes that we made earlier.


    var onMouseOver = "this.className=\"subMenu_Over\"";
    var onMouseOut = "this.className=\"subMenu_Out\"";

    document.write("<tr><td class='subMenu_Out' onMouseOver='" + onMouseOver + Carriage Return
    "' onMouseOut='" + onMouseOut + "' onClick='" + onClick + "'>");

And that's it! You can view the completed menu example here.

Now that you've the basics of the menus, you can put all the javascript code into a js file, and link it from your program, allowing you to add the menus to your page with only one or two lines of boilerplate code.

You may also like to try making your menu look a bit more interesting. Here's another one

Back to tutorials