CSS And HTML Two Level Menus Take One
This article will continue from where I left of with HTML and CSS List Based Menus.
For a lot of websites having a menu such as the one we built in the
previous article will completely fulfill the needs of the website owner
and it's users, however for some sites, such as the zones on DZone, there is a
definite need for a second level of navigation. These second level
elements also needs to be instantly accessible to the website users in
the same manner as it's one level counterpart. That is then the topic
of this article, creating drop-down and fly-out menu's using cascading
style sheets (CSS) and semantically coded HyperText Markup Language
(HTML). While in this article we will focus on HTML and CSS in part two we will also look at how to encourage
Internet Explorer 6, and below, to play along with a sprinkling of
JavaScript to overcome it's limitation of only acknowledging :link,
:hover and the rest of the CSS link styles when applied to <a>
tags and not any other HTML element. We will also look at the quirks presented by the various browsers and how to overcome them.
First let's look at the
HTML that we ended up with at the end of the last article as we will
be using it as the basis of this article:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>HTML and CSS Drop Down Menu</title>
<link rel="stylesheet" type="text/css" href="css/default.css" media="screen">
</head>
<body>
<ul id="nav">
<li><a href="index.html" title="return to front page">Home</a></li>
<li><a href="about.html" title="learn more about us">About</a></li>
<li><a href="portfolio.html" title="see some samples of our work">Portfolio</a></li>
<li><a href="contact.html" title="contact us">Contact</a></li>
</ul>
</body>
</html>
One of the great things about lists, and which further illustrates why it is so apt to use for menu's, is the fact that one can nest one list inside another. This is then exactly what we will need to do to get started on our two level menu. So isolating the unordered list part of the code change it to reflect the following:
<ul id="nav">
<li><a href="index.html" title="return to front page">Home</a></li>
<li><a href="about.html" title="learn more about us">About</a>
<ul>
<li>Corporate Profile</li>
<li>Employee Profiles</li>
<li>Divisions</li>
</ul>
</li>
<li><a href="portfolio.html" title="see some samples of our work">Portfolio</a></li>
<li><a href="contact.html" title="contact us">Contact</a></li>
</ul>
As you can see from the code above I added a nested unordered list containing three elements. Go ahead and open this in your browser, you should see the following:

NOTE: Remember to include this line on your new HTML page: <link rel="stylesheet" type="text/css" href="css/default.css" media="screen">
We are on our way to creating the drop-down menu. The first thing we need to do is have the second level elements hidden initially. Start by creating a new CSS file called dropdown.css and add the following code to it:
#nav li ul
{
display:none;
}
What this style rule is telling the browser is that any <ul> contained inside a <li> that is inside an element with and id of nav should not be displayed. One important thing to note here is that we are using display:none and not just vissibility:hidden. There is a very good reason for this, when using the visibility attribute the content is hidden by still takes up the space in the HTML document. On the other hand display:none completely removes the content from the DOM and gives us exactly what we are after. Next, remember to link your new style sheet in the head of your HTML document:
<link rel="stylesheet" type="text/css" href="css/dropdown.css" media="screen">
Next let's target the :hover and :focus states of our 'About' list element and instruct the browser to show the <ul> contained inside when one of these states are triggered. Add the following to your CSS file:
#nav li:hover ul, #nav li:focus ul
{
display:block;
}
If you now reload your page and move your mouse over the About link you should see the following:

While it is definitely not what we are after, at least the :hover and :focus states does show the nested <ul>. To get rid of the expanding 'About' we need to take the nested <ul> out of the default flow of the document. We do this by positioning the unordered list absolutely as follows:
#nav li:hover ul, #nav li:focus ul
{
position:absolute;
display:block;
}
Refresh your browser window and mouse over the 'About' link, this should now produce the following:

This is getting closer to our final result. One other thing we would want to change is to change the layout of the nested <ul> from inline back to it's default, making them links and styling them appropriately. So let's get started. Add the following to your CSS file to stop the second level ul from floating:
#nav li ul li
{
float:none;
}
Next we will change the three items to links:
<ul id="nav">
<li><a href="index.html" title="return to front page">Home</a></li>
<li><a href="about.html" title="learn more about us">About</a>
<ul>
<li><a href="corporate.html" title="Read our corporate profile">Corporate Profile</a></li>
<li><a href="employees.html" title="Read more about the people that work here.">Employee Profiles</a></li>
<li><a href="divisions.html" title="Read more about our various divisions">Divisions</a></li>
</ul>
</li>
<li><a href="portfolio.html" title="see some samples of our work">Portfolio</a></li>
<li><a href="contact.html" title="contact us">Contact</a></li>
</ul>
Refreshing the page and hovering over the 'About' link should produce the following:

Great! Because of the style rules we inherit from the default.css file the second level links was instantly styled. But let's not stop there, let's add something to seperate the drop-down a little more from the top level menu. By simply adding a 1 pixel top border to the drop-down menu we have a great looking and functioning drop-down menu:
#nav li ul li
{
float:none;
border-top:1px solid #fff;
}
Seeing that we have our drop down working, let's next look at creating a fly-out menu. As you know from the previous article the only thing we need to change in our HTML is get rid of the two currently linked stylesheets and add the following:
<link rel="stylesheet" type="text/css" href="css/vert_menu.css" media="screen">
As we did for the drop-down menu we will also here create a CSS file for our fly-out menu and link it to our new HTML page. Before we add the new CSS let's load up our new page and see what we are presented with:

Not to bad but, there is a mess going on there in the middle. As with the drop-down our first order of business is to add a style rule to initially hide our second level menu items.
#nav li ul
{
display:none;
}
Next let's link our new CSS file:
<link rel="stylesheet" type="text/css" href="css/flyout.css" media="screen">
Next add the rule to show the hidden menu items when either the :hover or :focus states are triggered:
#nav li:hover ul, #nav li:focus ul
{
display:block;
}
The result:

As we are not after the accordion effect here we need to take the nested <ul> out of the natural flow of the document and display it absolutely:
#nav li:hover ul, #nav li:focus ul
{
position:absolute;
display:block;
}
This still does not give us the desired effect and we need to look a bit further. But before we do that, let's complicate this situation just a little more. Nest the following unordered list inside your current 'Portfolio' list item:
<li><a href="portfolio.html" title="see some samples of our work">Portfolio</a>
<ul>
<li><a href="branding.html" title="Read more about logo design services">Logo Design</a></li>
<li><a href="webdesign.html" title="Read more about website design services.">Website Design</a></li>
<li><a href="webapps.html" title="Read more about our web applications">Web Applications</a></li>
</ul>
</li>
With this code added we will now have two fly-out menus, one behind 'About' and one behind 'Portfolio'. Now let's get these to fly out to the right side of the menu. Seeing that our sub menu items are now out of the flow of the document, and seeing that we have a width of 15 em set as the width of our first level menu's, we should simply be able to add the following and be good to go:
#nav li:hover ul, #nav li:focus ul
{
position:absolute;
top:0;
left:15em;
display:block;
}
Let's refresh the page and see what the result is:

Wow! Ok this is not what we are after. Now we can go ahead and tweak
the left positioning and the top position but, we would have to do this
for each and every fly-out menu and provides no re-usability,
definitely not what we want so, back to the drawing board. The problem
is that currently the top and left position of the sub menu is being
calculated based on the browser window's top corner and distance from
the left edge of the browser. To fix this we need to change our
container to contain the rule position:relative, this allows us to
absolutely position our fly-out based on the top and left as well as
right and bottom of our containing element and not the browser window.
Our first task is to add a class to our two containers i.e. 'About' and 'Portfolio':
<li class="second-level">
I named the class second-level as this is our container element for our second level menu items. Now we need to add the following to the top of our flyout.css file:
#nav li.second-level
{
position:relative;
}
Next change the CSS for our hover and focus states as follows:
#nav li:hover ul, #nav li:focus ul
{
position:absolute;
top:0;
margin-left:100%;
display:block;
}
Refresh your browser again and you should see:

Without changing anything the same should be true for the 'Portfolio' fly-out. Great! so this works well but, we need to add some style to clearly seperate our flyout from the first level menu items. Here I simply added a right border that is darker then the :link and :hover states.
#nav li.second-level li
{
border-left:5px solid #376D00;
}
Now this creates a nice seperation for our second level links:

That is a lot of green I admit but, if you have been following this tutorial using Firefox, you may be thinking, Great! Now I can go ahead and implement this on my new website. But before you do, stop, drop and roll. The pain is about to start. We now have to test this menu in the major four browsers, Firefox, Opera, Safari and Internet Explorer and then we also need to see how our old friend IE6 behaves. In part 2 of this series I will cover how to ensure your drop-down and fly-out menu's work in all browsers using both the tried and trusted Son Of Suckerfish solution as well as seeing how we can do it using jQuery
- Login or register to post comments
- 5895 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
webmist replied on Thu, 2008/10/23 - 3:06am
Please help. am a student trying to learn how to build HTML and CSS coded menus without the use of Javascript. Have been following the list based menus on this site and all was good till I tried to add the second level.
#nav li ul {display:none;
visibility:hidden; (I added this for screen readers by the way)
}
that works fine, the submenu disappears as it should. The problem is that it doesnt come back when I add
#nav li:hover ul, #nav li:focus ul {
display:block;
}
I have redone the whole thing from scratch three times but I cannot get this to work! help!
Also have a problem in that Firefox 3 shows a bullet with each of the top menu items (ok in IE which is a change) any ideas there?
thanks in anticipation
Judy (Australia)
Schalk Neethling replied on Fri, 2008/10/24 - 4:39am
in response to: webmist
Hi there Judy,
Are you using all of this to bring the drop back into view:
Also, I am posting a follow up on this article on Monday that will close all the gaps and make it more screen reader accessible. Unfortuantely there is no way to get away without JavaScript if you need to support IE6. I will be covering this in the next article as well.
webmist replied on Fri, 2008/10/24 - 11:14pm
Thank you very much for your reply. Since leaving my help request and your answer I have done a lot of experimentation with Suckerfish and Son of Suckerfish but was still having problems..maybe my brain was a bit stuffed. A weekend has almost finished and I have just deleted all the existing files and started again from scratch, and would you believe this time it works? perfectly in IE7,Opera 9.6, Firefox 3and Safari 3.1.2.I dont have IE 6 anymore but maybe I ought to reinstall it since there appear to be so many problems with people using it?
I have made one change that I think looks better, that is to go back to
#nav li:hover ul, #nav li:focus ul { position:relative; display:block;} so that when the dropdown appears, those links are under just the one main link rather than running under another one.
I would like however to know how to do those submenus you see on some sites, where the submenu opens up directly under the main link, usually in smaller fonts, and runs horizontally too.,usually starting back toward the left and running under adjacent links, I hope you know what I am talking about. If you get time, can you give us all some clues about this , or refer us to other relevant websites?
Thank you very much to you and all the other developers who put these lessons online for us who are still trying to come to grips with website design. I particularly like yours because you explain step by step what is happening, allowing me to understand as I do it, what action creates what effect. I hope you will continue to have time to put your stuff out there for us.
best wishes
Judy
webmist replied on Sat, 2008/10/25 - 5:28am
Update to my last comment: first of all, changed back to display:absolute because how I did it moved the other level one items down further below the nav bar - whch of course you would have realised.
Now am having a problem which didnt notice before;that is that when I first hover over the main menu and the submenu appears, it is only there the first time, I can go to each submenu item with no problems. But if I move the mouse off that and then go back, it is there to see but disappears when I try to move onto an item? this is occuring in all browsers. Refreshing the page fixes it again, momentarily , but obviously something is amiss.Please help, or maybe I ought to just read whaever you are going to add on Monday.
thanks
Judy
Schalk Neethling replied on Sat, 2008/10/25 - 8:32am
in response to: webmist
Hi there Judy, Thank youfor your kind words and questions. I am currently writing the second part of the article series and will make a point of addresseing all of the questions you have posted here.
Looking forward to your feedback on take two.
Kind Regards,
Schalk
Schalk Neethling replied on Mon, 2008/11/17 - 6:43pm
Hi there Judy,
Finally had the time to complete the second part of the series, you can read it here:
http://css.dzone.com/news/css-and-html-two-level-menus-t-0
alan11 replied on Fri, 2009/03/13 - 3:18pm
Hi Schalk,
Thank you for this very informative article. It describes exactly what I want to do. However, I got lost at the point where you added ( li class="second-level" ). From that point forward the code (as I entered it) did not do what you said it should do. Would you mind posting the complete source listing so that I can see where I went wrong?
Best regards,
Alan