Building An Expanding DHTML Menu With CSS and JavaScript

Today I am going to show you the different parts that make up a dropdown vertical menu for your website. This is not like your normal dropdown menu, which appears at the top of your content, however — these menus expand and remain visible until they are collapsed with the click of a mouse. The menu content actually expands with your page content, pushing the rest of whatever is below it down – so it can actually be used for more than just a menu (I might explore that a little later in another tutorial).

For now, let’s get it started…

What does the finished menu look like and how does it function?

Here’s a snapshot of what the menu looks like all coded up.

When you click on one of the links ie. Menu 1, Menu 2, Menu 3, Menu 4, Menu 5, this will expand or contract the submenu for that menu item. Basically, the end user must have JavaScript enabled to allow the submenus to work.

Let’s build the menu html

First I’m going to start with the 5 menus items. Here’s the code that I’m using, which is very simple; 5 hyperlinks. I will need to apply a style to these level one links, so I will make a class style called “menu1″.

Now I will create the menu1 class style and put it in the head of my document. So far we haven’t done anying revolutionary. I won’t explain the style that I’ve applied here because it’s pretty straight forward – it’s just a background image and the display:block makes it display like a rectangle.

Now I’ll add the sub menus in place exactly where they will appear. In this part, we will use some small tricks to setup the alignment. Firstly, we’ll wrap the whole submenu in a div that has a unique id for each menu. You can choose your own names for the div tags, but the main purpose of the div having a unique id is so that JavaScript can target that div specifically and make it appear or disappear. I also add a class style called ‘hide’ so that I can choose to hide all of the submenus from within my style sheet. The links are then listed within the div just like the main menu items and are given a submenu class style, to allow me to control the look of those in my style sheet as well:

I’ll also create the style that will show the hidden div tags (submenus):

.show{ display: block; }

Creating the JavaScript

If you’re not too familiar with JavaScript or are in a rush, feel free to just copy and paste the code shown below into your page header as is. There are no further tricky parts to setup as the JavaScript simply compares the current state of the chosen submenu and then swaps it, meaning that if it’s hidden, then it will be made visible, and vice versa.

Some Geek Speak, for those who just want to know what’s happening in the code.

Ok. What’s happening here is that when the page loads, the JavaScript and CSS styles in the head will load first and then your menu links will appear in the body of the page. JavaScript is loaded from top to bottom by the browser, so let’s read from inside the script tags:

The menu_status = new Array(); line will create a container ready to store the current state of your menu. This corresponds to the sections of the code that actually say what this menu_status now equals. eg. menu_status = ‘show’; and menu_status = ‘hide’;

There’s a function called showHide(theid) but this won’t do anything unless it is called from within your HTML code. ‘theid’ is the id of the menu being shown or hidden, and it’s called during the onClick even of that menu, such as onclick=”showHide(‘mymenu5′)”.

Ok, so the function loads ready to be used, but the page continues to load…

Your main links and submenus load, but only the main links show because the ‘hide’ style that’s applied to the submenus will hide the submenus. The submenus have in fact loaded already, they just aren’t showing just yet.

If you click on a main menu link, then here’s what happens:

The onClick event will fire up the showHide function and JavaScript will know which object we are working with by the id of the link.

JavaScript will get the menu element in question by the id that’s passed to the function

JavaScript now has to check whether the current menu_status is visible or not, and then swap it. If it’s not ‘show’ eg. if(menu_status[theid] != ‘show’) then the status_id will be made to equal ‘show’. If it’s not hide, eg. if(menu_status[theid] != ‘hide’) then it will be made to equal ‘hide’.

Conclusion In this article I’ve shown you how to build a basic vertical drop down menu using DHTML and JavaScript. This is a groovy little script that will allow you to create a nice menu, or even save you some space if you currently have multi level menus laid out on your site. You can probably do more with this script if you’re familiar with JavaScript or don’t mind experimenting, but we’ll leave it at that for now!

90 Responses to “Building An Expanding DHTML Menu With CSS and JavaScript”

This is excellent! Thank you very much! Your tutorial of an expanding menu was just about the only one that I understood, and wasn't needlessly complicated. It is also very cross-platform, which isn't something I can say for some of the others I've run across. I will definetely reccomend your tutorial(s) to others when asked. Linked in my favorites and from my website.

Yay! Thank you so much! It's exactly what I was looking for. I'd found a way over complicated version that didn't work with 3 layers cause you could only have one menu open and I couldn't work out how to stop that. But this is perfect! Such simple coding too, I think I may learn it _

Yeah just what I needed. And similar to comment #2 I´ve been searching the net over and over for a simple and flexible "3-layer-expanding-menu" et voilá I found it here. And last but not least I understood what I did. Thanks to you.

Very simplified article… good to see someone get it right. I would like to learn how to add persistent states to this function. I'm sure it would be as simple as adding a cookie function into the .js, I guess that's the next tutorial I'm looking for.

I liked how the geek speak was at the bottom. I am just an 18-yr-old interested in computers and I learned html and am beginning javascript. Your methods were new to my experience and interesting to peruse.

Great tutorial, very easy to understand. I am also looking for a persistent state of this. Also for teh person that suggested putting in href="javascript:?" in the link to create the normal mouseover action works but creates an error on the page. Anyone know a way to remove this error?

To fix the error with making the menus have a "click cursor", instead of typing href="javascript:?", type href="javascript:;". That way, the actual javascript will run normally, even though there is nothing to run in the script.

A good example and explained well. I suggest that you only need to query the current classname of the object and switch it between hide/show. Also you can collapse the last one expanded by keeping the name of the last expanded one.

how much of a geek does jerry at comment #7 sound. Jerry if your reading there's something you can gert to help your coding its called a life get out more and chase girls. Incidentally tutorial looks great just what I've been falling asleep looking for, for the last 20 mins

My goodness, if what all the comments
say are true, the author must be a heck
of a writer. He could be a new Dan Brown
if he switches to fiction writing. So,
to give due credit, congrats to the author.

Well, it's a good code and all, but I find one problem in it: If you copy and paste the exact code, then when you click ANYWHERE which horizontally leads to the line, it makes the menu drop down. It's not just clicking the letters themselves, but clicking the whole line of the letters.

Thank you, this is amazing and exactly what I have been looking for to add to my site! It is easy to follow and even easier to apply to an already pre-made site. I dont know JavaScript at all, but I was still able to use this. Thanks so much =)

This is the best tutorial I've found. The code is simple, and easy to understand without extra, fussy details that block the true nature of the code. In 28 lines of CLEAN CODE, you've done better than what others couldn't do in more than 40 lines of TRERRIBLE code.

Hi what would i need to do to add sub menu's to the sub menu's?
i.e another teir of submenus. e.g heading of "tools" that opens "spanners"
that opens sub menu's of "ring spanners" and "open end spanners" etc.

forgive my ignorance, but when i view this in ie, i have the following issue. i have placed text to the right of the menu. when i select a menu item the text stays in place, but it is indented to the right as the menu expands. is there an ie fix for this? any help is appreciated.

That is the easiest widget of this type I have seen. I would like to persist this state so that if the same menu opens on a different html page, it would remember whether it was open or closed. Would I do that by passing it through a querystring, then checking for the querystring on the page load? Sorry, not so good with javascript. And that's why I'm very thankful for this, Joseph!

Great tutorial, but like mentioned by other people before, it would be just so much greater if it included some optional code snipets for things like
Expand just one menu at a time (i.e. Menu1 closes when I open Menu2)
Retain memory of which submenu is open (i.e. when I click Link1, Menu1 submenu will remain open)
How to add links to main menu items without losing submenu functionality (i.e. when I click on Menu1 I am taken to a Menu1 overview page, but Submenu1 still opens).
If anybody knows how to do this and posted the code, I'm sure lots of people would be eternally grateful (including myself)!

it's really nice but its not really user convenent if u see the site menu
that every menu before this menu are very sensitive like onmouseover when we keep mouse on that it automatically show the submenu and u told that its space saving but i don't think so because when we click on menu it will open and take more space than other menu other menu only show the submenu and automatically close when we simply keep our mouse far from that but here we have to again click on menu1 and than it close the submenu.
still i feel its nice why i don't know…….

This was great help for what I am trying to achieve. I see a number of people posting here have asked for instructions on adding another level of sub-menus to sub-menu items. I was able to do this (parent > child > child) by adding a .subsubmenu class and a set of mysubmenu divs and calling them where appropriate in the menu. I've also added the cursor instruction so that the pointer is a hand, as per comment #57 by Ian. Dave's instructions (#18) got me part way to the next level of functionality I was after: previously expanded main menu item collapses when a different main menu item is clicked, so that only one main menu item is expanded at a time. Thanks Dave! I have one issue with it though: I need this to be ignored when it's a sub-menu that has another set of sub-sub-menu items to be displayed. I suppose that will be another "if" statement in Dave's script. Dave, are you out there?

Another question asked several times but unanswered is how to make the current state of the menu remain intact when a sub-menu item is clicked and the visitor is taken to the linked page. This is akin to cheating, but how I plan to do this is to create a different menu for each main menu item's related pages. In each of these menus I will change the "hide" state to "show" so that they match what main menu item was expanded when they clicked to that page. I will use server side includes to call up the appropriate menu. It would be nicer to build the functionality into one menu, but this should work. It won't actually be "retaining" its state, but on each sub page it will display the way it was when the visitor clicked something.

Great stuff. In comment #18 Dave provides the code to designate that only one menu item is expanded at a time. Now, I'd like to know how to tweak that so that when a sub menu has another level (sub sub menu) that function is ignored. The way it is now, when I click on my sub menu item, it's parent menu collapses.