Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
| de:modul:m291:learningunits:lu05:theorie:a_dom_traversal [2026/03/08 21:20] – gkoch | de:modul:m291:learningunits:lu05:theorie:a_dom_traversal [2026/03/08 23:24] (aktuell) – gkoch | ||
|---|---|---|---|
| Zeile 91: | Zeile 91: | ||
| ===== Schritt 2: querySelector vs. querySelectorAll ===== | ===== Schritt 2: querySelector vs. querySelectorAll ===== | ||
| - | {{: | + | {{: |
| Als Erstes selektieren wir die Elemente im DOM. Was passiert, wenn wir '' | Als Erstes selektieren wir die Elemente im DOM. Was passiert, wenn wir '' | ||
| Zeile 177: | Zeile 177: | ||
| - | ===== Schritt 4: DOM Traversal | + | ===== Schritt 4: Das richtige Panel finden – DOM Traversal ===== |
| {{: | {{: | ||
| - | Wenn ein Button geklickt wird, müssen wir zum benachbarten | + | Wir haben jetzt EventListener auf allen Buttons. |
| - | Der naheliegende erste Versuch | + | {{: |
| + | |||
| + | ==== DOM Traversal | ||
| + | |||
| + | Die sauberere Lösung nutzt die **Struktur des DOMs** selbst. Im HTML ist jedes Panel das direkte Geschwister-Element des zugehörigen Buttons: | ||
| + | <WRAP center round box 80%> | ||
| + | <code html> | ||
| + | <div class=" | ||
| + | <button class=" | ||
| + | <p class=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Mit '' | ||
| <WRAP center round box 80%> | <WRAP center round box 80%> | ||
| Zeile 189: | Zeile 203: | ||
| buttons.forEach((b) => { | buttons.forEach((b) => { | ||
| b.addEventListener(' | b.addEventListener(' | ||
| - | const panelElement = e.target.nextSibling; | + | const panelElement = e.target.nextElementSibling; // ✅ direkt das zugehörige Panel |
| - | panelElement.classList.add(' | + | panelElement.classList.add(' |
| - | console.log(e.target.nextSibling); | + | |
| }); | }); | ||
| }); | }); | ||
| Zeile 197: | Zeile 210: | ||
| </ | </ | ||
| - | **Warum klappt das nicht?** Im HTML-Quellcode sind Zeilenumbrüche und Leerzeichen zwischen Tags eigenständige DOM-Nodes (sog. **Text-Nodes**). | + | ==== Warum nicht einfach nextSibling? ==== |
| + | |||
| + | Der erste Versuch mit '' | ||
| - | Die richtige Methode: '' | ||
| <WRAP center round box 80%> | <WRAP center round box 80%> | ||
| <code javascript> | <code javascript> | ||
| buttons.forEach((b) => { | buttons.forEach((b) => { | ||
| b.addEventListener(' | b.addEventListener(' | ||
| - | const panelElement = e.target.nextElementSibling; // ✅ korrekt | + | const panelElement = e.target.nextSibling; |
| - | panelElement.classList.add(' | + | panelElement.classList.add(' |
| + | console.log(e.target.nextSibling); | ||
| }); | }); | ||
| }); | }); | ||
| Zeile 211: | Zeile 226: | ||
| </ | </ | ||
| - | Testen Sie: Alle Panels sollten sich jetzt öffnen lassen | + | **Warum?** Im HTML-Quellcode sind Zeilenumbrüche und Leerzeichen zwischen Tags eigenständige DOM-Nodes (sog. **Text-Nodes**). '' |
| - | + | ||
| - | ==== Übersicht: Traversal-Eigenschaften ==== | + | |
| <WRAP center round box 80%> | <WRAP center round box 80%> | ||
| Zeile 224: | Zeile 237: | ||
| </ | </ | ||
| - | ===== Schritt 5: Alle anderen Panels schliessen | + | Testen Sie: Alle Panels sollten sich jetzt öffnen lassen – aber noch nicht schliessen. |
| + | |||
| + | ===== Schritt 5 & 6: Zum finalen Script | ||
| {{: | {{: | ||
| - | Beim klassischen Accordion soll immer nur ein Panel offen sein. Vor dem Öffnen des geklickten Panels schliessen | + | Jetzt bauen wir den Code Schritt für Schritt zum finalen Script aus. Drei Dinge müssen noch gelöst werden: |
| - | <WRAP center round box 80%> | + | - Alle anderen Panels schliessen, wenn eines geöffnet wird |
| - | <code javascript> | + | - Ein geöffnetes Panel beim erneuten Klick wieder schliessen |
| - | buttons.forEach((b) => { | + | |
| - | | + | |
| - | const panelElement = e.target.nextElementSibling; | + | |
| - | // Alle Panels schliessen | + | ==== Problem: e.target kann das falsche Element sein ==== |
| - | buttons.forEach((andererBtn) | + | |
| - | andererBtn.nextElementSibling.classList.remove(' | + | |
| - | andererBtn.setAttribute(' | + | |
| - | }); | + | |
| - | // Geklicktes Panel öffnen | + | Wenn der Button ein Icon enthält |
| - | panelElement.classList.add('open'); | + | |
| - | }); | + | |
| - | }); | + | |
| - | </ | + | |
| - | </ | + | |
| - | + | ||
| - | Testen Sie: Ein Panel öffnen schliesst nun alle anderen. | + | |
| - | + | ||
| - | Das '' | + | |
| - | + | ||
| - | + | ||
| - | ===== Schritt 6: e.target vs. e.currentTarget ===== | + | |
| - | + | ||
| - | Unser Code funktioniert – hat aber noch eine versteckte Schwachstelle. Sobald wir Icons innerhalb des Buttons hinzufügen (z.B. als Pseudo-Elemente via CSS, was wir beim Styling noch tun), kann '' | + | |
| <WRAP center round box 80%> | <WRAP center round box 80%> | ||
| Zeile 262: | Zeile 257: | ||
| | '' | | '' | ||
| | '' | | '' | ||
| + | </ | ||
| + | |||
| + | Die Lösung: Wir lesen den Button immer via '' | ||
| + | |||
| + | ==== Das finale Script ==== | ||
| + | <WRAP center round box 80%> | ||
| <code javascript> | <code javascript> | ||
| - | // Im finalen Projekt: Button enthält ein SVG-Icon via CSS ::after | + | const buttons |
| - | // <button class=" | + | |
| - | buttons.forEach((b) => { | + | buttons.forEach((button) => { |
| - | | + | |
| + | const btn = e.currentTarget; | ||
| + | const panelElement = btn.nextElementSibling; | ||
| + | const panelIsOpen | ||
| - | | + | |
| - | // → Kann das :: | + | buttons.forEach((andererBtn) => { |
| - | + | andererBtn.nextElementSibling.classList.remove(' | |
| - | console.log(e.currentTarget); | + | |
| - | | + | |
| + | // Dieses Panel öffnen – aber nur wenn es vorher geschlossen war | ||
| + | if (!panelIsOpen) { | ||
| + | panelElement.classList.add(' | ||
| + | btn.setAttribute(' | ||
| + | } | ||
| }); | }); | ||
| }); | }); | ||
| Zeile 281: | Zeile 289: | ||
| </ | </ | ||
| + | **Was passiert bei jedem Klick?** | ||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | - Das innere '' | ||
| + | - Die '' | ||
| + | |||
| + | Das '' | ||