CSS And HTML Two Level Menus Take Two
I ended the first part of this two part series with a working two level
drop down and fly out menu using CSS and HTML. However, we did find
that all was not so well when it came to, particularly, Internet
Explorer 6. The aim of this part of the series then is to close the
gaps and ensure that our menus will work in Firefox, Opera 9.52 and
9.6, Safari 3, Internet Explorer 6 and 7 and for good measure, we will
ensure that it works with Google Chrome. So let's get started.
For
me the easiest way to tackle a situation like this is to list the
browsers and versions you want to, or need to, support and then work
from the top down. So for this article we will use the order of the
browsers as mentioned above. One important thing to note is that once
the menus work in your current test browser and you move on to the
next, you cannot simply forget about the previous browser. Excluding
Internet Explorer, which we will target directly using conditional
comments, we will more then likely need to make tweaks that might
effect the browsers that has already passed the test, so we have to go
back and confirm that everything is still 'a ok' in all tested browsers.
With
that said let's open up the dropdown.html and flyout.html files in
Firefox. No surprise here, as we saw in the previous article Firefox
displays our menu exactly as expected. No that was easy enough, next up
Opera 9.52. Open up the same two files. Again no surprises, let's open
these files in the latest version of Opera, Opera 9.6. Everything is
still as expected so on to the Safari it is. Still everything is as
expected, isn't developing for the web simple and easy.
Next up
is our old favourite Internet Explorer. Testing multiple versions of IE
on the same machine has been a problem developers has been faced with
for a long time. Evolt.org created the browser graveyard a while ago to
keep copies of all of the old browsers version and also had version
that you could install side by side on one machine. For the most part
they worked well and is still used by many developers.
Some
developers use services such as browser shots to get a screen shot of
their page in various browsers on various platforms and this definitely
has it's place. However, when you want to test interactive elements of
a page such as these menus, this type of testing just won't do. I
recently discovered IE Tester from Core Services. Even though this is
still only in alpha it is already an awesome tool and my testing tool
of choice when it comes to IE.
If you do not already have a copy of IETester, head over to the IETester site and download the latest version
for free before continuing. After the download completes, go ahead and
install your copy. The perfect environment for running IETester is
Windows Vista or Windows XP with IE7 installed. Once you have IETester
installed go ahead and launch the application. You will be presented
with the following screen:

Next we need to open up a tab with our first version of IE that we want to test our menu in. I will work from highest to lowest here. Go ahead and click the down arrow on the 'New Tab' button and select IE 8 beta 2.

Now here we encounter one of the missing features in IETester and that
is the ability to open a local file in the current tab. So in order to
do this, pop open another browser and open the drop-down and fly-out
HTML files in two seperate tabs. Next copy the location of the drop
down menu from the address bar and paste it into the address bar of
your IE8 tab and hit enter. Next open another IE8 tab, copy the
location of the fly-out menu from the browser and paste it into the new
tab.
Test both of these and you should find that the menus still
look and function perfectly in IE8. Next step is IE7. Close the current
two tabs and open up two IE 7 tabs. Again, copy and paste the location
of both the menu files into the new IE7 tabs and test to see whether
the menus still looks and behaves as expected.
Here is where I
find another possible bug in IETester, please confirm in the comments
if you experience the same, as I have IE7 installed on my computer
trying to use the IE7 tabs inside IETester does not work. If I paste
the location inside the address bar and hit enter, it just does
nothing. I suspect this might have something to do with the security
settings of IE7 where, if you launch IE7 and then try to open one of
the local files you will receive the following message:

Clicking the ok button will open a new window and launch your file. So
for the sake of IE7 testing I am going to launch the actual IE7 browser
and not do this testing inside IETester. So, after the new window has
opened and the file loaded you can go ahead and open a new tab and
paste the location of the other document in here. This time IE will not
display the error message and simply render your file.
If you
look at the drop-down menu in IE7 everything looks fine and the menu
functions as expected. Unfortunately the same cannot be said for the
fly-out. Looking at this, you will see that for the two menu items that
contain the fly-out's there is to much spacing between it and the
previous menu item so, we have some work to do.

So what is causing this additional 1 px top padding? Well, as it turns out, when we set the two list items containing the fly-outs to position:relative IE7 added a one pixel margin to the top of each of these. So changing our second-level rule as follows fixes this:
#nav li.second-level
{
position:relative;
margin-top:-1px;
}
Now, the one problem this brings is that if you now view this in any of
our other browsers again, our menu is now broken in those so, we need
to find a way to tell IE to apply the -1 pixel top margin but have the
other browsers ignore it. But we also need to go one step further, as
IE8 rendered it correctly we also do not want IE8 to apply this margin
so at this point, we need to target only IE7. How do we do this? IE
conditional comments to the rescue.
Inside your CSS folder create a new file and call it ie7.css. All we currently need to add to this file is the following:
#nav li.second-level
{
margin-top:-1px;
}
P.S. Remember to remove the top margin rule from flyout.css file.
Now
we need to link in our ie7.css file using the special IE conditional
comments. This is then the basic syntax of IE conditional comments:
<!--[if expression]> HTML <![endif]-->
Pop open the flyout.html page and add the following after the last linked CSS file:
<!--[if IE 7]>
<link rel="stylesheet" type="text/css" href="css/ie7.css" media="screen">
<![endif]-->
Note that there should be no spaces after the initial --, ! and the
last --. If you now view the page in IE7 it will display correctly,
also refreshing the page in the other browsers will reveal that they
are now again displaying the menu correctly. The conditional comment
here is pretty self explanatory but basically, we here tell IE that if
the browser version currently used is 7 then load and apply the style
sheet, if not, ignore the content inside the comment block.
Another
thing you may have noticed is unlike the other browsers, when you hover
over the two fly-out links and the contact us links it does not show
the little hand icon one is used to see when hovering over a link but
instead it remains an arrow, it also does not show the title text of
the anchor tag. When you move over the actual text you will see the
expected result. Position relative is throwing us a curve ball again,
let's fix it.
We need to ensure that both child and parent is
set to position relative. Currently our list item is set to relative
but the child lnk elements are not. So going back to vert_menu.css file
and adding the following to the #nav li a:link, #nav li a:visited rule
should solve our problem:
#nav li a:link, #nav li a:visited
{
position:relative;
}
Refresh IE7. Well it fixed our previous problem but introduced a new problem. Our final contact us link now has the large top margin back we fixed just a couple of steps back.

Let's find a way to fix this. We need to take note of the following extract of the CSS 2.1 spec to find a possible clue:
The box's position is calculated according to the normal flow (this is called the position in normal flow). Then the box is offset relative to its normal position. When a box B is relatively positioned, the position of the following box is calculated as though B were not offset.
(CSS2.1 9.3.1).
The
first thing we might think, even without reading the spec is, well,
give the contact us link a class and apply a -1 pixel top margin to it.
In actual fact, instead of creating a new id or class we could just
apply the existing second-level class to it and the problem is fixed.
However, is there a better way? Or is this just one of those IE bugs
that one has to apply a work around for?
One other thing that
makes this rabbit hole go even deeper is if you look at the fly-out
menus, they now have the same problem as the contact us link. What is
worse, if you try to move from one fly-out menu item to the next and
you are not quick enough, the fly-out goes away! Luckily there is a
simple way to fix all of this. Delete everything from your ie7.css file
and simple add the following:
#nav li
{
margin-top:-1px;
}
This takes care of the margins on all of our list items but the navigation problem on the fly-outs is still there. Add the following to the ie7.css file:
#nav li.second-level a, #nav li.second-level li
{
position:relative;
}
Refresh IE7 and everything should be good, sometimes the simplest solutions has the greatest effect.
NOTE: Even this seems to solve everything, there seems to be one new problem rearing it's head and that is that you cannot get to the 'Web Application' fly-out link. I believe this is because we are using the menu in isolation and that when used along with other content on the page this problem will go away. But to make sure, you can simply add the following to #nav, #nav ul rule in the vert_menu.css file
#nav, #nav ul
{
padding-bottom:.6em;
}
Right, so finally we can go to the last browser we need to support, our
old friend IE6, which we will hopefully will be able to put to rest
soon. For this, we are going back to IETester. Once you have opened
IETester go ahead and open two IE6 tabs and copy and paste the location
of the two files into the two seperate tabs. Does not look to bad but,
looks can be deceiving. Where did the drop down and fly out go?
For
IE6 there is not simple CSS solution to fix this problem and we need to
sprinklle on some JavaScript to make IE sneeze out those menus for us.
Now first there was Suckerfish and then came the Son of Suckerfish.
However I love jQuery and was extremely pleased to see the Superfish
jQuery plugin. This is then the one we will use in this article to get
IE6 to play along. Let's get started.
Download Superfish from this location:
http://users.tpg.com.au/j_birch/plugins/superfish/#download
Once downloaded extract the file. Create a new folder in your current
site structure called js and copy the superfish.js file into this
directory. You will also need jQuery so go ahead and copy the jQuery
file into your new js folder.
Now we need to link in our two
JavaScript files but, we will again us conditional comments to only
have these apply to IE6 as none of the browsers or version need this.
Add the following to your dropdown.html file inside the head and after
the styleheets.
<!--[if IE 6]>
<script src="js/jquery-1.2.6.min.js" type="text/javascript"></script>
<script src="js/superfish.js" type="text/javascript"></script>
<![endif]-->
Now go back into IETester and hit the Refresh Complete (no cache) option.

Go ahead and try out the drop down menu in the IE6 tab. Hmmmm, still
not working. No worries, we simply have not called the function and we
need to make a small edit to our CSS files. Let's handle the CSS first.
Open up the dropdown.css file and change the line:
#nav li:hover ul, #nav li:focus ul
to
#nav li:hover ul, #nav li:focus ul, #nav li.sfHover ul, #nav li.sfHover ul
The sfHover, ala Suckerfish, class is the class that will be added to out list items dynamically by JavaScript. All that is left is to call the Superfish function on out list items. Now create a new js file inside the js folder called ie6menufix.js and link it to the page, inside the conditional comments, as follows:
<script src="js/ie6menufix.js" type="text/javascript"></script>
NOTE: You could have added the code we will ad to this file to the head of your HTML document but, I prefer to keep things separated. Add the following to your new js file:
$(document).ready(function() {
$('ul#nav').superfish();
});Go ahead and refresh as before. Eureka, it is working but alas, we now have to deal with IE6's CSS bugs but before we do, lets also get the fly-out's to work. First thing, add the exact same scripts with the conditional comments to the flyout.html file. Next open flyout.css and change the line:
#nav li:hover ul, #nav li:focus ul
to
#nav li:hover ul, #nav li:focus ul, #nav li.sfHover ul, #nav li.sfHover ul
Do a refresh all, no cache as before here and it works! All that is
left is to fix the IE6 specific CSS problems, isn't web development fun!
Let's
tackle the one with the least problems first, that being the fly-out
this time. In your CSS folder create a new file called ie6.css. Then
inside the IE6 conditional comments of flyout.html add the following
before any of the JavaScript includes:
<link rel="stylesheet" type="text/css" href="css/ie6.css" media="screen">
Your conditional comment for dropdown.html should now resemble the following:
<!--[if IE 6]>
<link rel="stylesheet" type="text/css" href="css/ie6.css" media="screen">
<script src="js/jquery-1.2.6.min.js" type="text/javascript"></script>
<script src="js/superfish.js" type="text/javascript"></script>
<script src="js/ie6menufix.js" type="text/javascript"></script>
<![endif]-->
Now we need to add the CSS to this new file. Add the following to your ie6.css file:
#nav li
{
margin-top:-1px;
}
Problem solved! Now onto our dropdown which up to now has presented us
with the least amount of problems but now seems to pose the biggest
challenge. Oh yes, you can close the IE6 tab for the flyout now.
Adding the following to your ie6.css file solves some of our problem:
#nav li ul li
{
float:none;
border-top:1px solid #fff;
height:1%;
}
But, introduces something new:

Ok, so the space between the list items is not that extreme anymore but, the drop-down elements no stretch all the to right of the browser window. Now what we want. The simplest way to fix this is to give our list items an explicit width, so add the following to your ie6.css file:
#nav li ul li
{
float:none;
border-top:1px solid #fff;
height:1%;
width:175px;
}
Now, let's take care of the top border that is still to large. Now IE6 has a well known flaw that adds an extra 3 pixels to elements. Hmm, ok so that means adding the following should fix our menu:
#nav li ul li
{
float:none;
margin-top:-3px;
border-top:1px solid #fff;
height:1%;
width:175px;
}
And ideed it does! Now we have a two level drop down and flyout menu that will work in IE 6+, Opera 9.5+, Firefox 2+ as well as Safari 3+ with just a little help from jQuery and the Superfish plugin.
Answering User Questions
Following my part one entry of this series I received some questions
from a user called Judy. I promissed her that I will answer her
questions in this article and for the most part the above should answer
them but, one other question she had involves a little more.
She asked:
"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?"
Now these are the srop down menus one sees that instead of dropping
down in a veritcal fashion as ours have here, drop down and lines up in
a horizontal fashion. Let's see how we can change the current drop down
menu to enable this.
I first made a copy of the current dropdown.html file calling it
dropdown_alt.html and also saved a copy of the dropdown.css as
dropdown_alt.css. To dropdown_alt.css I changed the #nav li ul li rule
as follows:
#nav li ul li
{
float:none;
float:left;
border-top:1px solid #fff;
}
Loading the new HTML file into Firefox creates the effect she was after. It also works in Opera 9.5+, Safari 3+ as well as IE 6+. I hope this answers all of your questions Judy. If however you, or anyone else, have any additional question or run into any problems please let me know by commenting on this article.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)




