CBE Graphical mouseOver Pulldown Menu 3

Updates

11/18/02: Other CBE Menus...

Menu 1 - Basic
Menu 2
Menu 3
Menu 4 - Floating V
Menu 5
Menu 6
Menu 7
Menu 8
Menu 9 - Cascading
Menu 10
Menu 11 - Floating H

9/25/02: You can download the menu images used in this example.

11/14/01: Upgrade to CBE v4.

7/5/01: NN4.77 seems to completely ignore this padding: 0px 6px;, so I'm trying this text-indent: 6px; instead. The only problem is that now if a menu item wraps, the wrapped line will not indent. BTW, I made a mistake on the last upload of this file - this one has the correction.

7/4/01: There was a problem with this menu on NN4.x, especially NN4.77. When vertical padding was applied to the TD, strange things happened when there were no spaces in the menu items (sounds weird I know). The solution (at least, so far this is a solution) was to apply no vertical padding to the TD, like this: padding: 0px 6px;, and to get the vertical spacing by using this: line-height: 1.3em, even tho NN4.x seems to add more space than the other browsers. If you have any problems with it, please let me know.
I've tested this on Win98 with IE6.0, NN4.75, Mozilla0.9.2, NS6.01, and Opera5.12.
BTW, I also shortened the style class names ;)
I updated the Menu 3 Template as well.

6/19/01: I finally worked on these menus! This one works really well now. I've tested it on Win2K with IE5.00 and on Win98 with IE5.5, NN4.75, NS6.01, and Opera5.01.

The Images

The menu labels are just images in a table. Except for using absolutely positioned labels, this is the best way I've found to insure that the menu boxes are positioned properly under their respective labels. There are two images for each label - one is for the mouseover condition , and the other is for the mouseout condition . They are used for image rollovers.

Each menu box also uses a table. Their are four different types of rows in the table.
(1) The top row of the table is a graphic that is 156x2 pixels in size and is the top border of a standard Windows menu.
(2) The bottom row of the table also is a graphic that is 156x2 pixels in size and is the bottom border of a standard Windows menu.
(3) A row can contain a divider, which is just another 156x2 graphic that has the side borders of a standard Windows menu as well as the divider.
(4) Or a row can contain a menu item, which is text on a background graphic which has the side borders of a standard Windows menu and is 156x1 and tiles to fill the row.

The CSS

(1) The menuBar has width=100%. A table (with width=100%) is placed in this element, it will be as wide as the browser window.
(2) The menuBox has no height specified - it also has a table in it, and you just add more rows to the table to increase the height of the menuBox.
(3) Class mGfx defines the specific width and height of the top and bottom graphical borders, as well as the graphical divider.
(4) Class mItm defines width, height, padding, line-height, and background graphic for each row of the menuBox that is a menu item.

#menuBar {
  position:absolute; visibility:hidden; overflow:hidden;
  height:21px; width:250px; margin:0; padding:0;
  color:#000000; background:#000000;
}
.menuBox {
  position:absolute; visibility:hidden; overflow:hidden;
  width:156px; margin:0; padding:0px;
}
.mGfx {
  background:#c0c0c0; width:156px; height:2px; padding:0; margin:0;
}
.mItm {
  font-family: verdana,arial,sans-serif; font-size: 12px;
  width:144px; margin:0px; padding:0px;
  text-indent:6px; line-height:1.3em;
  color:#000000; background: #c0c0c0 url("../../images/mb_mid_156x1.jpg");
}
      

The Javascript

As you know, the windowOnload() function gets called after CBE is initialized and the cross-browser object model is in-place. Here, it performs the following tasks.
(1) The menuBar object is assigned to a variable, positioned at the top of the window, and made visible.
(2) An array of all menuBox objects is built.
(3) Each menu label has it's rollover images preloaded.
(4) Each menuBox object is positioned under its respective label.
(5) The activeMenu and menuOpen variables are initialized. activeMenu is an integer which indicates the currently active menuBox, and is a subscript into the menuArray. menuOpen is a boolean which indicates whether or not a menuBox is currently visible.

var menuArray, activeMenu=1, menuBar, menuOpen=false;
function windowOnload()
{
  var i, menuCount=3;
  var 
    menuBoxXOffset=0,     // menuBox x offset from menuBar.left()
    menuBoxYOffset=1,     // spacing between the label and it's menuBox
    menuLabelSpacing=2;   // spacing between each label
     
  menuArray = new Array();
  menuBar = document.getElementById('menuBar').cbe;
  menuBar.moveTo('n',75); // placement of the menuBar
  menuBar.show();
  for (i = 1; i <= menuCount; ++i) {
    menuArray[i] = document.getElementById('menu'+i).cbe;
    menuArray[i].moveTo(
      menuBar.left() + menuBoxXOffset + ((i-1)*menuLabelSpacing),      // x
      menuBar.top() + menuBar.height() + menuBoxYOffset                // y
    );
    menuArray[i].lblImg = 'label'+i;
    menuArray[i].lblOut = cbeNewImage("label"+i+"Out", "../../images/label"+i+"_out.jpg");
    menuArray[i].lblOver = cbeNewImage("label"+i+"Over", "../../images/label"+i+"_over.jpg");
    menuBoxXOffset += document.getElementById(menuArray[i].lblImg).width;
  }
  document.cbe.addEventListener("mousemove", menuHide);
}
      

The function menuShow() is called by a mouseover event on one of the menu labels.
(1) It hides the currently active menuBox and unrolls the corresponding menu label.
(2) It shows the newly active menuBox and rolls the corresponding menu label.
(3) It sets the variable activeMenu to the new value.
(4) It signals that menu is currently visible by setting menuOpen to true.

function menuShow(e,mn)
{
  if (mn == activeMenu && menuOpen) return;
  menuArray[activeMenu].hide()
  cbeSetImage(menuArray[activeMenu].lblImg, menuArray[activeMenu].lblOut);
  menuArray[mn].show();
  cbeSetImage(menuArray[mn].lblImg, menuArray[mn].lblOver);
  activeMenu = mn;
  menuOpen = true;
}
      

The function menuHide() is the document.onMousemove event listener installed in windowOnload(). It constantly checks the current mouse position to see if it is within the rectangles defined by the menuBar or the active menuBox. The method contains() was modified in v3b12 just for this use. Notice that it now takes four "clipping" arguments. This allows you to define a "clipping region" on the element. If the mouse pointer is within that region, the function returns true. Notice that I'm using a negative value for the top argument. So you can have a space between the label and the box (Thanks for the idea smd!). If the mouse moves out of these regions, the active menuBox is hidden, the corresponding label is unrolled, and menuOpen is set to false.

function menuHide(e)
{
  if (!menuOpen) return;
  var x = e.pageX;
  var y = e.pageY;
  if (!menuArray[activeMenu].contains(x,y,-2,0,0,0) && !menuBar.contains(x,y)) {
    menuArray[activeMenu].hide();
    cbeSetImage(menuArray[activeMenu].lblImg, menuArray[activeMenu].lblOut);
    menuOpen = false;
  }
}
      

With CBE v3, this file had its own image functions: imgLoad() and imgRoll(). But CBE v4 includes that functionality as part of the library with the methods: cbeNewImage() and cbeSetImage(). These methods are discussed in more detail here.

The HTML

View the source of this page to get a better look at the HTML used for the menu.