Abschnitt 10 · Lektion 2
Elemente finden
Um mit einem Element zu arbeiten, musst du es erst haben. Dafür gibt es zwei Methoden, und wenn du den CSS-Kurs kennst, kennst du auch schon ihre Sprache.
Zwei Methoden reichen
<header id="kopf">Kopfzeile</header>
<div class="karte"><h2>Erster</h2><button>Mehr</button></div>
<div class="karte hervor"><h2>Zweiter</h2><button>Mehr</button></div>
<div class="karte"><h2>Dritter</h2><button>Mehr</button></div> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
h2 {
font-size: 1.2rem;
margin: 1.25rem 0 0.5rem;
}
button,
input,
select,
textarea {
font: inherit;
}
button {
padding: 0.4rem 0.8rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
background: #fafaf9;
cursor: pointer;
} // querySelector nimmt einen CSS-Selektor und liefert
// das erste passende Element.
console.log("erste Karte:", document.querySelector(".karte").textContent);
console.log("mit zwei Klassen:", document.querySelector(".karte.hervor").textContent);
console.log("mit id:", document.querySelector("#kopf").textContent);
// querySelectorAll liefert alle als NodeList.
const karten = document.querySelectorAll(".karte");
console.log("Anzahl:", karten.length);
console.log("zweite:", karten[1].textContent);
// Jeder Selektor aus dem CSS-Kurs geht, auch die
// zusammengesetzten.
console.log("Nachfahr:", document.querySelector(".karte h2").textContent);
console.log("Kind:", document.querySelectorAll(".karte > button").length);
console.log("Nicht:", document.querySelectorAll(".karte:not(.hervor)").length); querySelector nimmt einen CSS-Selektor und liefert das erste passende Element. querySelectorAll liefert alle als NodeList.
Das Entscheidende steht im ersten Satz: es ist ein CSS-Selektor. Alles, was du im CSS-Kurs zum Ansprechen von Elementen gelernt hast, gilt hier unverändert. Klassen, id, Attribute, Nachfahren, >, :not, alles.
Deshalb sind das auch die einzigen beiden, die du brauchst. Es gibt ältere Methoden für Sonderfälle, aber keinen Fall, den diese beiden nicht abdecken.
Selektor ist nicht gleich Text. In querySelector(".karte") steht der Punkt für „Klasse”, genau wie im Stylesheet. Wer ihn weglässt, sucht nach einem Element namens karte, und das gibt es nicht.
Und woran siehst du, was ein Element überhaupt hat? Auf einem breiten Bildschirm sitzt in der Browser-Leiste ein Fadenkreuz, das ist der Inspector. Schalt ihn ein und fahr über die zweite Karte im Beispiel: oben im Auskunftsstreifen steht dann div.karte.hervor. Das ist der Tag, dahinter die id mit Raute und alle Klassen mit Punkt, also genau die Schreibweise, die querySelector erwartet. Über der Kopfzeile steht entsprechend header#kopf. Ein Klick friert die Auswahl ein, damit sie nicht wegläuft, sobald du den Zeiger bewegst.
Nach dem Klick steht im Streifen ein kleiner Chip mit $0, und der ist kein Schmuck: In der Eingabezeile der Console heißt das festgehaltene Element $0. Tipp dort $0, und die Antwort ist <div class="karte hervor">. $0.className antwortet "karte hervor", und document.querySelector(".karte.hervor") === $0 antwortet true: Der Selektor, den du aus dem Streifen abgelesen hast, findet genau das Element, auf das du gezeigt hast. Zeigen und Fragen gehören so zusammen, und in den Entwicklerwerkzeugen jedes Browsers heißt dasselbe Element ebenfalls $0. Ohne festgeklickte Auswahl ist es undefined.
Der ältere Weg
<header id="kopf">Kopfzeile</header>
<p>Ein Absatz</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} // Diese beiden finden dasselbe Element.
console.log("mit Raute:", document.querySelector("#kopf").textContent);
console.log("ohne Raute:", document.getElementById("kopf").textContent);
// Der Unterschied ist genau ein Zeichen, und zwar an der
// Stelle, an der man es vergisst.
console.log("falsch herum:", document.getElementById("#kopf"));
// Es gibt auch getElementsByClassName und getElementsByTagName,
// beide älter und beide mit einer Eigenheit: sie liefern eine
// lebende Liste, die sich mitändert.
const alt = document.getElementsByTagName("p");
console.log("vorher:", alt.length);
document.body.append(document.createElement("p"));
console.log("nachher:", alt.length);
// querySelectorAll liefert dagegen eine Momentaufnahme.
const neu = document.querySelectorAll("p");
document.body.append(document.createElement("p"));
console.log("querySelectorAll bleibt:", neu.length); getElementById gibt es weiterhin, und es ist die schnellste Art, ein Element über seine id zu finden. Für Seiten in der Größenordnung, um die es hier geht, ist der Unterschied nicht messbar.
Was man daran vergisst, ist die Raute. getElementById will die id ohne #, querySelector will sie mit. Wer beides mischt, bekommt null und keinen Hinweis darauf, woran es lag.
Daneben gibt es getElementsByClassName und getElementsByTagName, und die haben eine Eigenheit: sie liefern eine lebende Liste. Kommt später ein passendes Element dazu, taucht es dort von selbst auf. querySelectorAll liefert dagegen eine Momentaufnahme.
Das klingt nach einem Vorteil und ist meistens eine Überraschung: eine Liste, die sich beim Durchlaufen ändert, ist der klassische Weg in eine Endlosschleife. Im Kurs bleibt es deshalb bei den beiden query-Methoden.
Eine NodeList ist kein Array
<div class="karte"><h2>Erster</h2></div>
<div class="karte"><h2>Zweiter</h2></div>
<div class="karte"><h2>Dritter</h2></div> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
h2 {
font-size: 1.2rem;
margin: 1.25rem 0 0.5rem;
} const karten = document.querySelectorAll(".karte");
// Das hier geht: Länge, Index, forEach, for...of.
console.log("length:", karten.length);
console.log("Index:", karten[0].textContent);
karten.forEach((karte) => console.log("forEach:", karte.textContent));
// Und das hier geht nicht, obwohl es so aussehen sollte.
console.log("map:", typeof karten.map);
console.log("filter:", typeof karten.filter);
// Array.from macht daraus eine echte Liste.
const alsListe = Array.from(karten);
console.log("jetzt map:", alsListe.map((k) => k.textContent));
// Die drei Punkte aus Lektion 7.6 tun dasselbe.
console.log("mit Spread:", [...karten].length); Was querySelectorAll zurückgibt, sieht aus wie ein Array und ist keins. Es hat length, du kannst mit eckigen Klammern zugreifen, forEach und for...of funktionieren.
Aber map, filter und reduce gibt es dort nicht. Und das merkst du erst beim Ausführen, denn karten.map ist einfach undefined, und der Aufruf wirft dann einen TypeError.
Der Ausweg ist eine Zeile: Array.from(karten) macht daraus eine echte Liste, und danach gilt der ganze Abschnitt 6. Die drei Punkte aus Lektion 7.6 tun dasselbe: [...karten].
Den Unterschied siehst du in der Eingabezeile der Console schon an der Form der Antwort. karten antwortet mit NodeList {0: <div class="karte">, 1: <div class="karte">, 2: <div class="karte">}, geschweifte Klammern und Nummern davor. alsListe antwortet mit [<div class="karte">, <div class="karte">, <div class="karte">], eckige Klammern, wie jedes Array seit Abschnitt 6. Und karten.map antwortet undefined, alsListe.map mit ƒ map().
Diese eine Zeile ist der Grund, warum Lektion 10.6 überhaupt funktioniert. Sobald aus der NodeList ein Array geworden ist, ist die Seite auswertbar wie jede andere Datenquelle.
Einmal suchen, dann merken
<div class="karte"><h2>Erster</h2><p>Text</p></div> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
h2 {
font-size: 1.2rem;
margin: 1.25rem 0 0.5rem;
}
p {
margin: 0 0 0.75rem;
} // So nicht: dreimal dieselbe Suche.
console.log(document.querySelector(".karte h2").textContent);
console.log(document.querySelector(".karte h2").tagName);
console.log(document.querySelector(".karte h2").className);
// So: einmal suchen, in eine Variable, dann benutzen.
const ueberschrift = document.querySelector(".karte h2");
console.log(ueberschrift.textContent);
console.log(ueberschrift.tagName);
// Innerhalb eines Elements sucht man weiter, statt von
// document aus einen langen Selektor zu bauen.
const karte = document.querySelector(".karte");
console.log("in der Karte:", karte.querySelector("p").textContent);
// Das ist derselbe Gedanke wie ein Name für einen
// Zwischenschritt aus Lektion 2.8. Zum Schluss eine Gewohnheit, die den Rest dieses Abschnitts angenehmer macht.
Such ein Element einmal, gib ihm einen Namen und benutze den Namen. Dreimal document.querySelector(".karte h2") untereinander ist nicht nur dreimal dieselbe Arbeit, es ist vor allem dreimal dieselbe Stelle zum Ändern, wenn sich der Selektor einmal ändert.
Und such innerhalb eines Elements weiter, statt von document aus einen immer längeren Selektor zu bauen. karte.querySelector("p") sucht nur in dieser Karte. Das liest sich besser und es ist auch das, was du in Abschnitt 12 brauchst, wenn du zu einem geklickten Knopf die passende Karte suchst.
Es ist derselbe Gedanke wie der Name für einen Zwischenschritt aus Lektion 2.8: die Zeile wird kürzer, und der Fehler darin wird auffindbar.
Zum Mitnehmen
Alles, was du in CSS ansprechen kannst, kannst du auch hier ansprechen. Zwei Methoden reichen dafür, und du kennst die Selektoren schon.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.