Cascading Menu Script as HTML
Return to Cascading Menu Script Explained
Part 1
Part 2
/******************************************************
* HMenu.js - v. 1.05 000710
* Cascading menu creation system
* by Aaron Prenot
* Copyright (c) 2000 Aaron Prenot
* http://www.redrival.com/aprenot
*
* Published and Documented at
* o www.redrival.com/aprenot
* o www.webdevelopersjournal.com
*
* You may use this code on a public web site only if
* this entire copyright notice appears unchanged.
*
* Please send questions and bug reports to:
* aprenot@hotmail.com
******************************************************/
var menus = new Array();
var activeItem = null;
var IE4 = ((navigator.appName == "Microsoft Internet Explorer") &&
(parseInt(navigator.appVersion) >= 4 ) && !(navigator.appVersion.indexOf("5")
> -1 ));
function Menu(){
this.id = "";
this.subMenus = new Array();
this.items = new Array();
this.hasChildren = false;
this.isChild = false;
this.parentMenu = null;
this.parentItem = null;
}
function Item(){
this.id = "";
this.hasMenu = false;
this.menu = null;
this.parentMenu = null;
}
function initMenu(){
// test for IE4+, it won't work otherwise
if(!document.all) return false;
findMenus();
menuContainer.activeMenu = null;
menuContainer.closeAll = closeAll;
attachMenus();
}
function findMenus(){
var cTag = menuContainer.children;
for(var i=0; i < cTag.length; i++){
tcTag = cTag[i];
if(tcTag.className == "menu"){
var tMenu = findSubMenus(tcTag);
menus[menus.length] = tMenu;
}
}
var tHTML = "<div id=\"menuContainer\">";
for(var i=0; i < menus.length; i++){
var tcTag = menus[i]
tHTML += moveHTML(tcTag);
}
tHTML += "</div>";
menuContainer.outerHTML = "";
document.body.innerHTML += tHTML;
for(var i=0; i < menus.length; i++){
var tcTag = menus[i];
setupMenu(tcTag);
}
}
function findSubMenus(menu){
var cMenu = menu.children;
var tMenu = new Menu();
tMenu.id = menu.id;
for(var i=0; i < cMenu.length; i++){
var tcMenu = new Item();
tcMenu.id = cMenu[i].id;
if(tcMenu.id.indexOf("subMenu") != -1){
++i;
var subMenu = cMenu[i];
tMenu.subMenus[tMenu.subMenus.length] = findSubMenus(subMenu)
tMenu.subMenus[(tMenu.subMenus.length - 1)].isChild = true;
tMenu.subMenus[(tMenu.subMenus.length - 1)].parentMenu = tMenu;
tMenu.subMenus[(tMenu.subMenus.length - 1)].parentItem = tcMenu;
tMenu.hasChildren = true;
tcMenu.hasMenu = true;
tcMenu.menu = tMenu.subMenus[(tMenu.subMenus.length - 1)];
}
tcMenu.parentMenu = tMenu;
tMenu.items[tMenu.items.length] = tcMenu;
}
return tMenu;
}
function moveHTML(menu){
var tHTML = "";
if(menu.hasChildren == true){
for(var i=0; i < menu.subMenus.length; i++){
tHTML += moveHTML(menu.subMenus[i]);
}
}
var tMenu = eval(menu.id);
var tMenuHTML = tMenu.outerHTML;
tMenu.outerHTML = "";
tHTML += tMenuHTML;
return tHTML;
}
function setupMenu(menu){
if(menu.hasChildren == true){
for(var i=0; i < menu.subMenus.length; i++){
setupMenu(menu.subMenus[i]);
}
}
tMenu = eval(menu.id);
tMenu.noWrap = true;
tMenu.hasChildren = menu.hasChildren;
tMenu.hasVisibleChild = false;
tMenu.visibleChild = null;
tMenu.isChild = menu.isChild;
tMenu.onselectstart = returnFalse;
tMenu.onclick = handleMenuClick;
tMenu.currWidth = 0;
tMenu.items = menu.items;
for(var i=0; i < menu.items.length; i++){
setupItem(menu.items[i]);
}
tMenu.style.pixelWidth += 5;
for(var i=0; i < menu.items.length; i++){
var tItem = eval(menu.items[i].id);
if(!IE4){
tItem.style.width = "100%";
if(tItem.hasMenu == true) {
tItem.more.style.position = "absolute";
tItem.more.style.pixelLeft = (tMenu.style.pixelWidth - 17);
}
}
}
if(menu.isChild == true){
tMenu.parentMenu = eval(menu.parentMenu.id);
tMenu.parentItem = eval(menu.parentItem.id);
}
}
function setupItem(item){
tItem = eval(item.id);
tItem.highlight = highlight;
tItem.onmouseover = tItem.highlight;
tItem.parentMenu = eval(item.parentMenu.id);
tItem.hasMenu = false;
tItem.menu = null;
tItem.onclick = handleItemClick;
tItem.ondragstart = returnFalse;
tItem.noWrap = true;
if(item.hasMenu == true){
tItem.innerHTML += "<span id=\"" + item.id + "_more\"
class=\"more\">4</span>";
tItem.more = eval(item.id + "_more");
tItem.menu = eval(item.menu.id);
tItem.hasMenu = true;
}
if(!IE4) {
tItem.parentMenu.style.pixelWidth = Math.max(tItem.parentMenu.currWidth, tItem.offsetWidth);
}
}
function highlight(){
if(activeItem != null){
if(activeItem != this){
unhighlight(activeItem);
} else {
return;
}
}
event.cancelBubble = true;
this.className = "menuItemOver";
activeItem = this;
// dont open a menu thats already open
if((this.hasMenu == true) && (this.parentMenu.hasVisibleChild == true)
&& (this.parentMenu.visibleChild == this.menu)) return;
// if there is a menu open, close it
if(this.parentMenu.hasChildVisible == true){
hideMenu(this.parentMenu.visibleChild);
}
// if this item has a menu, show it
if(this.hasMenu){
showMenu(this.menu);
}
}
function unhighlight(menu){
event.cancelBubble = true;
menu.className = "menuItem";
}
function showMenu(menu, x, y){
event.cancelBubble = true;
if(menu){
if(IE4){
for(var i=0; i < menu.items.length; i++){
tItem = eval(menu.items[i].id);
if(tItem.hasMenu == true) {
tItem.more.style.pixelLeft = (menu.offsetWidth - tItem.more.offsetLeft - 17);
}
}
}
if(menu.isChild == true){
menu.style.pixelTop = menu.parentItem.offsetTop + menu.parentMenu.offsetTop +
4;
menu.style.pixelLeft = menu.parentMenu.offsetLeft + menu.parentMenu.offsetWidth
- 4;
menu.parentMenu.hasChildVisible = true;
menu.parentMenu.visibleChild = menu;
menu.style.zIndex = menu.parentMenu.style.zIndex + 1;
} else if(x && y){
menu.style.pixelTop = y;
menu.style.pixelLeft = x;
menuContainer.activeMenu = menu;
document.onclick = menuContainer.closeAll;
}
} else {
menu = eval(this.menu);
if(IE4){
for(var i=0; i < menu.items.length; i++){
tItem = eval(menu.items[i].id);
if(tItem.hasMenu == true) {
tItem.more.style.pixelLeft = (menu.offsetWidth - tItem.more.offsetLeft - 17);
}
}
}
menu.style.pixelTop = event.clientY;
menu.style.pixelLeft = event.clientX;
menuContainer.activeMenu = menu;
document.onclick = menuContainer.closeAll;
}
menu.className = "visibleMenu";
return false;
}
function hideMenu(menu){
// to handle the careless child menu hiding down below
if(menu == null) return false;
event.cancelBubble = true;
// i do this kind of carelessly. i was having trouble otherwise
hideMenu(menu.visibleChild);
if(menu.isChild == true){
menu.parentMenu.hasChildVisible = false;
menu.parentMenu.visibleChild = null;
} else {
document.onclick = "";
menuContainer.activeMenu = null;
}
menu.className = "menu";
}
function closeAll(){
hideMenu(menuContainer.activeMenu);
}
// simple function to return false
function returnFalse(){return false;}
// function to be used for later functionality
// for now it just keeps the menu open when it receives a click;
function handleMenuClick(){
event.cancelBubble = true;
return false;
}
// just like the function above, only it closes the menu
function handleItemClick(){
event.cancelBubble = true;
menuContainer.closeAll();
}
// searches the document for elements with a menu paramater
function attachMenus(){
for(var i in document.all){
if(document.all[i].menu){
document.all[i].onclick = showMenu;
}
}
}
Return to Cascading Menu Script Explained
Part 1
Part 2