Entry
how do i create a new drop-down menu from a drop-down menu?
Apr 7th, 2008 23:42
ha mo, Colin Fraser, Eric Magic,
I picked up a solution to this question from Angus Turnbull. I no
longer have his email but I used it as a proposed solution to a project
I was involved with. (While this works well, it was not used in the
project. However, I think this is a great menu structure, it would be
great in the right environment.) Thank you Angus, wherever you are.
Carefully read the instructions, do not change anything you do not need
to. While this is long, it does work.
The main page:
<HTML>
<HEAD>
<script src="menu.js"></script>
<style>
.itemBorder { border: 1px solid black }
.itemText { text-decoration: none; color: #FFFFFF; font: 14px Arial,
Helvetica }
</style>
</HEAD>
<BODY marginwidth="0" marginheight="0" style="margin: 0"
onLoad="writeMenus()" onResize="if (isNS4) nsResizeHandler()">
<!-- It's important that you position the menu over a background, like
a table/image -->
<table bgcolor="#000066" width="100%" border="0" cellpadding="0"
cellspacing="0">
<tr><td height="20"><font size="1"> </font></td></tr></table>
<p>
<font face="arial, helvetica" size="+2">Test Menu for Project</font>
<p>
</body>
</HTML>
The menu.js file:
//<SCRIPT LANGUAGE="JavaScript">
//<!-- Original: Angus Turnbull -->
var isDOM = (document.getElementById ? true : false);
var isIE4 = ((document.all && !isDOM) ? true : false);
var isNS4 = (document.layers ? true : false);
function getRef(id) {
if (isDOM) return document.getElementById(id);
if (isIE4) return document.all[id];
if (isNS4) return document.layers[id];
}
function getSty(id) {
return (isNS4 ? getRef(id) : getRef(id).style);
}
// Hide timeout.
var popTimer = 0;
// Array showing highlighted menu items.
var litNow = new Array();
function popOver(menuNum, itemNum) {
clearTimeout(popTimer);
hideAllBut(menuNum);
litNow = getTree(menuNum, itemNum);
changeCol(litNow, true);
targetNum = menu[menuNum][itemNum].target;
if (targetNum > 0) {
thisX = parseInt(menu[menuNum][0].ref.left) + parseInt(menu[menuNum]
[itemNum].ref.left);
thisY = parseInt(menu[menuNum][0].ref.top) + parseInt(menu[menuNum]
[itemNum].ref.top);
with (menu[targetNum][0].ref) {
left = parseInt(thisX + menu[targetNum][0].x);
top = parseInt(thisY + menu[targetNum][0].y);
visibility = 'visible';
}
}
}
function popOut(menuNum, itemNum) {
if ((menuNum == 0) && !menu[menuNum][itemNum].target)
hideAllBut(0)
else
popTimer = setTimeout('hideAllBut(0)', 500);
}
function getTree(menuNum, itemNum) {
// Array index is the menu number. The contents are null (if that menu
is not a parent)
// or the item number in that menu that is an ancestor (to light it up).
itemArray = new Array(menu.length);
while(1) {
itemArray[menuNum] = itemNum;
// If we've reached the top of the hierarchy, return.
if (menuNum == 0) return itemArray;
itemNum = menu[menuNum][0].parentItem;
menuNum = menu[menuNum][0].parentMenu;
}
}
// Pass an array and a boolean to specify colour change, true = over
colour.
function changeCol(changeArray, isOver) {
for (menuCount = 0; menuCount < changeArray.length; menuCount++) {
if (changeArray[menuCount]) {
newCol = isOver ? menu[menuCount][0].overCol : menu[menuCount]
[0].backCol;
// Change the colours of the div/layer background.
with (menu[menuCount][changeArray[menuCount]].ref) {
if (isNS4) bgColor = newCol;
else backgroundColor = newCol;
}
}
}
}
function hideAllBut(menuNum) {
var keepMenus = getTree(menuNum, 1);
for (count = 0; count < menu.length; count++)
if (!keepMenus[count])
menu[count][0].ref.visibility = 'hidden';
changeCol(litNow, false);
}
// *** MENU CONSTRUCTION FUNCTIONS ***
function Menu(isVert, popInd, x, y, width, overCol, backCol,
borderClass, textClass) {
// True or false - a vertical menu?
this.isVert = isVert;
// The popout indicator used (if any) for this menu.
this.popInd = popInd
// Position and size settings.
this.x = x;
this.y = y;
this.width = width;
// Colours of menu and items.
this.overCol = overCol;
this.backCol = backCol;
// The stylesheet class used for item borders and the text within items.
this.borderClass = borderClass;
this.textClass = textClass;
// Parent menu and item numbers, indexed later.
this.parentMenu = null;
this.parentItem = null;
// Reference to the object's style properties (set later).
this.ref = null;
}
function Item(text, href, frame, length, spacing, target) {
this.text = text;
this.href = href;
this.frame = frame;
this.length = length;
this.spacing = spacing;
this.target = target;
// Reference to the object's style properties (set later).
this.ref = null;
}
function writeMenus() {
if (!isDOM && !isIE4 && !isNS4) return;
for (currMenu = 0; currMenu < menu.length; currMenu++) with (menu
[currMenu][0]) {
// Variable for holding HTML for items and positions of next item.
var str = '', itemX = 0, itemY = 0;
// Remember, items start from 1 in the array (0 is menu object itself,
above).
// Also use properties of each item nested in the other with() for
construction.
for (currItem = 1; currItem < menu[currMenu].length; currItem++) with
(menu[currMenu][currItem]) {
var itemID = 'menu' + currMenu + 'item' + currItem;
// The width and height of the menu item - dependent on orientation!
var w = (isVert ? width : length);
var h = (isVert ? length : width);
// Create a div or layer text string with appropriate styles/properties.
// Thanks to Paul Maden (www.paulmaden.com) for helping debug this in
IE4, apparently
// the width must be a miniumum of 3 for it to work in that browser.
if (isDOM || isIE4) {
str += '<div id="' + itemID + '" style="position: absolute; left: ' +
itemX + '; top: ' + itemY + '; width: ' + w + '; height: ' + h + ';
visibility: inherit; ';
if (backCol) str += 'background: ' + backCol + '; ';
str += '" ';
}
if (isNS4) {
str += '<layer id="' + itemID + '" left="' + itemX + '" top="' + itemY
+ '" width="' + w + '" height="' + h + '" visibility="inherit" ';
if (backCol) str += 'bgcolor="' + backCol + '" ';
}
if (borderClass) str += 'class="' + borderClass + '" ';
// Add mouseover handlers and finish div/layer.
str += 'onMouseOver="popOver(' + currMenu + ',' + currItem + ')"
onMouseOut="popOut(' + currMenu + ',' + currItem + ')">';
// Add contents of item (default: table with link inside).
// In IE/NS6+, add padding if there's a border to emulate NS4's layer
padding.
// If a target frame is specified, also add that to the <a> tag.
str += '<table width="' + (w - 8) + '" border="0" cellspacing="0"
cellpadding="' + (!isNS4 && borderClass ? 3 : 0) + '"><tr><td
align="left" height="' + (h - 7) + '">' + '<a class="' + textClass + '"
href="' + href + '"' + (frame ? ' target="' + frame + '">' : '>') +
text + '</a></td>';
if (target > 0) {
// Set target's parents to this menu item.
menu[target][0].parentMenu = currMenu;
menu[target][0].parentItem = currItem;
// Add a popout indicator.
if (popInd) str += '<td class="' + textClass + '" align="right">' +
popInd + '</td>';
}
str += '</tr></table>' + (isNS4 ? '</layer>' : '</div>');
if (isVert) itemY += length + spacing;
else itemX += length + spacing;
}
if (isDOM) {
var newDiv = document.createElement('div');
document.getElementsByTagName('body').item(0).appendChild(newDiv);
newDiv.innerHTML = str;
ref = newDiv.style;
ref.position = 'absolute';
ref.visibility = 'hidden';
}
// Insert a div tag to the end of the BODY with menu HTML in place for
IE4.
if (isIE4) {
document.body.insertAdjacentHTML('beforeEnd', '<div id="menu' +
currMenu + 'div" ' + 'style="position: absolute; visibility: hidden">'
+ str + '</div>');
ref = getSty('menu' + currMenu + 'div');
}
// In NS4, create a reference to a new layer and write the items to it.
if (isNS4) {
ref = new Layer(0);
ref.document.write(str);
ref.document.close();
}
for (currItem = 1; currItem < menu[currMenu].length; currItem++) {
itemName = 'menu' + currMenu + 'item' + currItem;
if (isDOM || isIE4) menu[currMenu][currItem].ref = getSty(itemName);
if (isNS4) menu[currMenu][currItem].ref = ref.document[itemName];
}
}
with(menu[0][0]) {
ref.left = x;
ref.top = y;
ref.visibility = 'visible';
}
}
// Syntaxes: *** START EDITING HERE, READ THIS SECTION CAREFULLY! ***
//
// menu[menuNumber][0] = new Menu(Vertical menu? (true/false), 'popout
indicator', left, top,
// width, 'mouseover colour', 'background colour', 'border
stylesheet', 'text stylesheet');
//
// Left and Top are measured on-the-fly relative to the top-left corner
of its trigger, or
// for the root menu, the top-left corner of the page.
//
// menu[menuNumber][itemNumber] = new Item('Text', 'URL', 'target
frame', length of menu item,
// additional spacing to next menu item, number of target menu to
popout);
//
// If no target menu (popout) is desired, set it to 0. Likewise, if
your site does not use
// frames, pass an empty string as a frame target.
//
// Something that needs explaining - the Vertical Menu setup. You can
see most menus below
// are 'true', that is they are vertical, except for the first root
menu. The 'length' and
// 'width' of an item depends on its orientation -- length is how long
the item runs for in
// the direction of the menu, and width is the lateral dimension of the
menu. Just look at
// the examples and tweak the numbers, they'll make sense eventually :).
var menu = new Array();
// Default colours passed to most menu constructors (just passed to
functions, not
// a global variable - makes things easier to change later in bulk).
var defOver = '#336699', defBack = '#003366';
// Default 'length' of menu items - item height if menu is vertical,
width if horizontal.
var defLength = 22;
// Menu 0 is the special, 'root' menu from which everything else arises.
menu[0] = new Array();
// A non-vertical menu with a few different colours and no popout
indicator, as an example.
// *** MOVE ROOT MENU AROUND HERE *** it's positioned at (5, 0) and is
20px high now.
menu[0][0] = new Menu(false, '', 5, 0,
20, '#0000ff', '#000066', '', 'itemText');
// Notice how the targets are all set to nonzero values...
// The 'length' of each of these items is 40, and there is spacing of
10 to the next item.
// Most of the links are set to '#' hashes, make sure you change them
to actual files.
menu[0][1] = new Item(' Regions', '#', '', 100, 10, 1);
menu[0][2] = new Item(' Buildings', '#', '', 100, 10, 2);
menu[0][3] = new Item(' Events', '#', '', 100, 10, 3);
menu[0][4] = new Item(' Places', '#', '', 100, 10, 4);
menu[0][5] = new Item(' People', '#', '', 100, 10, 5);
// Non-zero target means this will trigger a popout
// An example of a link with a target frame/window as well...
menu[0][6] = new Item(' About', 'about.htm', '_new', 80, 10, 0);
// Districts menu.
menu[1] = new Array();
// This menu is positioned 0px across and 21 down from its trigger, and
is 100 wide.
// All text in this menu has the stylesheet class 'item' -- see the
<style> section above.
// We've passed a 'greater-than' sign '>' as a popout indicator. Try an
image...?
menu[1][0] = new Menu(true, '>', 0, 21, 110, defOver,
defBack, 'itemBorder', 'itemText');
menu[1][1] = new Item('Morgul Vale', '#', '', defLength, 0, 0);
menu[1][2] = new Item('Plains of Gorgoroth', '#', '', defLength, 0, 0);
menu[1][3] = new Item('Gondor', '#', '', defLength, 0, 0);
menu[1][4] = new Item('Hollin', '#', '', defLength, 0, 0);
// Buildings menu.
menu[2] = new Array();
menu[2][0] = new Menu(true, '>', 0, 21, 100, defOver,
defBack, 'itemBorder', 'itemText');
menu[2][1] = new Item('Orthanc', '#', '', defLength, 0, 0);
menu[2][2] = new Item('Barad Dur', '#', '', defLength, 0, 0);
menu[2][3] = new Item('Tower of Ecthelion', '#', '', defLength, 0, 0);
// Events
menu[3] = new Array();
menu[3][0] = new Menu(true, '»', 0, 21, 100, defOver,
defBack, 'itemBorder', 'itemText');
menu[3][1] = new Item('Battles', '#', '', defLength, 0, 6);
menu[3][2] = new Item('The Rings', '#', '', defLength, 0, 0);
menu[3][3] = new Item('Bree Incident', '#', '', defLength, 0, 0);
// Places
menu[4] = new Array();
menu[4][0] = new Menu(true, '»', 0, 21, 120, defOver,
defBack, 'itemBorder', 'itemText');
menu[4][1] = new Item('The Shire', '#', '', defLength, 0, 7);
menu[4][2] = new Item('Rivendell', '#', '', defLength, 0, 0);
menu[4][3] = new Item('Moria', '#', '', defLength, 0, 0);
//People
menu[5] = new Array();
menu[5][0] = new Menu(true, '>', 0, 21, 100, defOver,
defBack, 'itemBorder', 'itemText');
menu[5][1] = new Item('Frodo', '#', '', defLength, 0, 0);
menu[5][2] = new Item('Sauron', '#', '', defLength, 0, 0);
menu[5][3] = new Item('Saruman', '#', '', defLength, 0, 0);
// popout for Battles
menu[6] = new Array();
menu[6][0] = new Menu(true, '>', 100, 0, 120, defOver,
defBack, 'itemBorder', 'itemText');
menu[6][1] = new Item('Dagorlad', '#', '', 20, 0, 0);
menu[6][2] = new Item('Plennor Fields', '#', '', 20, 0, 0);
// popout for The Shire
menu[7] = new Array();
menu[7][0] = new Menu(true, '>', 120, 0, 100, defOver,
defBack, 'itemBorder', 'itemText');
menu[7][1] = new Item('Bag End', 'bagend.htm', '', 20, 0, 0);
menu[7][2] = new Item('Hobbiton', '#', '', 20, 0, 0);
// *** OPTIONAL CODE FROM HERE DOWN ***
// These two lines handle the window resize bug in NS4. See <body
onResize="...">.
// I recommend you leave this here as otherwise when you resize NS4's
width menus are hidden.
var popOldWidth = window.innerWidth;
nsResizeHandler = new Function('if (popOldWidth != window.innerWidth)
location.reload()');
// This is a quick snippet that captures all clicks on the document and
hides the menus
// every time you click. Use if you want.
if (isNS4) document.captureEvents(Event.CLICK);
document.onclick = clickHandle;
function clickHandle(evt)
{
if (isNS4) document.routeEvent(evt);
hideAllBut(0);
}
// This is just the moving command for the example.
function moveRoot()
{
with(menu[0][0].ref) left = ((parseInt(left) < 100) ? 100 : 5);
}
function gothere(loc) {
if (loc!='#') location.href=loc
else alert("This page is not yet ready!");
}
// End -->
You will need to make sure all the comments are properly arranges, but
there is a number of items that will guide you into the correct
procedures to changing this to suit your page.
Good luck.
http://www.businessian.com
http://www.computerstan.com
http://www.financestan.com
http://www.healthstan.com
http://www.internetstan.com
http://www.moneyenews.com
http://www.technologystan.com
http://www.zobab.com
http://www.healthinhealth.com