Oliver E.: Event-Handling, Ein-/Ausschalten von Listen...

Hallo Liebes Forum und Mitlesende,

ich habe ein (Verständnis-)Problem mit den Auswirkungen einer Funktion.

HTML-Schnipsel:

  
  <aside>  
    <nav>  
      <ul id="navi_touch">  
        <li>NAVIGATION:</li>  
      </ul>  
      <ul class="navigation">  
        <li><a href="#">Text</a></li>  
        <li><a href="#">Text</a></li>  
      </ul>  
      <ul class="navigation">  
        <li><a href="#">Text</a></li>  
        <li><a href="#">Text</a></li>  
      </ul>  
      <ul class="navigation">  
        <li><a href="#">Text</a></li>  
      </ul>  
    </nav>  
</aside>  

Die 3 Listen mit der Klasse "navigation" werden mittels document.getElementsByClassName selektiert und sollen beim "Klick" auf die Liste (mit der ID navi_touch) ein- bzw. ausgeblendet werden.

Beim Laden der Seite werden die Listen vorab ausgeschaltet, so dass der erste Klick das Einschalten bewirken soll.

Das betreffende CSS dazu:

.navigation  
{  
  display: block;  
}  
  
.aus  
{  
display: none;  
}

Das JavaScript dazu:

  
window.addEventListener("load", start, false);  
  
function start()  
{  
	  
/*	Nach dem Laden der Seite wird die Navigation ausgeblendet*/  
	var navi_elemente = document.getElementsByClassName('navigation');  
	var anzahl = navi_elemente.length;  
	for(i = 0; i < anzahl; i++)  
		{  
			document.getElementsByClassName('navigation')[0].className = "aus";  
		}  
	/*Anschließend werden die Event-Handler zum Ein- und Ausblenden der Navigation eingebunden*/  
	var naviElement = document.getElementById('navi_touch');  
	naviElement.addEventListener("click", navi_ein, false);  
	naviElement.addEventListener("click", navi_aus, false);  
}  
  
  
function navi_ein()  
{  
	var navi_elemente = document.getElementsByClassName('aus');  
	var anzahl = navi_elemente.length;  
	for(i = 0; i < anzahl; i++)  
		{  
			document.getElementsByClassName('aus')[0].className = "navigation";  
		}  
}  
  
function navi_aus()  
{  
	var navi_elemente = document.getElementsByClassName('navigation');  
	var anzahl = navi_elemente.length;  
	for(i = 0; i < anzahl; i++)  
		{  
			document.getElementsByClassName('navigation')[0].className = "aus";  
		}  
}  

Das Ausblenden beim Start (geladen) der Seite "works as intended" in den Browwsern (IE, Chrome, FF).

Jetzt das Problem. Sobald ich/man auf die Liste zum Einblenden klickt wird die Funktion "navi_ein" ausgeführt, abgearbeitet und die Listen eingeblendet --- ABER sobald die Funktion fertig ist, wird der ursprüngliche Zustand wieder hergestellt und die Listen sind wieder ausgeblendet. (Schleifendurchlauf mit Firebug peu a peu getestet).

Warum ist das so bzw. wieso verhält sich JavaScript so? Und wie kann ich das Problem beheben?

P.s.

  • Das Ausblenden vorab kommt nur beim Einsatz von kleinen Endgeräten zum Einsatz
  • JQuery Einsatz ist nicht gewollt

Lieben Gruß und Danke für Eure Zeit

Oliver

  1. Hallo,

    <ul id="navi_touch">
            <li>NAVIGATION:</li>
          </ul>

    Eine Liste mit nur einem Listenpunkt ist keine Liste...

    for(i = 0; i < anzahl; i++)
    {
    document.getElementsByClassName('navigation')[0].className = "aus";
    }

    so eine for-schleife verwendest du dreimal ohne dann den Zähler i zu verwenden und überhaupt sieht das komisch aus: das getByClass gibt dir doch das Array, das macht man nicht innerhalb der schleife.

    Gruß
    Kalk

    1. for(i = 0; i < anzahl; i++)  
        {  
        	document.getElementsByClassName('navigation')[0].className = "aus";  
        }  
      

      so eine for-schleife verwendest du dreimal ohne dann den Zähler i zu verwenden und überhaupt sieht das komisch aus: das getByClass gibt dir doch das Array, das macht man nicht innerhalb der schleife.

      Gruß
      Kalk

      Hallo Kalk,

      dann lass es mal durchlaufen und sehe selbst, was passiert wenn du statt ...[0]... ...[i]... einsetzt. Die Schleife bricht ab, nachdem i = 1 ist. Das Array ist ein "LiveNode-Objekt" und verändert seine Anzahl nach jedem Durchgang. Dies müsste man also Rekursiv durchlaufen, damit habe ich mich aber noch nicht beschäftigt. Letztendlich komme ich mit der obigen Schreibweise aber ans Ziel. Inwieweit das mit der Problemlösung zusammenhängt, entzieht sich mir gerade. Vielleicht magst du Abhilfe verschaffen?

      Gruß
      Oliver

      1. Hallo Oliver,

        wenn du getElementsByClassName selektierst und den ClassName änderst wirds kompliziert ;-))

        Versuche es damit:

        <script>  
        function start(e) {  
            var navi_elemente = document.getElementsByTagName('ul');  
            for(var i = 0; i < navi_elemente.length; i++) {  
                if (navi_elemente[i].className === 'navigation') {  
                    navi_elemente[i].className = 'aus';  
                }  
                else if (navi_elemente[i].className === 'aus') {  
                    navi_elemente[i].className = 'navigation';  
                }  
            }  
            var naviElement = document.getElementById('navi_touch');  
            naviElement.addEventListener('click', start);  
        }  
        window.addEventListener('DOMContentLoaded', start);  
        </script>
        

        gruesse qx

        1. Hallo Oliver,

          wenn du getElementsByClassName selektierst und den ClassName änderst wirds kompliziert ;-))

          gruesse qx

          Hallo qx,

          danke für den Workaround. Habs jetzt noch nicht ausprobiert, sieht auf den ersten Blick aber schon "flüssig" aus.

          viele Grüße und n schönen Abend.

          Oliver

      2. Hallo,

        dann lass es mal durchlaufen und sehe selbst, was passiert wenn du statt ...[0]... ...[i]... einsetzt. Die Schleife bricht ab, nachdem i = 1 ist. Das Array ist ein "LiveNode-Objekt" und verändert seine Anzahl nach jedem Durchgang. Dies müsste man also Rekursiv durchlaufen, damit habe ich mich aber noch nicht beschäftigt.

        Hat jetzt mit deiner Ausgangsfrage weniger zu tun, aber ein paar generelle Anmerkungen:

        Rekursion ist etwas anderes. Von Rekursion spricht man, wenn Funktionen sich selbst erneut aufrufen. Oder wenn eine verschachtelte, mehrdimensionale Datenstruktur (z.B. ein Baum) durchlaufen wird und dabei in der Verschachtelung »abgestiegen« wird. Eine NodeList ist nicht verschachtelt.

        Du meinst eher, dass man die Liste von hinten durchläuft, oder solange keine Einträge mehr da sind. Beispiel:

        var elements = document.getElementsByClassName('foo');  
        var element;  
        while (element = elements[0]) {  
          element.className = '';  
        }
        

        Das ist im Grunde das, was du schon machst, nur kompakter mit einer while-Schleife geschrieben.

        Oder von hinten durchlaufen:

        var elements = document.getElementsByClassName('foo');  
        var i = elements.length;  
        while (i--) {  
          var element = elements[i];  
          element.className = '';  
        }
        

        Das Operieren auf einer Live-NodeList versucht man i.d.R. zu vermeiden, weil es sehr Performance-intensiv ist. Bei jeder Änderung eines Elements muss der Browser intern die Abfrage document.getElementsByClassName('foo') wiederholen, um die NodeList zu aktualisieren.

        Man kann die Live-NodeList in einen normalen Array umwandeln:

        var elements = Array.prototype.slice.call( document.getElementsByClassName('foo') );

        Dann ändert sich der Inhalt des Arrays nicht, wenn man Elemente davon ändert. Der Browser muss also nicht mit jeder Änderung die NodeList neu berechnen.

        Mathias

        1. Hallo Mathias,

          Rekursion ist etwas anderes. Von Rekursion spricht man, wenn Funktionen sich selbst erneut aufrufen. Oder wenn eine verschachtelte, mehrdimensionale Datenstruktur (z.B. ein Baum) durchlaufen wird und dabei in der Verschachtelung »abgestiegen« wird. Eine NodeList ist nicht verschachtelt.

          Du meinst eher, dass man die Liste von hinten durchläuft, oder solange keine Einträge mehr da sind. Beispiel:

          das ist genau das was ich gemeint und womit ich mich falsch ausgedrückt habe. Danke für die Berichtigung.

          Das Operieren auf einer Live-NodeList versucht man i.d.R. zu vermeiden, weil es sehr Performance-intensiv ist...

          Man kann die Live-NodeList in einen normalen Array umwandeln:

          var elements = Array.prototype.slice.call( document.getElementsByClassName('foo') );

          Dann ändert sich der Inhalt des Arrays nicht, wenn man Elemente davon ändert. Der Browser muss also nicht mit jeder Änderung die NodeList neu berechnen.

          Mathias

          Dass müsste mich auf jeden Fall weiterbringen. Danke für deine Ausführungen.

          viele Grüße und n schönen Abend.

          Oliver

  2. Hallo!

    naviElement.addEventListener("click", navi_ein, false);
    naviElement.addEventListener("click", navi_aus, false);

    Sobald ich/man auf die Liste zum Einblenden klickt wird die Funktion "navi_ein" ausgeführt, abgearbeitet und die Listen eingeblendet --- ABER sobald die Funktion fertig ist, wird der ursprüngliche Zustand wieder hergestellt und die Listen sind wieder ausgeblendet.

    Klar, weil BEIDE Funktionen nacheinander beim Klick ausgeführt werden. Du musst hier nur die aufrufen, die zum aktuellen Status passt. Also ENTWEDER navi_ein ODER navi_aus. Den Status kannst du in einer Variable speichern, oder indem du das Vorhandensein der Klasse abfragst.

    Davon ganz abgesehen ist eine so komplexe JavaScript-Lösung nicht nötig. Toggle einfach eine Klasse beim gemeinsamen Elternelement und lass den Rest CSS erledigen.

    <nav>  
    <p><button id="navi_touch">Öffne/schließe Navigation</button></p>  
    <ul></ul>  
    <ul></ul>  
    <ul></ul>  
    </nav>
    
    document.getElementById('navi_touch').addEventListener('click', function() {  
      document.querySelector('nav').[link:https://developer.mozilla.org/en-US/docs/Web/API/Element.classList@title=classList.toggle]('opened');  
    });
    
    nav ul { display: none; }  
    nav.opened ul { display: block; }
    

    Das ist schon alles. Noch besser wäre es, den Inhalt zugänglich zu verstecken. display: none sorgt nämlich dafür, dass die Navigation für Screenreader und andere Zugangstechniken nicht lesbar ist.

    Grüße
    Mathias