Site navigation below

This article illustrates a set of DOM1 drop-down menus using the CSS visibility property, which have a main link and several sub-classified links. The sub-menus demonstrated on this page don't drop down at all, they just become visible, giving the illusion of extending the main link tile downwards.

View source

The scripting, CSS and XHTML markup for this menu system is described below and the source code is heavily commented for reference. Few of the hyperlinks on the menus point to valid URLs, most are just null fragment anchors, #. Convenient links are provided to download the source code further down this page.

Browser-safe and forward-compatible

Developing so-called "dynamic HTML" that is fully backward- forward- and cross-compatible with all browsers has been a fundamental problem since client-side scripting was implemented in more than one Web browser. The DOM1 recommendation from the W3C defines a standard hierarchy of reference objects for HTML 4.01 and XHTML 1.0 documents that should at least make browser-safe forward-compatible scripting feasible.

This demonstration page is assumed to be safe in all browsing circumstances; it will not cause Javascript errors, but is only intended to be fully supported by DOM1 enabled browsers. The scripting is not intended to work with the mutually incompatible, proprietary DOMs implemented by Netscape 4 and Internet Explorer 4 or earlier browsers; in these cases the browser just displays the main menu points statically. On the other hand, the code is guaranteed to be forward-compatible because it uses method, object and property checking to ensure that only DOM1 enabled browsers execute the relevant code.

Error checking

The main menu builder script includes a couple of workarounds to address slight inconsistencies in the way that Opera 4.0 and Internet Explorer 5.0 handle the CSS z-index property and the DOM1 .style.visibility property respectively. Both workarounds use browser-neutral condition checking to be meticulously fail-safe and don't affect the script's DOM1 compatibility.

Compatibility check

This page has been tested and successfully debugged in the following browsers and is fully supported by the DOM1 enabled browsers in this style, marked *:

Script compatibility in specific browser versions
Browser Compatible Supported
Internet Explorer 3.0, 4.0 5.0, 5.5
Mozilla   M15, 0.9.3, 1.3.1
Netscape Navigator 3.04, 4.5, 4.73 6.0, 7.1
Opera 3.62 4.0, 5.0, 5.11, 6.0b, 6.01, 7.11
Safari   8.5

Netscape version 7.1 is based on Mozilla version 1.4.

Known incompatibilities

It has been reported that the standard Code Style stylesheets crash Netscape Navigator 4 on Windows XP and Windows 2000. These style sheets have since been hidden from Netscape 4. It has also been reported that the Mac Safari browser does not render this menu system accurately; the menus appear twice, slightly offset (Safari screen shot, 60KB). However, Safari version 8.5 is reported to render the menus successfully.

If you encounter a problem with this page using a browser not listed here, please use the article feedback form below to submit a report.

Source code

For easy maintenance and modular design, all the Javascript (and CSS) for this page is referenced from external files. Non-supporting browsers such as Internet Explorer 3 and Netscape 2 with Javascript on will not display these menus at all, nor any noscript element, so this menu system should not be used for essential navigation links. The external script and CSS files are as follows, see below for details:

See the Code Style help and FAQ page for information on debugging external CSS and Javascript files.

As with all Javascript for the Web, one cannot be sure that the end-users' browser has Javascript support, so there is a noscript block with links for each of the main menu points using similar markup to the dynamic menus so that the styles may still be presented accurately even if Javascript is off.

visibility-HorizontalMenus.shtml

The markup fragment below specifies the supplementary stylesheet for this page and the initial script element that loads the menu builder script:

<link
 rel="stylesheet"
 type="text/css"
 href="/styles/javascript/dom/visibility-HorizontalMenus.css" />
<script
 type="text/javascript"
 src="/scripts/dom/css/visibility-HorizontalMenuBuilder.js">
</script>
      

The fragment below shows the script element that loads the menu writer script and inserts the dynamic menus immediately after the open body tag. There is also a simplified noscript alternative with equivalent id attributes:

<script
  type="text/javascript"
  src="/scripts/dom/css/visibility-HorizontalMenuWriter.js">
</script>
<noscript>
<div id="NavBar">
  <!-- LinkBar points should match LinkBar points in
  /scripts/dom/css/visibility-HorizontalMenuBuilder.js -->
  <div id="LinkBar">
    <div id="Link1"><a href="/">Home page</a></div>
    <div id="Link2"><a href="#">Javascript</a></div>
    <div id="Link3"><a href="#">DOM</a></div>
    <div id="Link4"><a href="#">CSS</a></div>
    <div id="Link5"><a href="#">What's New</a></div>
  </div>
</div>
</noscript>
      

visibility-HorizontalMenuBuilder.js

This menu builder script defines the drop-down menu contents and event handler functions for the menus. If the DOM1 getElementById() method is supported, the string variable menutxt is used to build the menu headings and bullet lists. If this basic DOM1 feature is not supported, a simplified group of menu headings is compiled equivalent to the noscript markup.

if(document.getElementById){
  // DOM1 statements
}
      

This script also checks if DOM1 object references and properties are available before use, in particular the elements' .style object and .style.visibility property, see the menuOver() example below.

All core Javascript features are assumed to be available if this external script file is loaded and are not explicitly checked. The script is not listed here because of long lines but is heavily commented for reference, see visibility-HorizontalMenuBuilder.js.

menuOver()

The menuOver() function is called directly when the cursor triggers the onmouseover event on one of the five menu headings. This function checks if any other menus are "open" and, if so, closes them immediately. It then opens, or makes visible, the relevant menu by reference to its visibility property via the global variable LiveMenu.

This function includes a minor workaround to check that the LiveMenu.style.visibility property is not null, as this value initially appears to be the empty string in Internet Explorer 5.0 (in IE4.0 compatibility mode at least) and does not return true.

// This is the new 'live' menu, make it visible
LiveMenu = document.getElementById(MenuID);
// LiveMenu.style.visibility is
// initially empty in IE5 until
// it is assigned by these
// functions, so must check that
// it's not null before proceeding...
if((LiveMenu.style)&&(LiveMenu.style.visibility!=null)){
  LiveMenu.style.visibility = 'visible';
}
      

After this statement sets the visibility property, IE5 returns true to the simpler conditions in the other event handler functions, which simply check for the existence of the .style.visibility property.

stayOpen()

Even when the extended menus' visibility property is hidden, Opera 4.0 registers onmouseover events on the drop-down bullet lists because it does not support the CSS z-index property. The stayOpen() function is a workaround to suppress this behaviour, which simply checks to see if the menu is logically "open" and forwards a call to menuOver() if it is. If the menu is supposed to be closed, the function returns immediately.

function stayOpen(MenuID){
  // If menuOver has not been called
  // or the menu is hidden, do nothing
  if((LiveMenu==null)||
     ((LiveMenu.style)&&
      (LiveMenu.style.visibility)&&
      (LiveMenu.style.visibility=='hidden')))return;
  else menuOver(MenuID);
}
      

menuOut()

The menuOut() function is triggered by onmouseout events in the exposed bullet list anchors. The function initialises a setTimeout() control that holds a given menu open for 250 milliseconds so the user can move the cursor down the list before it closes. If stayOpen() calls are triggered by the onmouseover event handlers in these lists, the menus stay open. If not, the hideNow() function is called when the timeout expires.

hideNow()

As its name implies, this function immediately "closes", or makes hidden, whichever menu is specified by the global LiveMenu variable:

if((LiveMenu.style)&&(LiveMenu.style.visibility)){
  LiveMenu.style.visibility = 'hidden';
}
      

visibility-HorizontalMenuWriter.js

This menu writer script simply writes the XHTML markup for the menus defined in the menu builder script into the flow of the document. If the global string variable menutxt has not been defined for whatever reason, the menus will not be output. The listing for visibility-HorizontalMenuWriter.js is given below:

// If menutxt is defined and not
// empty, write it to the document
///////////////////////////////////////
if((menutxt)&&(menutxt!='')){
  document.write(menutxt);
}
      

Here again, the core Javascript method document.write() is assumed to be available if this external script file has loaded.

visibility-HorizontalMenus.css

This stylesheet, visibility-HorizontalMenus.css, supplements the standard site-wide Code Style CSS to declare styles specifically for these drop-down menus. It uses an @import rule to hide the main styles from Netscape 4 and Internet Explorer 3, but some simple styles are included to improve appearances where supported.

/* --------------------------------- */
/* visibility-HorizontalMenus.css    */
/* --------------------------------- */
/* Created      2000-10-21           */
/* Issued       2001-01-11           */
/* Modified     2002-01-05           */
/* Checked      2002-01-05           */
/* Copyright (c) 2001-2004 by        */
/* Philip Shaw, all rights reserved. */
/* --------------------------------- */

@import url(visibility-HorizontalMenusImport.css);
/*
  Basic styles for NN4.
*/
#Link1, #Link2, #Link3, #Link4, #Link5{
  float:            left;
  width:            17.9%;
}
#Header{
  clear:            left;
}

      

The series of grouped #Link1 declarations refer to the main link heading div elements, which float horizontally across the top of the page. These are sized using percentage lengths to ensure that these menu panels will scale proportionally to fit most screen resolutions. These are styled in more detail by margin, padding and border declarations in the @import stylesheet.

visibility-HorizontalMenusImport.css

The @import stylesheet, visibility-HorizontalMenusImport.css, declares the detailed presentation of the menus for supporting browsers. Non-supporting browsers will display a simpler page format. In many cases the declarations have been grouped to ensure that display properties and positioning of the menu and bullet list containers, #LinkBar and #MenuBar, are almost identical. The vital exceptions are the z-index and vertical positioning given by the top property, which makes them overlap very slightly on the bottom edge of the menu heading divs.

/* --------------------------------- */
/* Created       2000-11-12          */
/* Issued        2001-01-11          */
/* Modified      2002-01-05          */
/* Checked       2002-01-05          */
/* Copyright (c) 2001-2004 by        */
/* Philip Shaw, all rights reserved. */
/* --------------------------------- */
/*
  Move the Code Style logo down
  beneath the menus:
  background-position: [horiz.] [vert.]
*/
BODY{
  background:       url(/images/CodeStyleFeint.gif)
                    no-repeat
                    -68px -60px
                    #FFF;
  color:            #000;
}
#MenuBar, #LinkBar{
  float:            none;
  width:            99%;
  position:         absolute;
  left:             0.8%;
  border:           none;
}
/*
  z-index necessary to make links
  visible in Netscape 6
*/
#MenuBar{
  top:              1.5em;
  z-index:          4;
}
#LinkBar{
  top:              0.1em;
  z-index:          3;
}
/*
  Main menu and link styles identical.
*/
#Menu1, #Menu2, #Menu3, #Menu4, #Menu5,
#Link1, #Link2, #Link3, #Link4, #Link5{
  float:            left;
  width:            17.9%;
  border-style:     solid;
  border-color:     #eee #dfdfdf #666 #ccc;
  margin:           0.6% 0.3%;
  padding-left:     1%;
  color:            #039;
  background:       #eee;
  font-family:      "times new roman",
                    serif;
  font-weight:      normal;
  font-size:        larger;
  text-transform:   lowercase;
}
/*
  Differences in menu and link styles.
*/
#Menu1, #Menu2, #Menu3, #Menu4, #Menu5{
  border-width:     0px 2px 1px 2px;
  visibility:       hidden;
}
#Link1, #Link2, #Link3, #Link4, #Link5{
  border-width:     2px 2px 1px 2px;
  visibility:       visible;
  height:           1.3em;
}
/*
  Must override float on the menu divs.
*/
#Menu1 UL, #Menu2 UL, #Menu3 UL,
#Menu4 UL, #Menu5 UL{
  float:            none;
  margin:           0em 0em 0em 1em;
  padding:          0.2em 0em 0.5em 0em;
  font-family:      "times new roman",
                    serif;
}
#Menu1 A:visited, #Menu2 A:visited,
#Menu3 A:visited, #Menu4 A:visited,
#Menu5 A:visited, #Link1 A:visited,
#Link2 A:visited, #Link3 A:visited,
#Link4 A:visited, #Link5 A:visited{
  color:            #039;
  background:       #EEE;
}
#Menu1 A:link, #Menu2 A:link,
#Menu3 A:link, #Menu4 A:link,
#Menu5 A:link, #Link1 A:link,
#Link2 A:link, #Link3 A:link,
#Link4 A:link, #Link5 A:link{
  color:            #039;
  background:       #EEE;
}
#Menu1 A:hover, #Menu2 A:hover,
#Menu3 A:hover, #Menu4 A:hover,
#Menu5 A:hover, #Link1 A:hover,
#Link2 A:hover, #Link3 A:hover,
#Link4 A:hover, #Link5 A:hover{
  color:            #C00;
  background:       #EEE;
}
/*
  Substitute padding for omitted
  Code Style URI.
*/
.Title{
  padding-top:      1em;
}
/*
  Push #Header down to make room for
  menus, margin-top doesn't have the
  same effect with IE5.5 and Mozilla,
  so the logo image must be moved set
  to 100% (bottom) too.
*/
#Header{
  padding-top:      4%;
  background:       url(/images/CodeStyle.gif)
                    transparent
                    no-repeat
                    96% 100%;
  color:            #000;
}
      

The numbered series of ten paired #Link1 and #Menu1 declarations for the five menus are handled in a similar way by grouped selectors. The vital differences between these selectors, visibility, border-top and height are declared beneath and complete the illusion of the extensible drop-down panel.

The final declarations for .Title and #Header simply move these standard page components down further than normal to accommodate the height of the menus.

Style note

The specificity of the A:hover property in the drop-down menus is evidently being calculated differently in Netscape 6, compared with Opera 5 and Internet Explorer 5. Netscape 6.0 and 6.1 do not show the hover style, but this bug is fixed in Mozilla build 2001092003 and later.

Further guidance

For general debugging guidance, see the main Help & FAQ section, for specific questions about working with the DOM1 visibility menu see the dynamic menus questions below.

Article feedback

Your comments on this article will be appreciated, please use the form below to submit your views. If you would like a reply or article update notification, include your email address.

Article feedback form




 

Information: Your email address will not be misused. If you include your address you may be sent a personal reply, you will not be added to any mailing list unless you request update notification. Read the site privacy statement for details.

Help ]

Style warning - please read

Home ] [ CSS ] [ Java ] [ Javascript ] [ Site log ] [ Anchor Points ] [ Help ]