In diesem Kapitel setzen wir JavaScript im Kontext von Webseiten ein. Man nennt das auch Frontend-Programmierung im Gegensatz zur Backend-Programmierung, wo es um die Programmierung eines Servers geht.

Wie Sie wissen, kann man sich alle HTML-Elemente als Knoten in einem Baum vorstellen. Man nennt diesen Baum auch Document Object Model oder kurz DOM. Das DOM ist unser wichtigstes Tool, um mit Hilfe von JavaScript Elemente der Webseite zu manipulieren.

Denken Sie auch daran, dass Sie viele der folgenden Beispiele direkt im Browser ausprobieren können, indem Sie die JavaScript-Konsole aktivieren. In Chrome sieht das so aus:

JavaScript-Konsole in Chrome

(Löschen Sie die Historie von eventuellen Fehlermeldungen einfach mit CTRL-K).

8.1 JavaScript einbinden

Wie bekommen Sie Ihren Code auf die Webseite? Ähnlich wie bei CSS gibt es verschiedene Möglichkeiten. Auch hier ist die Option, eine externe Datei anzulegen, die Methode der Wahl.

8.1.1 Im HTML: Code-Schnipsel (script)

Sie können sowohl im head als auch im body jederzeit ein Element script einbauen, wo Sie JavaScript-Code einstellen.

Der folgende Code lässt ein Fenster aufgehen, das erst weggeklickt werden muss, bevor es weitergeht:

<script>
  alert('hello');
</script>

8.1.2 Im Element: Events

Sie können jedes HTML-Element interaktiv machen, indem Sie auf bestimmte Events reagieren, z.B. auf das Anklicken eines Elements:

<p onclick="console.log('CLICK')">
  Klick mich an!
</p>

Einige der möglichen Events sind:

Sie schreiben i.d.R. eine eigene JavaScript-Funktion, die Sie mit onclick aufrufen. Sie können dieser Funktion mit this das angeklickte Element als Parameter mitgeben. Dazu schreiben Sie

<p onclick="myHandler(this)">
  Klick mich an!
</p>

Sie müssen entsprechend eine Funktion haben:

<script>
  function myHandler(el) {
    alert('You clicked ' + el.tagName);
  }
</script>

Eine ausführliche Liste von Events finden Sie bei w3schools.

8.1.3 In eigener Datei (script)

Wie bei CSS ist es üblich, sämtlichen JavaScript-Code in eine separate Datei zu packen. Dazu wird ein Unterverzeichnis js angelegt und dort erstellen Sie zunächst eine leere Datei z.B. namens script.js.

Wenn Sie zwei HTML-Seiten haben, könnte Ihre Verzeichnisstruktur anschließend so aussehen:

css/style.css
js/script.js
index.html
kontakt.html

In der HTML-Datei legen Sie ähnlich wie bei der CSS-Datei einen Link an, verwenden dazu allerdings das Element script:

<script src="js/script.js"></script>

Heutezutage ist es üblich, diesen Link am Ende des Body anzulegen. Grund ist, dass durch das Laden der JavaScript-Datei nicht das Laden der sichtbaren HTML-Elemente verzögert werden soll.

8.2 JavaScript und DOM

8.2.1 DOM: Document Object Model

Wie anfangs gesagt, ist das Document Object Model unser Werkzeug, um auf die Webseite zuzugreifen. Es handelt sich um eine Baumstruktur von Objekten, die das HTML-Dokument wiederspiegeln.

Ein Baum besteht aus Knoten (engl. nodes). Es gibt genau einen Wurzelknoten (engl. root). Alle Knoten außer der Wurzel haben genau einen Elternknoten (engl. parent). Jeder Knoten kann einen Kindknoten (engl. child) oder mehrere Kindknoten (engl. children) oder auch kein Kinder haben.

Beispiel für einen DOM-Baum

(In der Abb. sind Attribute nicht dargestellt, da sie zu den blauen Elementknoten dazu gehöhren.)

Das heißt, es gibt die folgenden Arten von Knoten:

Wichtig sind die Elemente, denn sie entsprechen einem HTML-Element.

Jedes Element (blau) ist ein JavaScript-Objekt und hat somit Eigenschaften und Methoden.

Sie können über die Variable document auf die Wurzel und über seine Eigenschaften auf die Kindknoten zugreifen.

Um auf den HTML-Code (als Text) eines DOM-Elements zuzugreifen, verwendet man innerHTML, z.B.

document.documentElement.innerHTML


Es ist auch möglich, mit innerText nur den reinen Text (wie oben, aber ohne HTML-Markup) zu erhalten:

document.documentElement.innerText


Weitere nützliche Eigenschaften sind:

Jedes DOM-Objekt hat jetzt auch Methoden, um innerhalb des Objekts zu suchen, z.B. nach Klassen.

8.2.2 Selektion

Ähnlich wie in CSS möchte man auf bestimmte Elemente der Webseite zugreifen, um auszulesen oder zu ändern. Wie wissen, dass man den Elementtyp, Klassen oder IDs verwenden kann, um auf Elemente zuzugreifen. Darüber hinaus kann man über Beziehungen zwischen Knoten (Eltern-Kind, Geschwister) im Baum "navigieren".

Die Selektion funktioniert über Methoden, die man auf einem Knoten aufruft. Diese Methoden geben entweder einen einzelnen Knoten zurück oder eine Liste von Knoten, wie nennen sie NodeList. Eine NodeList funktioniert exakt so wie ein Array. Wichtig ist noch, dass bei solchen Selektionen die zurückgegebenen NodeListen die Elemente in der gleichen Reihenfolge enthalten, wie sie im HTML-Dokument definiert sind.

Elementselektor

Mit "Element" meinen wir einen HTML-Elementtyp wie p oder h1 oder em.

Mit der Methode getElementsByTagName() können Sie z.B. alle Elemente vom Typ p im Dokument auswählen:

document.getElementsByTagName('p')


Wir erhalten NodeList. Sie bekommen den ersten Absatz daher mit

document.getElementsByTagName('p')
[0]

Da der erste Absatz wieder ein DOM-Objekt ist, können Sie dort wieder nach Elementen suchen:

document.getElementsByTagName('p')
[0].getElementsByTagName('strong')


Wollen Sie die gesamte Knotenliste durchlaufen, verwenden Sie eine For-Schleife. Achten Sie darauf, dass die Methode getElementsByTagName() Zeit kostet, d.h. sie sollte unnötig innerhalb einer Schleife aufgerufen werden. Merken Sie sich die Liste vorab mit einer Variablen:

ls = document.getElementsByTagName('p')


Jetzt können Sie die Schleife bauen, um z.B. den Text aller Absätze auszugeben:

for (i = 0; i < ls.length; i++) {
  console.log(i + ": " + ls[i].innerText);
}

Klassenselektor

Mit der Methode getElementsByClassName() können Sie z.B. alle Elemente mit Klasse "foo" auswählen:

document.getElementsByClassName('foo')


Wir erhalten auch hier eine NodeList.

ID-Selektor

Mit der Methode getElementById() können Sie z.B. das Element mit ID "textblock" auswählen:

document.getElementById ('textblock')


Hier bekommen wir ein einzelnes Element zurück, falls ein solches existiert. Ansonsten wird null zurückgegeben.

Selektion über CSS

Die Methode querySelectorAll() erlaubt Ihnen, CSS-Selektoren zu verwenden, die Sie von den CSS-Regeln her kennen. Zum Beispiel alle Elemente ul innerhalb von nav:

document.querySelectorAll('nav ul')

Hier erhalten wir eine NodeList zurück.

Mit querySelector() bekommen wir den ersten Knoten zurück, der "passt":

document.querySelector('nav ul')

8.2.3 Elemente ändern

Inhalte

Um den Inhalt eines Elements zu ändern, können Sie die Eigenschaft innerText wie eine Variable benutzen, der Sie einen neuen Inhalt zuweisen:

document.getElementsByTagName('p')
[1].innerText = "Doktor Foo was here."

Falls Sie auch HTML einbetten wollen, verwenden Sie innerHTML:

document.getElementsByTagName('p')[1].innerHTML = "Doktor <strong>foo</strong> was <a href='http://hs-augsburg.de'>here</a>."

ID, Klassen, Attribute

Oft möchten Sie ID oder Klasse eines Elements ändern, um ein anderes Stückchen CSS auf das Element anzuwenden.

IDs funktionieren wie Eigenschaften - Sie können sie einfach setzen. Hier wird der ID des Body-Elements auf "special" gesetzt (egal, ob oder wie der ID vorher gesetzt war):

document.body.id = "special"

Wollen Sie einen ID ganz entfernen, müssen Sie removeAttribute() anwenden:

document.body.removeAttribute('id')

Klassen werden über setAttribute() geändert:

document.body.setAttribute('class', 'foo')

Wenn das Element vorher bereits Klassen angehörte, werden diese so gelöscht. Um eine Klasse hinzuzufügen, muss man also schreiben:

cls = document.body.getAttribute('class')
if (cls) {
  document.body.setAttribute('class', cls + ' foo')
} else {
  document.body.setAttribute('class', 'foo')
}

Sie können mit setAttribute() natürlich beliebige Attribute setzen, z.B. einen Handler einbauen:

document.getElementsByTagName('p')[1].setAttribute('onclick', 'alert("hello")');

Mit hasAttribute() können Sie prüfen, ob ein Attribut existiert. Alternativ können Sie getAttribute() verwenden und prüfen, ob Sie null zurückbekommen.

Styling

Um das Styling (also das CSS) eines Elements zu ändern, verwendet man die Eigenschaft style:

document.body.style.backgroundColor = 'yellow'

Dabei ist zu beachten, dass CSS-Eigenschaft mit Bindestrich wie "background-color" in CamelBack-Schwreibweise geändert werden müssen, also "backgroundColor".

Löschen

Man kann auch mit der Funktion remove ein Element löschen:

document.getElementsByTagName('p')[1].remove()

Das Element wird aus dem DOM-Baum entfernt.

8.2.4 Im Baum navigieren

Häufig möchten Sie von einem Element auf das nächste Geschwisterelement oder auf den Elternknoten springen. Dazu gibt es Eigenschaften.

Mit parentNode greifen Sie auf den Elternknoten zu:

document.body.parentNode.nodeName

Mit children auf alle Kinder:

document.body.children

Mit nextElementSibling auf alle Kinder:

document.body.nextElementSibling

Sie müssen aufpassen, ob Sie auch die Texte als "Knoten" zählen möchten oder nicht. Mit nextSibling bekommen Sie evtl. einen Textknoten (#text), mit nextElementSibling bekommen Sie das nächste HTML-Element (was i.d.R. auch gewünscht ist).

8.3 Übungen

(a) Akkordeon

Versuchen Sie, ein "Akkordeon" zu programmieren. Das heißt, bei einem Click auf einer Überschrift wird jeweils der darunter liegende Abschnitt angezeigt. Die anderen Abschnitte werden versteckt.

Beispieldatei Akkordeon

Sie können die folgende Basisdatei verwenden: akkordeon_js.html