Positioning with CSS

As of version 2.0, the UW Flyout Menus support CSS positioning. This allows you to control exactly where the menus appear; with normal positioning, the menu will still be adjusted to appear on the page. Of course, this means that your user may need to scroll to see your menus, so take care with how you define the menus.

Using CSS positioning is much faster, since the flyout code does not need to compute the location of the trigger image, nor does it need to position the menu. Of course, it's possible to replicate the functionality of flyout menus using the :hover pseudo-class, but unless you're very careful with how you set up the menus, they may be very difficult to use. Unlike CSS menus, flyout menus have a delay before showing and hiding menus, which make them easier to navigate.

This example shows how to position with only CSS. It does not use arrow images, even though that can be done. Instead, it uses CSS to change background colors of selected items.

As can be seen, you must make sure that there is good feedback as to which item is selected. This example uses background color to match selected item with the flyout menu. It's probably not enough color difference, but the colors can be changed by just changing the CSS.

A simplified example is available, which does not include the description below, making it easier to see what's happening.

HTML

The HTML for this example seems very complicated, but it's just an extension of nested lists, although there are a couple Internet Explorer workarounds which were added. Other than those, only id, class, onmouseover, and onmouseout attributes were added.

<ul id="nomenus">
    <li id="gr"><a href="#" onmouseover="mIn ('gr')"
		onmouseout="mOut ('gr')">Groceries </a>
	<div><ul class="flyout" id="l_gr">
	    <li id="gr_pr"><a href="#" onmouseover="mIn ('gr_pr')"
			onmouseout="mOut ('gr_pr')">Produce </a>
		<div><ul class="flyout" id="l_gr_pr">
		    <li><a href="#">Apples </a></li>
		    <li><a href="#">Lettuce </a></li>
		</ul></div>
	    </li>
	    <li id="gr_me"><a href="#" onmouseover="mIn ('gr_me')"
			onmouseout="mOut ('gr_me')">Meat </a>
		<div><ul class="flyout" id="l_gr_me">
		    <li><a href="#">Chicken </a></li>
		    <li><a href="#">Pork Chops </a></li>
		</ul></div>
	    </li>
	</ul></div>
    </li>
    <li id="of"><a href="#" onmouseover="mIn ('of')"
		onmouseout="mOut ('of')">Office Supplies </a>
	<div><ul class="flyout" id="l_of">
	    <li id="of_pa"><a href="#" onmouseover="mIn ('of_pa')"
			onmouseout="mOut ('of_pa')">Paper Products </a>
		<div><ul class="flyout" id="l_of_pa">
		    <li><a href="#">Envelopes </a></li>
		    <li><a href="#">Stationery </a></li>
		</ul></div>
	    </li>
	    <li id="of_fa"><a href="#" onmouseover="mIn ('of_fa')"
			onmouseout="mOut ('of_fa')">Fasteners </a>
		<div><ul class="flyout" id="l_of_fa">
		    <li><a href="#">Paper Clips </a></li>
		    <li><a href="#">Staples </a></li>
		</ul></div>
	    </li>
	</ul></div>
    </li>
</ul>

The id tags identify both the menus and the <li> items which enclose them, which we tag to make the Javascript easier to implement. We are not using arrow images, but there must be an object with that identifier, or the menus won't appear.

The first IE workaround is to add spaces before closing the links. Otherwise, the innermost set of menus would be too tall, as described in the List of Links snippet. The second workaround is to wrap the flyout unordered lists with <div>s, which prevents the other menus from being too tall. What the added <div> does is prevent IE from trying to render the sublists as inline items, and therefore rendering the spaces after the links (even though the menus are absolutely positioned).

CSS

While the HTML is pretty straightforward, the CSS is less so. It is, however, not as complicated if taken a section at a time. The first thing you'll notice is the outermost <ul> has an id of nomenus, but the CSS rules assume menus. We change the id with Javascript, when we're sure that the flyouts are being used.

ul#menus {
    position: relative;
    font-size: 0.8em;
}

This rule is for the outermost unordered list. We need to make it a position container so that we can place the menus relative to it. We also reduce the font size a bit to make it more likely that the menus will fit on the page.

ul#menus ul {
    position: absolute;
    left: 100%;
    top: 0px;
}

For any submenus, we will want to define their position to be just to the right of the current menu, with the same top position. This works because we have no horizontal padding or border (defined next), so we know the left edge of the submenu will align with the right edge of the current menu. The way the rule is defined, this is true for nested submenus, also.

ul#menus, ul#menus ul {
    width: 10em;
    list-style-type: none;
    margin: 0
    padding: 0;
}

Each menu has a width of 10em. We need to define a width so they don't take up the whole width of the page. The list's bullets are disabled, and all margins and padding removed.

ul#menus li {
    color: white;
    background: #66f;
    margin-bottom: 0.5em;
}

For each menu list item (both the main ones and ones for the flyout menus) we set the text color and background. The color will be overridden with the link color we define later, but it's good practice to always have a color set. We define a bottom margin to give some space between the items.

ul#menus li li {
    background: #33f;
}
ul#menus li li li {
    background: #00f;
}

For each subsequent menu, the color gets darker.

ul#menus a {
    display: block;
    color: white;
    padding: 0.1em 0.5em;
    text-decoration: none;
}

Links are displayed as block entities instead of inline, so they occupy the full width of the <li> element, which makes them easier to use. We set the color to white, to override any link colors the browser may have defined. We add padding to give some room around the link, and remove the link's underline.

ul#menus li.over {
    background: #33f;
}
ul#menus li.over li.over {
    background: #00f;
}

When an item is selected, we define a small bit of Javascript to change the class of the selected item. When that happens, these rules override the backgrounds we defined above.

Javascript

There is very little Javascript in this example; only a few defaults are overridden. The way these are defined, however, is different than in the other examples. The two methods can be interchanged.

if (typeof FlyLyr != 'undefined' && FlyLyr.on) {
    flyDefs ( {
	position: "CSS",
	showImage: function (img, lyr) {
	    img.className = 'over';
	},
	hideImage: function (img, lyr) {
	    img.className = '';
	}
    } );
    window.onload = function () {
	var ul = document.getElementById ('nomenus');
	if (ul)
	    ul.id = 'menus';
    }
}

The first thing we need to do is test whether Flyout Menus are enabled. If so, we change some of the defaults.

Instead of creating an object to pass to the flyDefs() function, we create an anonymous object inline, which sets three properties. The most important one in this example is the position property. By setting the value to CSS, we tell Flyout Menus that we will be overriding its default positioning with our own CSS positioning.

The other two items are simple functions which set and clear the class of an <li> item when its menu is showing. In the HTML above we use what would normally be the id of an arrow image and assign it to a list item. It is that item which is pased as the first argument to our functions, so it is easy for us to manipulate the class name.

The last thing we do is set a function to run when the page is loaded. We change the id if the main unordered list from nomenus to menus. We need to do this to make the submenus visible if Javascript or the flyouts are disabled. When using CSS positioning, the flyout code does not remove the menus from the normal flow of the document as they do with menus which aren't positioned with CSS. This example does no styling for the case of flyouts being disabled.