Rollover menus

The idea

CSS rollover menus are pretty easy to implement and can look really nice. But using simple CSS a:hover selectors makes them a little "clunky" - there's no smooth transition between menu and no menu. Which is why, when I saw bytefx linked from the CSS Beauty website, I thought "that would make nice menus". And with a little playing, it did.

Building the navigation

The navigation itself is pretty simple HTML, using a div for each section, which contains a list of the sub-pages. Semanticaly, it might have been better to use a series of nested lists; instead, I've set each section header as an h2 element. The code for the navigation is as follows (truncated a bit... view the source to see it in full), and sits in its own PHP include file:

<div id="nav">
	<div class="menu" name="hover" onMouseOver="bigger(this, 100);" onMouseOut="smaller(this);">	
	 <h2><a href="../aboutJF/index.php" title="About JuicyFly - hover for more">About JuicyFly</a></h2>	
	  <ul>
<li><a href="../aboutJF/concept.php">&#187; the concept</a></li>
<li><a href="../aboutJF/tech.php">&#187; the technology</a></li>
<li><a href="../aboutJF/sitemap.php">&#187; sitemap</a></li>
<li><a href="../contact/index.php">&#187; contact</a></li>
</ul>
</div>
... </div>

The CSS for the navigation is as follows:

#nav 	{z-index: 1; 
		position: relative; 
		margin: 0 auto 0 auto; 
		top: 260px; 
		padding: 0;
		width:650px; 
		border-top: 1px solid #cccccc;}
		
.menu  	{z-index: 100; 
		background-color: #ffffff; 
		border-bottom: 1px solid #cccccc;
		margin: 0; 
		padding: 0;
		float: left;
		width: 130px;
		color: #ff0000; 
		font-family: Arial, Helvetica, sans-serif; 
		font-size: 0.8em; 
		font-weight:bold; 
		height: 16px; 
		overflow:hidden;}

.menu a, .menu h2 a {color: #000099; 
		text-decoration:none; 
		font-weight: bolder;}

.menu h2 {font-size: 1em;}

.menu a:hover, .menu h2 a:hover {color: #ff0000; 
		text-decoration:underline;}

.menu ul, .menu li, .menu li a {list-style:none; 
		margin: 0; 
		padding: 0; 
		color: #000099; 
		background: #fff; 
		line-height:150%;}

.menu ul {padding-top: 5px;}

/* Included  in IE only stylesheet */

#nav {filter:alpha(opacity=90);}

As you can see, IE requires the filter:alpha(opacity=n); code in order to handle the fade effect.

The JavaScript itself relies on the bytefx js library; you can download it and see the technical details on the site at http://www.devpro.it/bytefx. Two functions are set in the head of the page: bigger and smaller. I know, not very original. For some reason it gives me pleasure to name functions in as silly a way as possible, which is bound to cause me problems at some point... anyway, the functions are called on mouse over and mouse out, as you can see from the navigation HTML. Here are the functions themselves:

function bigger (currentDiv, divHeight)
{
bytefx.fade(currentDiv, 100, 80, 20, function(){bytefx.size(currentDiv, {width:130,height:divHeight,$height:1}, 20);});
clearInterval(currentDiv.bytefx["size"]);
}
function smaller (currentDiv) 
{
bytefx.size(currentDiv, {width:130, height:16}, 20, function () {bytefx.fade(currentDiv, 80, 100, 20);});
}

The bytefx functions are set up so that the last variable of each is another function to be called when the transition - be it size, alpha, or whichever - is complete.

The function bigger first fades the current div from an opacity of 100% to 80% at a speed of 20; then it calls the size function, increasing the height of the div to the value called by the function and set in the link as the second variable. There's a little adjustment for differences in IE and FireFox handling of sizes (whether borders are included in a div's size or not) and then - finally - the re-size function is cleared. This stops the function being re-called, which would make the menu flicker when moving the mouse over the sub-links - the bubbling issue. Thankfully, that's nicely explained here - http://www.quirksmode.org/js/events_order.html - although it took me a lot of swearing to finally fix.

The heights of the elements are found with a little trial and error; they, of course, depend on the number of navigation items, if any feed onto a second line, as well as the line height of the element.

The function smaller handles the changes in reverse order: first re-setting the height to 16px, the height of the closed navigation bar, then fading back to 100% opacity. Because it's done on the onMouseOut action, there's no need to clear it - as it's leaving the div, it can't bubble.

Remaining issues

It's still far from perfect, however. It's certainly not accessible; if JavaScript is disabled, there's no deep navigation on the site at the moment. I'm going to fix that with either a set of accessible links at the bottom of the page or - and this would be the ideal solution - detect JavaScript using PHP and include the appropriate navigation file. It's just a matter of building a navigation without Javascript which doesn't take up much room - but still contains all the information. CSS may well be the solution to this, because using lists as I have done means that the navigation remains pretty sensible if the page is displayed as text only.

Another "nice to have" would be automatic detection of the menu size, so that I don't have to go through the trial and error part of the setup. Still, it's pretty easy to guess, and the Firefox developer's toolbar has a handy ruler you can use to measure things too.

It's also a bit... "quirky". Mouse away too quickly, and the menu stays open. It's not the end of the world, but it would be nice if it didn't do that. When I actually understand JavaScript event handlers I might work out why. In the mean time, please let me know if you have any ideas...