![]() |
Dynamisches HTML:
|
|
| |
| Name: | Daniel Thoma |
| E-Mail: |
|
Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!
Im Web sind bereits zahllose DHTML Menüs zu finden, die eine aufklappbare Baumstruktur zur Verfügung stellen.
Die meisten von ihnen sind aber kompliziert und unflexibel.
Das hier vorgestellte Skript soll diese Probleme lösen, indem das das Menü vollständig mit HTML gestaltet wird,
und nur die notwendige Ereignisverarbeitung in Javascript realisiert wird.
Dadurch wird das Menü fast beliebig anpassbar und bleibt selbst dann benutzbar, wenn kein Javascript verfügbar ist.
Anzeigebeispiel: So sieht's aus
<html>
<head>
<title>Beispiel</title>
<script type="text/javascript">
/*
* Fügt den Listeneinträgen Eventhandler und CSS Klassen hinzu,
* um die Menüpunkte am Anfang zu schließen.
*
* menu: Referenz auf die Liste.
* data: String, der die Nummern aufgeklappter Menüpunkte enthält.
*/
function treeMenu_init(menu, data) {
var array = new Array(0);
if(data != null && data != "") {
array = data.match(/\d+/g);
}
var items = menu.getElementsByTagName("li");
for(var i = 0; i < items.length; i++) {
items[i].onclick = treeMenu_handleClick;
if(!treeMenu_contains(treeMenu_getClasses(items[i]), "treeMenu_opened")
&& items[i].getElementsByTagName("ul").length
+ items[i].getElementsByTagName("ol").length > 0) {
var classes = treeMenu_getClasses(items[i]);
if(array.length > 0 && array[0] == i) {
classes.push("treeMenu_opened")
}
else {
classes.push("treeMenu_closed")
}
items[i].className = classes.join(" ");
if(array.length > 0 && array[0] == i) {
array.shift();
}
}
}
}
/*
* Ändert die Klasse eines angeclickten Listenelements, sodass
* geöffnete Menüpunkte geschlossen und geschlossene geöffnet
* werden.
*
* event: Das Event Objekt, dass der Browser übergibt.
*/
function treeMenu_handleClick(event) {
if(event == null) { //Workaround für die fehlenden DOM Eigenschaften im IE
event = window.event;
event.currentTarget = event.srcElement;
while(event.currentTarget.nodeName.toLowerCase() != "li") {
event.currentTarget = event.currentTarget.parentNode;
}
event.cancelBubble = true;
}
else {
event.stopPropagation();
}
var array = treeMenu_getClasses(event.currentTarget);
for(var i = 0; i < array.length; i++) {
if(array[i] == "treeMenu_closed") {
array[i] = "treeMenu_opened";
}
else if(array[i] == "treeMenu_opened") {
array[i] = "treeMenu_closed"
}
}
event.currentTarget.className = array.join(" ");
}
/*
* Gibt alle Klassen zurück, die einem HTML-Element zugeordnet sind.
*
* element: Das HTML-Element
* return: Die zugeordneten Klassen.
*/
function treeMenu_getClasses(element) {
if(element.className) {
return element.className.match(/[^ \t\n\r]+/g);
}
else {
return new Array(0);
}
}
/*
* Überprüft, ob ein Array ein bestimmtes Element enthält.
*
* array: Das Array
* element: Das Element
* return: true, wenn das Array das Element enthält.
*/
function treeMenu_contains(array, element) {
for(var i = 0; i < array.length; i++) {
if(array[i] == element) {
return true;
}
}
return false;
}
/*
* Gibt einen String zurück, indem die Nummern aller geöffneten
* Menüpunkte stehen.
*
* menu: Referenz auf die Liste
* return: Der String
*/
function treeMenu_store(menu) {
var result = new Array();;
var items = menu.getElementsByTagName("li");
for(var i = 0; i < items.length; i++) {
if(treeMenu_contains(treeMenu_getClasses(items[i]), "treeMenu_opened")) {
result.push(i);
}
}
return result.join(" ");
}
</script>
<style type="text/css">
li.treeMenu_opened ul {
display: block;
}
li.treeMenu_closed ul {
display: none;
}
</style>
<body onload="treeMenu_init(document.getElementById('menu'), '')">
<ul id="menu">
<li>erstens
<ul>
<li>A</li>
<li>B</li>
</ul>
</li>
<li>zweitens
<ul>
<li>a</li>
<li>b</li>
</ul>
</li>
</ul>
</body>
</html>
Mit onload="treeMenu_init(document.getElementById('menu'), '')" wird nach
dem Laden der Seite die Initialisierungsfunktion aufgerufen. Dabei wird ihr eine
Referenz auf das Element mit der Id "menu" übergeben.
Diese Funktion fügt dann jedem li-Elementen die Klasse treeMenu_closed hinzu, wenn
diesem nicht bereits die Klasse treeMenu_opened zugeordnet ist und es eine Unterliste
enthält.
Außerdem wird jedem li-Element noch ein onclick-Eventhandler hinzugefügt, sodass beim
Clicken auf ein li-Element die Funktion treeMenu_handleClick aufgerufen wird.
Die Funktion handleClick(event) ersetzt lediglich die Klasse treeMenu_closed durch die Klasse treeMenu_opened und umgekehrt, sodass ein geschlossener Menüpunkt beim Clicken geöffnet und ein geöffneter geschlossen wird.
Die Funktion treeMenu_store(menu) gibt einen String zurück, der den aktuellen Zustand eines
Menüs beschreibt. Der String enthält die Nummern der Menüeinträge, die offen sind.
Mit onunload="window.name = treeMenu_store(document.getElementById('menu'))" kann
man so den Zustand des Menüs beim Verlassen der Seite speichern.
Diesen String kann man der Funktion treeMenu_init(menu, data) als zweiten Parameter übergeben.
Mit onload="treeMenu_init(document.getElementById('menu'), window.name)" kann man
den Zustand auf einer anderen Seite oder nach einem reaload wieder herstellen.
Da das Menü erst beim Laden der Seite geschlossen wird, bleibt es bei abgeschaltetem Javascript einfach offen.
Das class-Attribut kann mehrere Klassen enthalten. Daher können den li-Elementen auch Klassen zugewiesen werde, wenn man das Skript verwendet. Das class-Attribut wird nicht überschrieben, sondern die Klassen treeMenu_closed und treeMenu_opend durch die jeweils andere ersetzt.
Der Funktion treeMenu_init muss eine Referenz auf eine geordnete oder ungeordnete (ol/ul) Liste übergeben werden.
Es ist problemlos möglich, mehrere Menüs zu erzeugen, indem man treeMenu_init(menu, data) mehrmals aufruft.
Die folgenden Stellen werden empfohlen, um das obige Beispiel besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.
Artikel: Wertübergabe mittels window.name
W3C: DOM 2 Events Spezifikation: Definition der Event-Schnittstelle
W3C: HTML 4 Spezifikation: Klassenattribute
SELFHTML: Objekt Referenz