Teil von SELFHTML aktuell Teil von Artikel Teil von Dynamisches HTML

Dynamisches HTML:
Aufklappbare Menüs

nach unten Autor
nach unten Hinweise zum Thema
nach unten Beispiel mit Erläuterungen
nach unten Weiterführende Links

Autor

Name: Daniel Thoma
E-Mail: E-Mail dthoma@gmx.net

Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!

nach obennach unten

Hinweise zum Thema

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.

nach obennach unten

Beispiel mit Erläuterungen

Popup-Seite 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>

Erläuterung:

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.

Menüzustand speichern und wieder herstellen

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.

Beachten Sie:

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.

nach obennach unten

Weiterführende Links

Die folgenden Stellen werden empfohlen, um das obige Beispiel besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.

bereichsübergreifende Seite Artikel: Wertübergabe mittels window.name
englischsprachige Seite W3C: DOM 2 Events Spezifikation: Definition der Event-Schnittstelle
englischsprachige Seite W3C: HTML 4 Spezifikation: Klassenattribute
bereichsübergreifende Seite SELFHTML: Objekt Referenz

Teil von SELFHTML aktuell Teil von Artikel Teil von Dynamisches HTML

© 2007 bereichsübergreifende Seite Impressum