200922Okt

jQuery-Tutorial: Selektoren, Filter und Traversing

Im zweiten Teil meiner jQuery-Tutorialreihe beschäftigen wir uns mit Selektoren, Filter und DOM-Begrifflichkeiten, die man für die Arbeit mit jQuery benötigt. Wie bei meinem Einsteiger-Tutorial empfiehlt sich zum Experimentieren mit dem JavaScript-Framework die Online-Testumgebung JS Bin.
Zu Beginn dieses Workshops will ich die Begrifflichkeiten klären, welche die DOM-Struktur betreffen. Die Begriffe sind unabhängig von jQuery und sollen dem besseren Verständnis von DOM-Elementen dienen.

  • Englisch:
  • + parent (= ancestor)
  • + child (= descendant)
  • - child (= descendant)
  • - child (= descendant / sibling)
  •  
  • Deutsch:
  • + Elternteil (= Vorfahre)
  • + Kind (= Nachkomme)
  • - Kind (= Nachkomme)
  • - Kind (= Nachkomme / Geschwister)
  •  
  • Auf DOM-Struktur bezogen:
  • + <div> (= Vorfahre)
  • + <span> (= Nachkomme von div)
  • - <p> (= Nachkomme von span)
  • - <a> (= Nachkomme von span / Geschwister von p)

Ein DOM-Element steht also immer in Relation zu anderen Elementen. Elternteile sind automatisch Vorfahren und Kind-Elemente haben somit immer ein Elternteil, der gleichzeitig auch den Vorfahren des Elements darstellt. Wird die DOM-Struktur mithilfe eines Selektors durchlaufen, spricht man von "Traversing" — der Vorgang kann durch Filter oder spezifizierte DOM-Angaben optimiert werden. Die Geschwindigkeit der Selector-Engine eines Frameworks ist bei größeren HTML-Dokumenten wichtig. Messen und analysieren lässt sich die Performance von JavaScript-Operationen bei TaskSpeed oder Mozillas Dromaeo.

Um die verschiedenen Methoden zum Auswählen von DOM-Elementen schrittweise erklären zu können, werde ich alle Beispiele auf folgendes HTML-Konstrukt anwenden:

  • Quelltext
  • <head>…</head>
  • <body>
  • <div class="level0">Text0
  • <div class="klasse">Text1a</div>
  • <div class="level1">Text1b
  • <span id="level2">Text2a</span>
  • <span>Text2b</span>
  • <div class="klasse">Text2c</div>
  • </div>
  • <div>Text1c</div>
  • <span>Text1d</span>
  • </div>
  • </body>

Der Code kann einfach in JS Bin im "HTML"-Tab eingefügt werden — zum Editieren des Quelltextes muss die Textfläche per Doppeklick aktiviert werden. Die bestehenden Elemente im <body>-Bereich sollten gelöscht werden. Mit der ESCAPE-Taste wird der Editier-Modus verlassen. Nachdem man in der linken oberen Ecke bei "Include Library" die jQuery-Bibliothek ausgewählt hat, kann der "JavaScript"-Reiter aktiviert werden. Der dort bestehende Code muss ebenfalls entfernt werden. Um das Aussehen unseres HTML-Konstruktes festzulegen, werden wir alle CSS-Eigenschaften nun mit jQuery erstellen — dazu kann der folgende Code in das "JavaScript"-Feld von JS Bin eingefügt werden (natürlich kann alles auch offline in Test-Dateien per Editor erstellt werden):

  • Quelltext
  • $("div, span").css({
  • "padding": "15px",
  • "border": "2px red solid",
  • "display": "block"
  • });
  •  
  • $("span").css("background", "blue");

Ich werde den Code gleich analysieren. Der komplette HTML- und JS-Code sollte dann in JS Bin etwa so aussehen:

JS Bin

Nun kann im "Output"-Tab die fertige Ausgabe betrachtet werden, die der folgenden Abbildung entsprechen sollte.

JS Bin

Nun zurück zu unserem jQuery-Code: In der ersten Zeile werden zwei Selektoren für die allgemeinen DOM-Elemente div und span verwendet und per Komma aneinandergereiht:

  • $("div, span").css({

Mit der CSS-Funktion ".css()" werden in den darauf folgenden Zeilen die verschiedenen CSS-Attribute festgelegt. Da hier mehrere Eigenschaften angewendet werden, müssen geschweifte Klammern ({…}) und Doppelpunkte in der CSS-Funktion verwendet werden. Wichtig sind zudem die Kommata, die am Ende einer Zeile stehen müssen, damit die nachfolgende Zeile von der Funktion ausgewertet wird — in der letzten Zeile darf kein Komma stehen, da es sich um die letzte Anweisung handelt. Im direkten Vergleich unterscheidet sich der jQuery-Code also nur an wenigen Stellen vom gleichbedeutenden CSS-Code:

  • jQuery:
  • $("div, span").css({
  • "padding": "15px",
  • "border": "2px red solid",
  • "display": "block"
  • })
  •  
  • CSS-Äquivalent:
  • div, span {
  • padding: 15px;
  • border: 2px red solid;
  • display: block;
  • }

Neben den doppelten (wahlweise auch einfachen) Anführungszeichen, die in jQuery bei der Zuweisung von Werten und Attributen verwendet werden müssen, unterscheidet sich der Code lediglich von den Kommata bzw. Semikolons. Die letzte Zeile unsere jQuery-Codes färbt alle span-Elemente zusätzlich blau ein.

  • $("span").css("background", "blue");

In jQuery gibt es nun viele Möglichkeiten, gezielt DOM-Elemente anzuwählen. Neben allgemeinen HTML-Elementen (div, span, p, etc.), IDs (#id), Klassen (.class) und dem Stern-Selektor (*) stellen hierarchische Selektoren die einfachste Methode dar, verschaltete oder untergeordnete Elemente anzusprechen. Zu den hierarchischen Funktionen zählen Folgende:

  • $("Vorfahre Nachkomme")
  • $("Elternteil > Kind")
  • $("Element ~ Geschwister")
  • $("Element + Nachfolger")

Die erste Funktionalität (ancestor descendant) verändert also alle Nachkommen (untergeordnete Elemente) eines DOM-Elements — das bedeutet, dass auch die Nachkömmlinge von verschachtelten Elementen angesprochen werden. Alle folgenden jQuery-Snippets müssen am Ende des bestehenden jQuery-Codes eingefügt werden, damit die CSS-Modifikationen zuletzt angewendet werden. Hier noch einmal das HTML-Konstrukt zur Erinnerung:

  • Quelltext
  • <div class="level0">Text0
  • <div class="klasse">Text1a</div>
  • <div class="level1">Text1b
  • <span id="level2">Text2a</span>
  • <span>Text2b</span>
  • <div class="klasse">Text2c</div>
  • </div>
  • <div>Text1c</div>
  • <span>Text1d</span>
  • </div>

Die folgende Zeile würde alle untergeordneten span-Elemente des div-Elements mit der Klasse "level0" einfärben:

  • Resultat
  • $(".level0 span").css("background", "green");

Die span-Elemente von dem verschachtelten div-Container mit der Klasse "level1" wären also auch betroffen. Möchte man nur die span-Objekte der Klasse "level1" modifizieren, muss der Code so lauten:

  • Resultat
  • $(".level1 span").css("background", "green");

Nun stellt sich die Frage, wie man ausschließlich die span-Elemente der Klasse "level0" verändert. Hierzu lässt sich der "Kind"-Selektor (child) einsetzen, welcher nur die Kind-Elemente der ersten untergeordneten Stufe eines Elements anspricht. Der Code dazu:

  • Resultat
  • $(".level0 > span").css("background", "green");

Die dritte Möglichkeit der oben genannten Selektor-Funktionen stellt die "Geschwister"-Funktion (sibling) dar: Geschwister-Elemente befinden sich auf der gleichen Ebene wie das vorangestellte Ziel-Element. Mit der Funktion, die über die Tilde (~) aufgerufen wird, werden alle Objekte nach dem definierten DOM-Element aufgegriffen. Ein Beispiel:

  • Resultat
  • $(".klasse ~ div").css("background", "green");

Damit würden alle nachfolgenden div-Container (und auch deren Inhalt) auf der Ebene des Objekts mit der Klasse "klasse" umgefärbt werden — das letzte Element der Ebene, ein span-Element, wäre also nicht betroffen.
Der letzte hierarchische Selektor wäre die Nachfolger-Funktion (next), mit der das nachfolgende Element eines spezifizierten DOM-Objekts verändert werden kann:

  • Resultat
  • $("div + span").css("background", "green");

Hiermit werden alle span-Elemente modifiziert, die direkt auf ein div-Container folgen und auf gleicher Ebene liegen: Das span-Objekt mit der ID "level2", welches dem div-Block mit der Klasse "level1" untergeordnet ist, wäre also nicht betroffen. Das nachfolgende Element muss zudem direkt auf das gewünschte DOM-Element folgen — befindet sich ein Element dazwischen, greift der Selektor nicht.

Neben den hierarchischen Funktionen gibt es in jQuery Filter, durch die Elemente noch genauer spezifiziert werden können. Ein Filter wird mit einem Doppelpunkt an das Ziel-Element angehängt. Zusätzlich gibt es noch Attribut-Filter, die durch eckige Klammern ([…]) deklariert werden. Dazu später mehr.
Eine vollständige Liste aller Selektoren findet man in der offiziellen jQuery-Dokumentation. Um beispielsweise auf das erste vorkommende DOM-Objekt zuzugreifen, lässt sich der ":first"-Filter benutzen:

  • Resultat
  • $("span:first").css("background", "green");

Nun würde das erste span-Element der gesamten DOM-Struktur eingefärbt werden. Der ":first"-Filter lässt sich auch in untergeordneten Elementen anwenden, so dass explizit das erste Element eines anderen DOM-Elements angesprochen wird:

  • Resultat
  • $(".level0 span:first").css("background", "green");

Diese Zeile hätte den gleichen Effekt wie "$("span:first")", da das span-Element mit der ID "level2" auch das erste auftretende span-Objekt in dem Container "level0" ist. Selektorfunktionen und Filter lassen sich nun kombinieren — eben haben wir bereits den "ancestor descendant"-Selektor mit dem ":first"-Filter kombiniert. Wenn wir stattdessen nun den "Child"-Selektor benutzen, wird das erste vorkommende span-Element auf der ersten untergeordneten Ebene des anfangs definierten Elements (".level0") angesprochen — das span-Objekt mit der ID "level2" wird ignoriert, da es sich bereits auf der zweiten untergeordneten Ebene befindet. Der Code hierzu wäre also:

  • Resultat
  • $(".level0 > span:first").css("background", "green");

In jQuery gibt es zusätzliche Traversing-Funktionen wie ".parent()", ".children()" oder".siblings()", die als direkte Auswahlfunktion genutzt werden können. Beispiel:

  • Resultat
  • $(".klasse").parent().css("background", "green");
  • $(".level1").children().css("background", "yellow");
  • $("#level2").siblings().css("background", "red");

Die DOM-Modifikationen werden nacheinander ausgeführt, weshalb drei Farben sichtbar sind, obwohl der umgebende div-Block grün eingefärbt wurde.

Jedes Objekt in der DOM-Struktur ist einem anderen Element untergeordnet und hat somit eine Index-Nummer (Array). Das div-Objekt mit der Klasse "level0" ist auf oberster Ebene angesiedelt und ist somit das erste Element des body-Elements — wie in einem Array beginnen die Index-Nummern nicht mit 1, sondern mit 0. Möchte man nun also den dritten vorkommenden div-Container auswählen, muss der ":eq()"-Filter (equals) in Verbindung mit der Index-Nummer "2" verwendet werden.

  • Resultat
  • $("div:eq(2)").css("background", "green");

Das dritte div-Element in der gesamten DOM-Struktur wäre also das Objekt mit der Klasse "level1". Auch hier gelten wieder die Regeln der Vererbung — möchte man untergeordnete Elemente eines DOM-Objekts verändern, muss dem Ziel-Element das Eltern-Element vorangestellt werden. Hier noch ein Beispiel mit dem Stern-Selektor:

  • Resultat
  • $(".level1 *:eq(2)").css("background", "green");

Mit dem Stern-Selektor wird schlicht jedes gültige HTML-Element überprüft (wie in CSS). Diese Zeile wählt also das dritte Element in dem Container mit der Klasse "level1" aus: In diesem Fall das div-Objekt mit der Klasse "klasse".

  • <div class="level0">                 = Global: Index-Nummer 0
  • <div class="klasse"></div>       = Global: Index-Nummer 1
  • <div class="level1">             = Global: Index-Nummer 2
  • <span id="level2"></span>    = Index-Nummer 0 (Global: 3)
  • <span></span>                = Index-Nummer 1 (Global: 4)
  • <div class="klasse"></div>   = Index-Nummer 2 (Global: 5)
  • </div>
  • <div></div>                      = Global: Index-Nummer 6
  • <span></span>                    = Global: Index-Nummer 7
  • </div>

Die globalen Index-Nummern beziehen sich aud das body-Element, da nur in diesem Bereich sichtbare HTML-Elemente dargestellt werden können. Soll das letzte Element der DOM-Struktur mit der Index-Nummer 7 verändert werden, wäre dieser Code falsch:

  • $("*:eq(7)").css("background", "green");

Ohne vorangestelltes Eltern-Element wird das gesamte Dokument (document) als Ursprung verstanden: Dazu zählen alle existierenden Tags wie html, head, script und so weiter. Der eben genannte Code würde also das body-Element grün einfärben. Um nun das letzte DOM-Element des body-Containers anzusprechen, muss der Code lauten wie folgt:

  • Resultat
  • $("body *:eq(7)").css("background", "green");

Damit wäre folglich das letzte span-Element des body-Containers eingefärbt. In jQuery werden die Index-Nummern auch dazu benutzt, alternierende Farben ("odd" und "even") auf Elemente (z.B. Tabellen oder Listen) anzuwenden. Allerdings muss man sich als Entwickler nicht mehr um die Nummern kümmern, da jQuery die Arbeit übernimmt:

  • Resultat
  • $(".level0 *:even").css("background", "green");

Bei der Einstellung "even" handelt es sich um die ungerade Reihen, beginnend bei der ersten Reihe. Fortlaufend werden die Reihen (nicht die Index-Nummern) 3, 5, 7 und so weiter ausgewählt, während mit dem "odd"-Filter die Reihen 2, 4, 6, etc. verändert werden.

Schließlich gibt es in jQuery noch Attribut-Filter, der nicht mit der Attribut-Funktion ".attr()" zu verwechseln ist. Der Attribut-Filter kann dazu benutzt werden, Elemente mit bestimmten Attributen wie name, rel, title oder auch id auszuwählen. Ich habe in dem Beispiel ein span-Element bewusst mit einer ID versehen, da wir diese nun mit dem Attribut-Filter nutzen werden:

  • Resultat
  • $("span[id]").css("background", "green");

Somit wird also der komplette DOM-Baum nach span-Elementen durchsucht, die eine ID besitzen. Den Attributen kann zusätzlich ein Operator vorangestellt werden, um die Angaben weiter spezifizieren zu können. So lässt sich mit dem "="-Operator ein Attribut auf einen bestimmten Wert überprüfen:

  • Resultat
  • $("div[class='level1']").css("background", "green");

Nun wird lediglich der div-Container mit der Klasse "level1" eingefärbt — zu beachten sind die einfachen Anführungszeichen, durch die der Wert maskiert werden muss. Natürlich können auch Filter kombiniert werden, so dass folgende Ausdrücke möglich sind:

  • Resultat
  •  
  • $("div[class*='k']:contains('2c')").css("background", "green");

Ausgewählt wird damit der div-Block, dessen Klassenbezeichnung den Buchstaben "k" enthält und der Inhalt den Text "2c" beinhaltet.
Damit wären wir am Ende dieses Tutorials. Wie man sehen kann, bietet jQuery umfassende Möglichkeiten, die DOM-Struktur eines HTML-Dokuments effizient zu durchlaufen.

RSS-Feed abonnieren