Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

CSS And HTML Two Level Menus Take One

DZone's Guide to

CSS And HTML Two Level Menus Take One

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

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:

[img_assist|nid=5060|title=|desc=|link=none|align=none|width=490|height=66]

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:

[img_assist|nid=5061|title=|desc=|link=none|align=none|width=489|height=64]

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:

[img_assist|nid=5064|title=|desc=|link=none|align=none|width=344|height=63]

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:

[img_assist|nid=5065|title=|desc=|link=none|align=none|width=265|height=158]

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:

[img_assist|nid=5067|title=|desc=|link=none|align=none|width=226|height=277]

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:

[img_assist|nid=5069|title=|desc=|link=none|align=none|width=223|height=275]

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:

[img_assist|nid=5070|title=|desc=|link=none|align=none|width=432|height=164]

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:

[img_assist|nid=5071|title=|desc=|link=none|align=none|width=440|height=163]

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:

[img_assist|nid=5072|title=|desc=|link=none|align=none|width=441|height=162]

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

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}