Predefining Menus

Flyout Menus are normally created using the makeLayer () function. If you wish to use menus which you've already defined, that can be done, too. Note this functionality will not work with Netscape 4, Internet Explorer 4, or Opera 5, since they cannot maniuplate the document as newer browsers can.

If you wish to define your own flyouts rather than having the list of links used by default, there are a few important notes:

Free-form Flyouts

A simple use of useLayer () is to have text instead of a list, such as the snippet above. The styles we will be using are:

ul.firsttest {
    font-family: Arial, Verdana, sans-serif;
    font-size: 0.8em;
}
ul.firsttest a {
    text-decoration: none;
}
div.flyout.firsttest {
    width: 15em;
    color: black;
    background: white;
    border: 2px solid blue;
    padding: 0.5ex 1ex;
}
a img {
	border: 0;
}

And the menus show up as:

A simplified version of this example is available.

Notice that the text for the links and the flyout is the same size, even though the HTML lists the flyout as declaring firsttest twice, which would normally mean that the font size should be 0.8em x 0.8em = 0.64em; since the flyout is removed from the normal HTML stream, it does not inherit the outer firsttest class.

Also notice that the links in the flyouts are underlined, further showing that they do not inherit the text-decoration: none attribute. If your browser is set to never show underlines, then you will not see behavior.

This example only shows simple text in the flyout, but you can include images as well as complex HTML markup to make the menu appear quite sophisitcated.

For the purposes of this page, a small JavaScript block is inserted at the end of each section to force useLayer () to be called for each menu. This ensures that the defaults for each set of menus is used, and defaults set later in the page do not apply.

Menus as Lists

Cascading Style Sheets can help you create very complex menus with relatively simple HTML markup. For example, coupling this HTML:

<ul class="secondtest">
<li><a href="position.html" onmouseover="mIn ('first2')"
	onmouseout="mOut ('first2')">Complex Positioning
	<img id="first2" src="/home/graphics/mo/noarrow.gif"
	    width="6" height="11" alt="" /></a>
    <ul id="l_first2" class="flyout secondtest">
    <li>Realtive to any image</li>
    <li>Against any side</li>
    <li>Prioritize rules</li>
    </ul></li>
<li><a href="uselayer.html" onmouseover="mIn ('second2')"
	onmouseout="mOut ('second2')">Predefining Menus
	<img id="second2" src="/home/graphics/mo/noarrow.gif"
	    width="6" height="11" alt="" /></a>
    <ul id="l_second2" class="flyout secondtest">
    <li>Can have any look</li>
    <li>Automatically initialized</li>
    <li>CSS can make content accessible</li>
    </ul></li>
</ul>

with this style sheet:

ul.secondtest {
    font-family: Arial, Verdana, sans-serif;
    font-size: 0.9em;
}
ul.secondtest a {
    text-decoration: none;
}
ul.secondtest.flyout {
    width: 15em;
    color: black;
    background: white;
    margin: 0;
    border: 2px blue solid;
    padding: 0;
    list-style-type: none;
}
ul.flyout li {
    margin: 0;
    padding: 0.25em 0.5em;
}
ul.flyout li.secondtesttitle {
    background: blue;
    text-align: center;
}
ul.flyout li.secondtesttitle a {
    color: white;
    font-weight: bold;
}
a img {
	border: 0;
}

and referencing this bit of Javascript in the head of the document:

var newDefs = new Object;
newDefs.preDetach = function (lyr) {
    if (lyr.parentNode.parentNode.className != 'secondtest')
	return;		// Only use for second set of menus
    var li = document.createElement ('li');	// Will be title
    li.className = 'secondtesttitle';		// Set style class
    var node = lyr.parentNode.firstChild.cloneNode (true);
    node.removeChild (node.lastChild);		// Copy link, remove IMG
    li.appendChild (node);			// Add to LI
    lyr.insertBefore (li, lyr.firstChild);	// First item in flyout
};
flyDefs (newDefs);

creates the following menus:

A simplified version of this example is available.

There are some important things to notice for this example. First, nested unordered lists can be used to make flyouts work on CSS-capable browsers, but browsers which don't understand CSS will still show the content. Using lynx, the above menus appear as:

* Complex Positioning 
     + Realtive to any image
     + Against any side
     + Prioritize rules
* Predefining Menus 
     + Can have any look
     + Automatically initialized
     + CSS can make content accessible

For browsers which understand some of CSS but not all, you need to hide the style sheet. There are many different ways to do this, but the probably easiest is to refer to the style sheet with an @import statement.

Another thing to notice with this example is that each menu has a title, even though we don't define that in the HTML. The Javascript we use is called the first time before a menu is displayed and takes the title from the menu's parent, inserting it at the front of the unordered list. The script also checks to make sure this is the second example, so the titles don't also show up on the first example.

CSS Showing and Hiding

You can choose to use CSS styles to show and hide elements. For example, the HTML:

<ul class="thirdtest" onmouseover="thirdover (event)"
	onmouseout="thirdout (event)">
<li><a href="position.html">Complex Positioning</a>
    <ul class="flyout thirdtest">
	<li>Realtive to any image</li>
	<li>Against any side</li>
	<li>Prioritize rules</li>
    </ul></li>
<li><a href="uselayer.html">Predefining Menus</a>
    <ul class="flyout thirdtest">
	<li>Can have any look</li>
	<li>Automatically initialized</li>
	<li>CSS can make content accessible</li>
    </ul></li>
</ul>

with this style sheet:

ul.thirdtest {
    width: 12em;
    font-family: Arial, Verdana, sans-serif;
    font-size: 0.9em;
}
ul.thirdtest li {
    position: relative;
    border: 0 white solid;
    border-width: 1px 0;
}
ul.thirdtest a {
    display: block;
    color: blue;
    background: white;
    text-decoration: none;
}
ul.thirdtest a.showing {
    color: white;
    background: blue;
}
ul.thirdtest.flyout {
    width: 15em;
    color: black;
    background: white;
    margin: 0;
    border: 2px blue solid;
    padding: 0;
    list-style-type: none;
}
ul.thirdtest.flyout li {
    margin: 0;
    padding: 0.25em 0.5em;
}
ul.thirdtest ul.flyout {
    width: auto;
    margin-left: 1em;
    border: none;
    list-style-type: circle;
    font-size: 1em;
}
ul.thirdtest ul.flyout li {
    padding: 0;
}

and referencing this Javascript in the head of the document:

var newDefs = new Object;
newDefs.position = 'IMG|t=t;IMG-r=l';
newDefs.showImage = function (obj, lyr) {
    obj.className = 'showing';
};
newDefs.hideImage = function (obj, lyr) {
    obj.className = '';
};
flyDefs (newDefs);

document.thirdflyIDnum = 0;

function thirdobj (e) {
    if (! document.getElementById)
    	return null;
    e = e || event;
    var o = e.target || e.srcElement;
    while (o && o.tagName != 'A' && o.id != 'mainlinks')
	o = o.parentNode;
    return o && o.tagName == 'A' && o;
}

function thirdover (e) {
    var o = thirdobj (e);
    if (! o)
    	return;
    if (! o.id) {
	o.id = 'thirdfly' + document.thirdflyIDnum++;
	var f = o.nextSibling.nextSibling;
	if (! f || f.tagName != 'UL')
	    return;
	f.id = 'l_' + o.id;
    }
    mIn (o.id);
}

function thirdout (e) {
    var o = thirdobj (e);
    if (! o)
	return;
    mOut (o.id);
}

creates the following menus:

A simplified version of this example is available. This example does not function with Opera 6, since it does not have the necessary object attributes needed for the JavaScript to be able to traverse the HTML object hierarchy. There are several subtleties in this code.

No Trigger Image

Since no trigger image is used, the name associated with that image trigger is moved to the link itself. It's still possible to use an image by using CSS to declare the image as part of the background. You can make the image change by making a new background declaration in the "active" style. You must declare the complete set of background properties, otherwise Internet Explorer for Windows will not correctly apply the styles.

No onMouseOver, onMouseOut, ID Attributes

Instead of assigning event actions for each link (onmosueover, onmouseout) one set of onmouseover and onmouseout events is assigned for the containing unordered list. These event handlers are called every time the mouse enters part of the unordered list (including elements within it) and the handler only responds for the appropriate links.

Since we know the link is our trigger, we can also make the event handler assign an identifier for the link (the flyout will be positioned relative to the link) and the associated flyout menu.

Style Sheet

The style sheet is where most of the subtleties exist.

UL Width

An explicit width is declared for the unordered list which contains the list of links. This is so the flyouts don't end up completely on the right hand side of the window.

LI Declarations

The list items for the links (not the flyout items) have a one-pixel border at the top and bottom, with a color which matches the background. Without this border, Internet Explorer for Windows makes each item too large (as if the line-height were almost 2em).

The items are also declared to have relative positioning. This has no effect layout-wise, but Internet Explorer for Windows sometimes has problems positioning flyouts since it can't properly determine the location of the triggering item (in our example, the links themselves). This problem only appears when a normally-inline object (such as a link) is defined to be a block element, and the enclosing item is not a position container (setting the position to relative makes the list item a position container).

A and A.showing Declarations

These are the two states for the link used by the flyout. The links are set to block display, which is needed by Konqueror (but not Safari, which is based on the same engine).

UL.thirdtest UL.flyout Declarations

If JavaScript is not enabled, the menus will be visible to the user since JavaScript is required to define the flyout class as not being displayed (image on right). These two declarations override our other ones and make the flyout content appear as a list again.

These declarations take precedence over the previous ones because they are more specific with what objects they will match. They don't impact use with flyouts since the flyout objects are removed from the normal HTML flow when the flyouts are active, and therefore are not descendants of UL.thirdtest.

JavaScript

A position definition is used to make sure the flyout gets positioned right next to the link item, which makes the border blend with the link's background.

The showImage and hideImage functions change the class of the trigger object (the links in our example) when the flyout menu appears and disappears.

The event handlers for the unordered list receives the onmouseover and onmouse events, and makes sure that one of our links was the target before continuing. If this is the first time a particular flyout menu will be used, the event onmouseover handler will assign a unique identifier to both the link and the flyout menu.