Univerzální roletové menu v JavaScriptu

31. srpna 2005

Před nějakým časem přede mnou vyvstala potřeba použití roletového menu v jedné webové aplikaci. Po poměrně podrobném a dlouhém hledání již hotových programů na webu jsem zjistil, že dostupná menu jsou buďto otřesná, nebo nepochopitelná, nebo za peníze, případně splňují všechny tři dříve uvedené přídomky zároveň. Proto jsem se pustil do tvorby vlastního programu a o výsledek, především zdrojový kód, bych se s vámi nyní rád podělil.

Od menu jsem očekával splnění následujících bodů:

  • Musí být použitelné ve všech běžně užívaných prohlížečích bez nutnosti modifikace kódů či rozlišování, o jaký prohlížeč jde.
  • Musí být jednoduše a snadno definovatelné a měnitelné programátorem.
  • Pokud to bude možno, ve stránce jako takové zapisovat jenom minimální kód, vše nechat v JS.
  • Mělo by se co nejvíce podobat roletovému menu v běžně užívaných programech.
  • Mělo by být modifikovatelné i po svém vykreslení ve stránce (nejméně zapínaní a vypínaní jednotlivých položek).

Jak se mi to celé podařilo splnit se dozvíte v následujících řádcích. Samotné menu si můžete stáhnout a otestovat na svém počítači.

Celkově jsem se rozhodl jako kontejnery pro menu a submenu použít divy a v nich tabulky, abych se tak vyhnul potřeby volat funkce jako jsou insertAdjancedHTML nebo insertAdjancedElement, s jejichž použitím nemám dobré zkušenosti.

Oproti tomu tag div mi poskytuje snadnou možnost změny jeho polohy a atributu z-index a tabulka v něm zase pomocí insertRow, insertCell snadno umožní vykreslit menu samotné, ostylovat jej a obsloužit potřebné funkce. V následující kapitole bude popis, jak toto menu použít ve Vašich projektech a po ní bude následovat velmi podrobně okomentovaný zdrojový kód pro případ, že byste potřebovali něco změnit nebo vylepšit.

Použití menu ve stránce

Do stránky samotné je potřeba vepsat následující kód:

Tento div je pro horní lištu menu.

<div id=“jsMenu“>
<table id=“jsBar“ width=“1%“ cellpadding=“0Px“ cellspacing=“0Px“ border=“0Px“></table>
</div>

Tento div je pro první úroveň submenu.

<div id=“subMenu1″ class=“jsSubMenu“ style=“display:none;“>
<table id=“jsSubMenu1″ class=“jsSubMenuTable“ width=“1%“ cellpadding=“0Px“ cellspacing=“0Px“ border=“0Px“></table>
</div>

Tento div je pro druhou úroveň, analogicky si můžete do stránky vepsat tolik úrovní, kolik jenom budete od Vašeho menu chtít.

<div id=“subMenu2″ class=“jsSubMenu“ style=“display:none;“>
<table id=“jsSubMenu2″ class=“jsSubMenuTable“ width=“1%“ cellpadding=“0Px“ cellspacing=“0Px“ border=“0Px“></table>
</div>

Poté do tagu <body> přidejte onLoad=“paintMenu()“

Definice menu a jednotlivých submenu

Definice položky hlavního menu je ve funkci addMenuBar a ta má následující vstupní proměnné:

  • Pořadí položky
  • Název položky
  • Obrázek před položkou
  • Zapnuto/Vypnuto při startu stránky

Příklad: addMenuBar(0, ‚Zábava‘, ‚./img/i_zobr.gif‘, 1); Tato funkce vloží do globální proměnné menuBar na pozici uvedené v proměnné Pořadí položky novou položku hlavního menu.

Definice jedné položky submenu

Definice položky submenu ve funkci addMenuItem a ta má následující vstupní proměnné:

  • Pořadí položky
  • Název položky
  • Obrázek před položkou
  • Zapnuto/Vypnuto při startu stránky
  • Akce provedená na onClick
  • Nápověda do title stránky

Příklad:

menuBar[0].addMenuItem(0, ‚Solitaire‘, ‚./img/minilogo.gif‘, 1, “, ‚Poskládejte si solitaire ve svém prolížeči‘);

Pokud budete chtít definovat další submenu, uděláte to obdobně:

menuBar[0].menuItems[0].addMenuItem(0, ‚Classic Window\’s solitaire 1‘, “, 1, ‚chgLoc(„www.smartblue.net/solitaire/sol1.html“)‘, ‚Klasika z windows, obracíme jednu kartu‘);

Funkce addMenuItem je tedy definována úplně stejně jako pro hlavní lištu, tak pro submenu dalších úrovní.

Pokud se budete chtít dozvědět více, prohlédněte si prosím výše uvedenou stránku anebo si pročtete i zbytek článku. V následujících kapitolách bych rád zevrubně probral a okomentoval zdrojový kód tohoto menu.

Objektový model menu

Pro snazší orientaci v dalším výkladu bych rád zde definoval několik pojmů, se kterým budu dále operovat:

  • Horní lišta – hlavní menu, pod kterým se zobrazují další submenu. Ve stránce je to div id=“jsMenu“ a v něm vepsaná tabulka id=“jsBar“. Horní lišta obsahuje 1 až x položek.
  • Submenu – roletové menu, podřízené Horní liště nebo jinému submenu. Ve stránce jsou to div s id=“subMenu1-5 a v nich vepsané příslušné tabulky s id=“jsSubMenu1-5. Pro každou úroveň je použit jeden div a jedna tabulka, takže veškeré submenu přímo podřízené horní liště se zobrazuje v divu subMenu1 a tabulce jsSubMenu1. Každé submenu obsahuje 1 až x položek, případně též oddělovací čáry.

Menu lze rozdělit do dvou základních prvků – položky horního lišty – objekt menuBarCell, jejich kolekce je v matici menuBar, která je definována jako globální proměnná. Druhý základní prvek jsou položky jednotlivých submenu menuItem, jejich kolekce je v matici menuItems objektu menuBarCell případně v objektu menuItem, kde reprezentují submenu další úrovně. Každý z těchto objektů zapouzdřuje vše potřebné k svojí funkci.

Jako globální jsou funkce kliknutí myší na menu, přejetí myší přes položku menu, případně opuštění menu. Tyto funkce však pouze rozliší objekt, kde tato událost nastala a zavolají patřičnou metodu daného objektu.

Objekt menuBarCell, jeho proměnné a metody, definice horní lišty menu

Objekt menuBarCell má následující proměnné:

  • id – pozice objektu v nadřízené matici menuBar a také pořadí, v jakém bude vykreslen, musí být definováno a musí být unikátní
  • value – text položky, musí být definováno
  • img – url obrázku, který má být vykreslen před textem, nemusí být definováno
  • isOn – příznak vypnuto/zapnuto, pokud není definován, zapnuto.
  • isShown – příznak, jestli je tato položka menu rozbalena anebo ne, defaultně 0
  • obj – objekt typu td, ve kterém je tato položka fyzicky vykreslena. Při vložení objektu se nevyplňuje, k jejímu naplnění dojde až v okamžiku vykreslení menu.
  • menuItems- matice podřízených objektů typu menuItem.
  • top – zde se po vykreslení uloží vypočtená hodnota, která udává absolutní Y pozici podřízeného submenu
  • left – de se po vykreslení uloží vypočtená hodnota, která udává absolutní X pozici podřízeného submenu.

Samotná deklarace objektu menuBarCell potom vypadá následovně:

function menuBarCell(id, value, img, isOn) {
this.id = id;
this.value = value;
this.img = img;
this.isOn = isOn;
this.isShown = 0;
this.obj = null;
this.menuItems = new Array();
this.lastClicked = -1;
this.top = 0;
this.left = 0;
return this;
}

Objekt menuBarCell má definovány následující metody:

  • paintCell
  • clearContainer
  • doClick
  • addMenuItem

menuBarCell.prototype.paintCell = function(parentRow)

Jak už jsem se dříve zmiňoval, toto menu je celé založené na kombinaci divu a tabulek. Horní lišta je v HTML stránce zapsána takto:

<div id=“jsMenu“>
<table id=“jsBar“ width=“1%“ cellpadding=“0Px“ cellspacing=“0Px“ border=“0Px“></table>
</div>

Vstupní proměnná metody paintCell je potom jediný řádek této tabulky. Samotná metoda potom vloží do tabulky novou buňku a nadefinuje na ní obsluhu událostí onClick, onMouseOver, onMouseOut voláním příslušných globálních funkcí. Celá funkce potom vypadá následovně:

menuBarCell.prototype.paintCell = function(parentRow) {
var newCell = parentRow.insertCell(this.id);
if (this.img != “) {
newCell.innerHTML = ‚<img src=“‚+this.img+'“ class=“menuImg“>  ‚;
}
newCell.innerHTML += this.value;
newCell.onclick = new Function(‚barClick(‚+this.id+‘);‘);
newCell.onmouseover = new Function(‚barMouseOver(‚+this.id+‘);‘);
newCell.onmouseout = new Function(‚barMouseOut(‚+this.id+‘);‘);
newCell.className = „jsMenuBarCellOff“;
this.obj = newCell;
}

Funkce nevrací žádné výstupní proměnné.

Poznámka: Pokud budete chtít udělat víceřádkovou horní lištu, mělo by to jít, ale nezkoušel jsem to. Jediný problém, který mne napadá je umístění podřízeného submenu s překrytím dolního řádku víceřádkové horní lišty a tím i kolize událostí onClick atd. mezi příslušnou položkou horní lišty a submenu.

menuBarCell.prototype.clearContainer = function()

Tato funkce vyprázdní div, ve kterém bylo vykreslené podřízené submenu a připraví jej k dalšímu použití. Na úrovni objektu menuBarCell obsahuje pouze kontrolu na to, zda je definováno submenu a potom volá funcki clearContainer patřičného submenu.

Nemá žádné vstupní parametry ani nevrací žadné výstupní proměnné.

Celá funkce potom vypadá následovně:

menuBarCell.prototype.clearContainer = function() {
if (this.menuItems[0]) {
this.menuItems[0].clearContainer();
}
}

menuBarCell.prototype.doClick = function()

Obsluha kliknutí na jednu položku horní lišty. Tato procedura musí zajistit zobrazení a skrytí submenu prvního řádu. Jak už bylo výše uvedeno, toto submenu má podobu divu, který zajišťuje správnou polohu submenu na obrazovce a v něm je tabulka, obsahující samotné submenu. Prvním úkonem této procedury je zjištění, zda obsahuje podřízené submenu, pokud ne, je procedura ukončena. Dalším krokem je zjištění, jestli je podřízené submenu zobrazeno, tato informace je uložena v proměnné isShown. Pokud není, je potřeba spočítat polohu levého horního rohu submenu a to přes výčet offsetTop a offsetLeft všech nadřízených tagů k dané položce (je to buňka v tabulce a tabulka je v divu, takže je to TR, TABLE a DIV). Samotný výpočet y-vé souřadnice vypadá následovně:

var y = this.obj.parentNode.offsetTop+ //TR
this.obj.parentNode.parentNode.offsetTop+ //TABLE
this.obj.parentNode.parentNode.parentNode.offsetTop+ //DIV
this.obj.parentNode.parentNode.offsetHeight+1; //offset výška tabulky + odsazení
U x-ové souřadnice je potřeba přidat i polohu buňky tabulky, výpočet potom vypadá takto:
var x = this.obj.offsetLeft+ //TD
this.obj.parentNode.offsetLeft+ //TR
this.obj.parentNode.parentNode.offsetLeft+ //TABLE
this.obj.parentNode.parentNode.parentNode.offsetLeft+15; //DIV + odsazení zleva

Pak následuje vyprázdnění divu submenu, jeho umístění na x a y souřadnice a jeho vykreslení do obrazovky.

Uživatel má možnost jedním kliknutím menu zobrazit a druhým skrýt, ale stejně tak má možnost skrýt menu i kliknutím na jinou položku Horní lišty, proto je potřeba si někam uložit, které menu bylo naposledy zobrazeno a které bude potřeba skrýt. K tomu slouží globální proměnná lastClicked. Stejně tak je nutné stejnojmenné proměnné pro každý objekt typu menuBarCell a menuItem.

Nemá žádné vstupní parametry ani nevrací žádné výstupní proměnné.

Samotná procedura potom vypadá následovně:

menuBarCell.prototype.doClick = function() {
if (this.menuItems.length <= 0) { return 1; }
if (this.isShown == 0) {
var y = this.obj.parentNode.offsetTop+
this.obj.parentNode.parentNode.offsetTop+
this.obj.parentNode.parentNode.parentNode.offsetTop+
this.obj.parentNode.parentNode.offsetHeight+1;
var x = this.obj.offsetLeft+
this.obj.parentNode.offsetLeft+
this.obj.parentNode.parentNode.offsetLeft+
this.obj.parentNode.parentNode.parentNode.offsetLeft+15;
this.top = y;
this.left = x;
document.getElementById(‚subMenu1‘).style.top = y;
document.getElementById(‚subMenu1‘).style.left = x;
document.getElementById(‚subMenu1‘).style.display = “;
this.menuItems[0].clearContainer();
for (var i = 0; i < this.menuItems.length; i ++) {
this.menuItems[i].paintCell();
}
this.isShown = 1;
lastClicked = this.id;
} else {
document.getElementById(‚subMenu1‘).style.display = ‚none‘;
if (this.menuItems.length > 0) {
this.menuItems[0].clearContainer();
for (var i = 0; i < this.menuItems.length; i ++) {
this.menuItems[i].isShown = 0;
this.menuItems[i].lastClicked = -1;
}
}
this.isShown = 0;
this.lastClicked = -1;
}
}

Přidání podřízeného submenu – funkce addMenuItem

Bude popsána podrobně na stejné funkci objektu menuItem.

Definice Horní lišty – addMenuBar

Na definici horní lišty slouží globální procedura addMenuBar, která vytvoří nový objektový typu menuBarCell a vloží jej do kolekce těchto objektů v matici menuBar.

Procedura má tyto vstupní parametry:

  • id – pořadí bráno od leva, číslo – povinný
  • value – text položky, text- povinný
  • img – url obrázku, který se má vykreslit nalevo před položku – nepovinný, defaultně null
  • isOn – 1/0 – zapnuto/vypnuto – nepovinný, defaultně 1

Procedura nevrací žádné hodnoty a vypadá následovně:

function addMenuBar(id, value, img, isOn) {
var oneMenuBar = new menuBarCell(id, value, img, isOn);
menuBar[id] = oneMenuBar;
}

Vykreslení menu do stránky paintMenu

Tato globální procedura vykreslí celé menu, doporučuji umístit do <body onLoad=“paintMenu();“>. Nemá žádné vstupní parametry a nevrací žádné hodnoty, vypadá následovně:

function paintMenu() {
var menuObj = document.getElementById(‚jsBar‘);
newRow = menuObj.insertRow(0);
for (var i = 0; i < menuBar.length; i ++) {
menuBar[i].paintCell(newRow);
}
}

Objekt menuItem, jeho proměnné a metody, definice submenu

Objekt menuItem má následující proměnné:

  • parentObj – nadřízený objekt typu menuItem nebo menuBarCell
  • id – pozice objektu v nadřízené matici menuBar a také pořadí, v jakém bude vykreslen, musí být definováno a musí být unikátní
  • value – text položky, musí být definováno
  • img – url obrázku, který má být vykreslen před textem, nemusí být definováno
  • isOn – příznak vypnuto/zapnuto, pokud není definován, zapnuto. Pokud je to -1, je to separátor, vodorovná čára.
  • action – procedura volaná na onClick položky
  • altText – text který se při události onMouseOver vepíše do window.title
  • level – absolutní úroveň submenu, první pod lištou má level = 1, vyplňuje se automaticky při deklarování objektu voláním addMenuItem
  • isShown – příznak, jestli je tato položka menu rozbalena anebo ne, defaultně 0
  • obj – objekt typu td, ve kterém je tato položka fyzicky vykreslena. Při vložení objektu se nevyplňuje, k jejímu naplnění dojde až v okamžiku vykreslení menu.
  • menuItems – matice podřízených objektů typu menuItem.
  • lastClicked – ukazatel na posledy kliknutou položku dceřinného submenu. Defaultně -1.
  • paintImg – při deklaraci submenu se kontroluje, jestli obsahuje obrázky anebo ne, pokud je v submenu byť jen jediný obrázek, důležité pro zarovnání položek submenu, položky, které nemají obrázek jsou potom doplněny průhledným obrázkem. Pokud je paintImg = 0, nekreslí se žádné obrázky.
  • top – zde se po vykreslení uloží vypočtená hodnota, která udává absolutní Y pozici podřízeného submenu
  • left – de se po vykreslení uloží vypočtená hodnota, která udává absolutní X pozici podřízeného submenu.
  • className – třída css použitá pro tag <tr> ve kterém je tato položka vykreslena. Při deklaraci se vyplňuje na přednastavenou hodnotu jsMenuItemValue.

Samotná deklarace potom vypadá následovně:

function menuItem(parentObj, id, value, img, isOn, action, altText, level) {
this.parentObj = parentObj;
this.id = id;
this.value = value;
this.img = img;
if (img == “) {
this.img = imgRoot+’/empty.gif‘;
this.imgOff = this.img;
} else {
var pom = img.split(‚.‘);
var adds = pom[pom.length-1];
var imgOff = new Array();
for (var i = 0; i < pom.length-1; i ++) {
imgOff[i] = pom[i];
}
this.imgOff = imgOff.join(‚.‘)+’_off.’+adds;
}
this.isOn = isOn;
this.action = action;
this.altText = altText;
this.level = level;
this.isShown = 0;
this.obj = null;
this.menuItems = new Array();
this.lastClicked = -1;
this.paintImg = 0;
this.top = 0;
this.left = 0;
this.className = „jsMenuItemValue“;
return this;
}

Objekt menuItem má definovány následující metody:

  • addMenuItem
  • paintCell
  • doClick
  • clearContainer
  • enable
  • disable

Funkce addMenuItem

Tato funkce vytvoří nový objekt typu menuItem a přidá jej do kolekce menuItems. Procedura má následující vstupní proměnné:

  • id – pozice objektu v nadřízené matici menuBar a také pořadí, v jakém bude vykreslen, musí být definováno a musí být unikátní
  • value – text položky, musí být definováno
  • img – url obrázku, který má být vykreslen před textem, nemusí být definováno
  • isOn – příznak vypnuto/zapnuto, pokud není definován, zapnuto.
  • action – procedura volaná na onClick položky
  • altText – text který se při události onMouseOver vepíše do window.title

Funkce nevrací žádnou výstupní hodnotu a vypadá následovně:

menuItem.prototype.addMenuItem = function (id, value, img, isOn, action, altText) {
var mI = new menuItem(this, id, value, img, isOn, action, altText, this.level+1);
if (this.menuItems.length && this.menuItems.length >= 0) {
this.menuItems[this.menuItems.length] = mI;
} else { this.menuItems[0] = mI; }
var paintImg = 0;
for (var i = 0; i < this.menuItems.length-1; i ++) {
if (this.menuItems[i].paintImg == 1) { paintImg = 1; }
}
if (img != “) { paintImg = 1; }
for (var i = 0; i < this.menuItems.length; i ++) {
this.menuItems[i].paintImg = paintImg;
}
}

Funkce paintCell

Provede vykreslení jedné položky submenu do stránky. Funkce je poměrně složitější než obdobná funkce u objektu typu menuBarCell, protože musí umět vykreslit i následující věci:

  • šipku rozbaleno/sbaleno pokud položka obsahuje další podřízené submenu a status šipky se mění podle toho, jestli je toto submenu zobrazeno anebo ne.
  • oddělovací čáru mezi jednotlivými položkami

Funkce paintCell nemá žádné vstupní ani výstupní proměnné a vypadá následovně:

menuItem.prototype.paintCell = function() {
//načtu si tabulku do které mám submenu vykreslit
var tableItem = document.getElementById(‚jsSubMenu’+this.level);
//vložím nový řádek do tabulky
var newRow = tableItem.insertRow(this.id);
var addClassName = “;
if (this.isOn == 0) { addClassName = ‚Off‘; }
//kontrola, jestli má podřízené submenu a mám zobrazit šipku – je to class
if (this.menuItems.length > 0) {
newRow.className = „jsMenuItemArrow“+addClassName;
} else {
newRow.className = „jsMenuItemNoArrow“+addClassName;
}
//vložím novou buňku
var newCell = newRow.insertCell(0);
//isOn = -1 – test na oddělovací čáru, má to jiné chování než obyčejná položka menu, především při opouštění menu a jeho skrytí, nebere si to totiž lastClicked parametr daného meunItems nadřízeného objektu
if (this.isOn == -1) {
newCell.innerHTML = ‚<hr class=“jsMenuItemLine“>‘;
this.obj = newCell;
newCell.onmouseover = new Function(‚lineOver();‘);
newCell.onmouseout = new Function(‚lineOut();‘);
return 1;
}
//test na vykreslení obrázku
if (this.paintImg == 1) {
var img = this.img;
if (this.isOn == 0) { img = this.imgOff; }
newCell.innerHTML = ‚<img src=“‚+img+'“ class=“menuImg“>  ‚;
}
//deklarace obslužných funkcí buňky
newCell.innerHTML += this.value;
newCell.onclick = new Function(‚itemClick(‚+this.id+‘,this);‘);
newCell.onmouseover = new Function(‚itemOver(‚+this.id+‘,this);‘);
newCell.onmouseout = new Function(‚itemOut(‚+this.id+‘,this);‘);
newCell.parentObj = this.parentObj;
newCell.className = „jsMenuItemValue“+addClassName;
newCell.noWrap = true;
this.obj = newCell;
}

Funkce doClick – obsluha kliknutí na položku submenu

Tato procedura je v podstatě hlavní procedurou fungování celého menu. V případě, že daná položka má definovanou akci, vykoná eval na tuto akci a submenu uzavře. Pokud položka vlastní podřízené submenu, toto menu se buďto zobrazí anebo skryje. Věc komplikuje to, že musím skrýt jiné rozbalené submenu a všechny s ním související nadřízené submenu.

Funkce nemá žádné vstupní ani výstupní proměnné.

menuItem.prototype.doClick = function() {
//test na podřízené submenu
if (this.menuItems.length <= 0) {
//pokud není a je definovaná vykoná eval na danou akci
if (this.action != “) { eval(this.action); }
this.clearContainer();
//skryje sám sebe
if (this.parentObj) {
//dokud existuje rodič – skryje i rodiče
var parentObj = this.parentObj;
while (parentObj) {
parentObj.clearContainer();
parentObj.lastClicked = -1;
parentObj.isShown = 0;
parentObj = parentObj.parentObj;
}
}
//lastClicked jako globální proměnná obsahuje informaci, na kterou položku z menuBar bylo kliknuto, i to se musí vynulovat
lastClicked = -1;
return 1;
}
//zobrazení/skrytí podřízeného submenu
if (this.isShown == 0 && this.menuItems.length > 0) {
//zobrazení
//napočítání součadnic levého horního rohu přes nadřízený objekt
var y = this.obj.parentNode.offsetTop+
this.obj.parentNode.parentNode.offsetTop+
this.obj.parentNode.parentNode.parentNode.offsetTop+5+
this.parentObj.top;
var x = this.obj.offsetLeft+
this.obj.parentNode.offsetLeft+
this.obj.parentNode.parentNode.offsetLeft+
this.obj.parentNode.parentNode.parentNode.offsetLeft+
this.obj.parentNode.parentNode.parentNode.offsetWidth+
this.parentObj.left-4;
this.top = y; this.left = x;
document.getElementById(‚subMenu’+(this.level+1)).style.top = y;
document.getElementById(‚subMenu’+(this.level+1)).style.left = x;
document.getElementById(‚subMenu’+(this.level+1)).style.display = “;
//vyčistí tabulku a vepíše do ní submenu
this.menuItems[0].clearContainer();
for (var i = 0; i < this.menuItems.length; i ++) {
this.menuItems[i].paintCell();
}
this.isShown = 1;
this.parentObj.lastClicked = this.id;
} else {
//skrytí
document.getElementById(‚subMenu’+(this.level+1)).style.display = ‚none‘;
if (this.menuItems.length > 0) {
this.menuItems[0].clearContainer();
for (var i = 0; i < this.menuItems.length; i ++) {
this.menuItems[i].isShown = 0;
this.menuItems[i].lastClicked = -1;
}
}
this.parentObj.lastClicked = -1;
this.isShown = 0;
this.lastClicked = -1;
}
}

Funkce clearContainer

Tato funkce provede skrytí jak této položky, tak všech jí podřízených submenu de facto rekurzivním voláním stejné funkce pro všechny úrovně podřízených submenu.

Neobsahuje žádné vstupní ani výstupní proměnné a vypadá následovně:

menuItem.prototype.clearContainer = function() {
//pokud má podřízená submenu, musí skrýt i je
if (this.menuItems.length > 0) {
this.menuItems[0].clearContainer();
for (var i = 0; i < this.menuItems.length; i ++) {
this.menuItems[i].isShown = 0;
this.menuItems[i].lastClicked = -1;
}
}
var tableItem = document.getElementById(‚jsSubMenu’+this.level);
var stopValue = tableItem.rows.length;
//vymaže tabulku se submenu samotným – všechny jeho řádky
for (var i = 0; i < stopValue; i ++) {
tableItem.deleteRow(0);
}
//skryje div
if (document.getElementById(‚subMenu’+(this.level+1))) {
document.getElementById(‚subMenu’+(this.level+1)).style.display = ‚none‘;
}
}

Funkce enable

Položka submenu může být disable – sice se zobrazuje, ale má jiný styl a nejde na ní kliknout, touto funkcí se opět zapne. Mělo by se volat zároveň s překreslením menu.

Nemá žádné vstupní ani výstupní proměnné a vypadá následovně:

menuItem.prototype.enable = function () {
this.isOn = 1;
}

Funkce disable

Opak funkce enable, vypne položku.

menuItem.prototype.disable = function () {
this.isOn = 0;
}

Globální funkce pro obsluhu submenu a menu

Pro snazší napsání a přehlednější kód jsem se rozhodl „odchytávat“ události, odehrávající se v menu a submenu globálními funkcemi a teprve v nich volat příslušné funkce mnou definovaných objektů.

Funkce, které začínají na item jsou pro menuItem, zbylé pro menuBarCell:

  • barClick(id) – obslouží kliknutí na hlavní lištu
  • barMouseOver(id) – najetí myší nad položku hlavní lišty
  • barMouseOut(id) – opuštění položky
  • itemClick(id, obj) – obsluha kliknutí na položku submenu
  • item over(id, obj) – najetí myší na položku submenu
  • itemOut(id,obj) – opuštění položky submenu
  • lineOver() – najetí myší nad oddělovací čáru
  • lineOut() – opuštění oddělovací čáry
  • doHideMenu() – skryje submenu po jeho opuštění
  • paintMenu() – vykreslí menu do stránky

Funkce BarClick

Obslouží kliknutí na položku hlavního menu, vstupem je id této položky, výstupní proměnná není. Důležitá je změna globální proměnné lastClicked, která obsahuje id naposledy kliknuté položky, pokud se klikne opětovně na tu samou položku, nastaví se na -1 a tato skryje se podřízené submenu. Nastavení položky lastClicked se provádí přímo ve funkci doClick objektu menuBarcell. Samotná funkce vypadá následovně:

function barClick(id) {
if (lastClicked > -1 && lastClicked != id) {
menuBar[lastClicked].doClick();
lastClicked = -1;
}
menuBar[id].doClick();
}

Funkce barMouseOver

Obslouží vstup kurzoru na položku hlavního menu. Změní její styl na zvýraznění této položky – jsMenuBarCellOn – a zároveň zavolá onClick této položky. Vstupní a výstupní proměnné jsou jako u barClick a funkce vypadá následovně:

function barMouseOver(id) {
if (lastClicked > -1) { barClick(id); }
menuBar[id].obj.className = „jsMenuBarCellOn“;
}

Funkce barMouseOut

Obslouží výstup kurzoru z dané položky. Vrátí styl zpět na předchozí styl nezvýrazněné položky. Vstupní a výstupní proměnné jsou jako u barClick a funkce vypadá následovně:

function barMouseOut(id) {
menuBar[id].obj.className = „jsMenuBarCellOff“;
}

Funkce itemClick

Obsluha kliknutí na položku submenu. Vstupem je id položky submenu a odkaz na tuto položku. Je to z důvodu skrytí i nadřízeného submenu po vykonání akce, nebo skrytí podřízeného submenu při opětovném kliknutí na jedu a tu samou položku. Samotná funkce potom vypadá následovně:

function itemClick(id, obj) {
var paternMenu = obj.parentObj;
if (paternMenu.menuItems[id].isOn != 1) { return 0; }
if (obj.parentObj.lastClicked > -1 && obj.parentObj.lastClicked != id) {
obj.parentObj.menuItems[obj.parentObj.lastClicked].doClick();
}
paternMenu.menuItems[id].doClick();
}

Funkce itemOver

Obsluha vstupu kurzoru na položku submenu. Provede zvýraznění této položky, zapíše altText do window.status a potom provede itemClick dané položky. Další důležitá věc kromě správy lastClicked daného menuItems je správa globální proměnné hideMenu, která se naplní při itemOut nebo lineOut – je to id časovače, který, pokud není zavčas smazán, provede skrytí celého menu, protože došlo k opuštění menu a submenu. Samotná funkce potom vypadá následovně:

function itemOver(id, obj) {
if (hideMenu > -1) {
clearTimeout(hideMenu);
hideMenu = -1;
}
window.status = obj.parentObj.menuItems[id].altText;
if (obj.parentObj.menuItems[id].isOn != 1) { return 0; }
if ( obj.parentObj.menuItems[id].menuItems.length > 0) {
itemClick(id, obj);
}
obj.className = obj.parentObj.menuItems[id].className+’On‘;
window.status = obj.parentObj.menuItems[id].altText;
}

Funkce itemOut

Provede obsluhu opuštění položky submenu, nastaví časovač hideMenu na provedení doHideMenu pokud nedojde ke vstupu na jinou položku submenu do cca 0.5 sekundy. Funkce má stejné vstupní proměnné jako itemClick a nemá žádnou výstupní proměnnou. Samotná funkce vypadá následovně:

function itemOut(id, obj) {
if (obj.parentObj.menuItems[id].isOn != 1) {
hideMenu = setTimeout(„doHideMenu()“, 500);
return 0;
}
obj.className = obj.parentObj.menuItems[id].className;
window.status = “;
hideMenu = setTimeout(„doHideMenu()“, 500);
}

Funkce lineOver

Provede obsluhu vstupu kurzoru na oddělovací čáru submenu. Nejde o nic jiného než o vynulování časovače hideMenu. Nemá žádné vstupní ani výstupní proměnné a vypadá následovně:

function lineOver() {
if (hideMenu > -1) {
clearTimeout(hideMenu);
hideMenu = -1;
}
}

Funkce lineOut

Obsluha opuštění oddělovací čary submenu. Provede nastavení časovače pro skrytí submenu obdobně jako itemOut. Nemá žádné vstupní ani výstupní proměnné a vypadá následovně:

function lineOut() {
hideMenu = setTimeout(„doHideMenu()“, 500);
}

Funkce doHideMenu

Po opuštění submenu v případě že nedojde k vynulování časovače tato funkce pomocí opětovného zavolání doClick naposledy kliknuté položky hlavní menu skryje všechna podřízená submenu a nechá pouze hlavní lištu. Neobsahuje žádné vstupní ani výstupní proměnné a vypadá následovně:

function doHideMenu() {
if (lastClicked > -1) {
menuBar[lastClicked].doClick();
}
}

Funkce paintMenu

Tato funkce provede vykreslení menu do stránky, je potřeba volat ji v onLoad tagu body. Nemá vstupní ani výstupní proměnné a vypadá následovně:

function paintMenu() {
var menuObj = document.getElementById(‚jsBar‘);
newRow = menuObj.insertRow(0);
for (var i = 0; i < menuBar.length; i ++) {
menuBar[i].paintCell(newRow);
}
}

Souhrn

Závěrem bych rád řekl, že možná bude moje menu pro někoho nepochopitelné, pro jiné otřesné, ale určitě bude zadarmo…

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Předchozí článek svice.cz
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

1 komentář

  1. sdf

    Říj 27, 2011 v 13:41

    a v chromu to nefunguje vubec :D :D :D

    Odpovědět

Napsat komentář: sdf Zrušit odpověď na komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *