Durch das Registrieren von Event-Handlern wird die angegebene Funktion immer dann ausgeführt, wenn das jeweilige Ereignis beim jeweiligen Element eintritt. In dieser Handler-Funktion ist es meistens nötig, auf die näheren Umstände des Ereignisses zu reagieren. Beispielsweise sind bei einem Mausklick die Koordinaten des Mauszeigers interessant oder bei einem Tastendruck die gedrückte Taste.
All diese Informationen sind im Event-Objekt gespeichert. Dieses Objekt repräsentiert das individuelle Ereignis, das der Handler gerade verarbeitet. Es bietet zahlreiche Eigenschaften mit Informationen zum Ereignis und einige Methoden, um das Verhalten des Ereignisses zu steuern. Wenn Sie bei der Ereignis-Verarbeitung diese Daten benötigen, ist der Zugriff auf das Event-Objekt die erste Aufgabe in der Handler-Funktion.
In neueren Browsern gestaltet sich dieser Zugriff einfach: Das Event-Objekt wird der Handler-Funktion automatisch als erster Parameter übergeben. Sie muss dieses nur noch entgegen nehmen. Der Parametername ist dabei frei wählbar. Üblicherweise wird der Bezeichner event
verwendet.
function handlerfunktion(event) { window.alert("Es ist ein Ereignis vom Typ " + event.type + " passiert."); }
Diesen Zugriff auf das Event-Objekt unterstützen alle relevanten Browser. Lediglich ältere Internet Explorer vor Version 9 unterstützen diese Technik nicht. Für diese Browserversionen ist eine Sonderlösung notwendig. Diese Internet-Explorer-Versionen übergeben das Event-Objekt nicht als Parameter an die Handler-Funktion, sondern stellen es unter dem globalen Objekt window.event
zur Verfügung. Auch wenn es den Anschein hat, so ist das Event-Objekt dort nicht dauerhaft gespeichert. window.event
verweist auf das Event-Objekt des Ereignisses, das gerade verarbeitet wird.
Wenn Sie die besagten älteren Browser unterstützen wollen oder müssen, ist eine Vereinheitlichung notwendig. Diese ist recht einfach: Wir prüfen, ob der Funktion ein Parameter übergeben wurde und somit die lokale Variable event
einen Wert hat. Falls dies zutrifft, können wir den Parameter direkt verwenden. Andernfalls speichern wir in der Variable event
einen Verweis auf window.event
.
function handlerfunktion(event) { // Vereinheitlichung: if (!event) { // Korrektur für den Internet Explorer < 9 event = window.event; } // Browserübergreifender Zugriff: window.alert("Es ist ein Ereignis vom Typ " + event.type + " passiert."); }
Nach der Vereinheitlichung steht das Event-Objekt browserübergreifend in einer Variable zu Verfügung.
Mit if (!event)
wird geprüft, ob der Wert der Variablen bei einer Umwandlung in den Typ Boolean den Wert false
ergibt. Eine solche Abfrage ist hier möglich, weil event
entweder ein Objekt enthält oder, falls der Handler-Funktion nichts übergeben wird, mit dem Wert undefined
initialisiert wird. Dieser ergibt in Boolean umgewandelt false
.
Eine gleichwertige Kurzschreibweise nutzt den Oder-Operator ||
. Intern funktioniert dies wie die besagte if
-Anweisung. Das Ziel ist ebenfalls vereinheitlichter Zugriff auf das Event-Objekt über die Variable event
.
function handlerfunktion(event) { event = event || window.event; window.alert("Es ist ein Ereignis vom Typ " + event.type + " passiert."); }
Der Oder-Operator ||
überprüft, ob der Wert links true
ergibt, also der Funktionsparameter event
bei der Umwandlung in den Typ Boolean den Wert true
ergibt. Wenn dies der Fall ist, ergibt der Ausdruck den Wert von event
und es wird event = event
ausgeführt. Dabei passiert nichts, die Variable wird mit ihrem eigenen Wert überschrieben.
Interessant ist der andere Fall, wenn kein Parameter übergeben wurde. Dann ist das Ergebnis des Ausdrucks der Wert rechts vom ||
-Operator. Dann wird event = window.event
ausgeführt. Durch die Oder-Verzweigung ist das Event-Objekt in jedem Fall in der Variable event
gespeichert.
Beide der Schreibweisen erfüllen dieselbe Funktion. Die erste ist klarer und leicht verständlich, die zweite ist kürzer, erfordert jedoch das Verständnis des ||
-Operators.
Viele Ereignisse im Dokument haben eigentümliche Auswirkungen. Ein Beispiel: Wenn der Anwender auf einen Link klickt, so tritt ein click
-Ereignis ein. Das bringt den Browser dazu, dem Link zu folgen und zum angegebenen Linkziel (der URL) zu navigieren. Das bedeutet, dass der Browser die Ressource vom Webserver herunterlädt und anzeigt. Ein weiteres Beispiel: Wenn der Anwender den Absende-Buttons eines Formulars aktiviert, so tritt ein submit
-Ereignis an. Das bringt den Browser dazu, das Formular an den Webserver zu übertragen.
Der Browser behandelt also standardmäßig gewisse Ereignisse und führt die sogenannte Standardaktion (englisch default action) aus, ohne dass der Seitenautor eine entsprechende JavaScript-Logik definiert hat.
Beim Unobtrusive JavaScript versieht man z.B. bestehende Links mit einer JavaScript-Logik. Die ursprüngliche Funktionalität des Links will man dann zumeist unterdrücken: Beim Klick auf den Link soll nur das Script ausgeführt werden, nicht mehr das Linkziel angesprungen werden.
Angenommen, wir haben folgenden Link:
<a href="bilder/bild.jpg" id="vollbildlink">Bild in Originalgröße ansehen</a>
Mit JavaScript soll diesem Link nun ein click
-Handler hinzugefügt werden, der das verlinkte Bild im aktuellen Dokument einblendet, anstatt das Bild einzeln anzuzeigen. Wie dieses Einblenden umgesetzt wird, interessiert uns an dieser Stelle nicht, sondern nur das Unterdrücken der Standardaktion.
Im traditionellen Event-Handling wird die Standardaktion unterdrückt, indem die Handler-Funktion false
als Ergebnis zurückgibt. Am Ende der Funktion wird daher die Anweisung return false;
notiert.
function zeigeVollbild() { // Blende das Bild ein, auf das der Link zeigt. // … (an dieser Stelle uninteressant) … // Unterdrücke schließlich die Standardaktion: return false; } // Registriere Event-Handler document.getElementById("vollbildlink").onclick = zeigeVollbild;
Beachten Sie, dass mit der return
-Anweisung die Funktion beendet wird. Code, der auf diese Anweisung folgt, wird nicht ausgeführt. Es sei denn, die return
-Anweisung ist z.B. durch eine if
-Anweisung gekapselt und wird nicht in jedem Fall ausgeführt.
Wenn Sie kein return false
notieren, führt der Browser automatisch die Standardaktion aus. Sie müssen ihn also nicht mit einem return true
oder auf andere Art dazu bringen – sie können die Standardaktion lediglich verhindern.
Neben return false
gibt es modernere Techniken, um die Standardaktion zu verhindern. Der DOM-Standard, auf den wir später noch zu sprechen kommen, bietet eine Methode namens preventDefault
beim Event-Objekt, mit der sich die Standardaktion unterdrücken lässt. Das obige Beispiel könnte auch folgendermaßen aussehen:
function zeigeVollbild(event) { // Browserübergreifender Zugriff auf das Event-Objekt if (!event) event = window.event; // Unterdrücke die Standardaktion durch Aufruf von preventDefault: event.preventDefault(); // Fehler im Internet Explorer < 9! // Blende das Bild ein, auf das der Link zeigt. // … (an dieser Stelle uninteressant) … };
Der Vorteil von preventDefault
ist, dass es im Gegensatz zu return false
auch mitten in der Handler-Funktion aufgerufen werden kann, ohne sie gleich zu beenden. Das Beispiel demonstriert dies.
Der Nachteil ist, dass der Internet Explorer diese standardisierte Methode erst ab Version 9 kennt. Er hat jedoch eine gleichwertige Boolean-Eigenschaft des Event-Objekts namens returnValue
. Weist man dieser den Wert false
zu, so wird die Standardaktion unterbunden. Um auch ältere Internet Epxlorer zu unterstützen, kann die Existenz der preventDefault
-Methode abgefragt werden. Existiert diese nicht, wird alternativ die Eigenschaft returnValue
gesetzt:
function zeigeVollbild(event) { if (!event) event = window.event; // Existiert die Methode preventDefault? if (event.preventDefault) { // Dann rufe sie auf. // DOM-Standard event.preventDefault(); } else { // Andernfalls setze returnValue. // Microsoft-Alternative für Internet Explorer < 9 event.returnValue = false; } // Blende das Bild ein, auf das der Link zeigt. // … (an dieser Stelle uninteressant) … };
Diese Vorgehensweise sei hier der Vollständigkeit halber erwähnt. Wenn sie Ihnen unnötig kompliziert erscheint, so können Sie sich mit dem herkömmlichen return false
zufrieden geben, das die Aufgabe hinreichend erfüllt. Sie müssen allerdings beachten, dass mit return false
die Handler-Funktion beendet wird.
Bisher haben wir erfahren, dass Ereignisse bei bestimmten Elementen passieren. Dort können wir sie überwachen, indem wir Handler registrieren. Tritt das Ereignis bei diesem Element ein, wird die Handler-Funktion ausgelöst.
Die Wirklichkeit ist etwas komplizierter. Die Verarbeitung eines Ereignisses verläuft in drei Phasen, die nacheinander durchlaufen werden. Davon lernen wir nun eine zweite kennen. Die dritte ist weniger wichtig. Sie wird unter Capturing beschrieben.
Ein Ereignis passiert bei einem Element, dem sogenannten Zielelement (englisch target element), und löst dort alle Handler aus, die für das entsprechende Ereignis registriert wurden. Diese bereits bekannte Phase nennt sich entsprechend Ziel-Phase.
Mit dieser Phase ist die Ereignis-Verarbeitung nicht zuende, denn anschließend steigt das Ereignis im DOM-Elementbaum auf: Zum Eltern-Element, dann zu dessen Eltern-Element und so weiter. Dieser Vorgang nennt sich Bubbling. Das Wort ist abgleitet von bubble, Englisch für Blase. Wie Luftblasen im Wasser steigen Events auf.
Beim Aufsteigen werden die entsprechenden Handler auch beim Eltern-Element des Zielelements ausgeführt. Dann bei dessen Eltern-Element und so weiter, bis das Ereignis schließlich den obersten document
-Knoten erreicht hat.
Das Ereignis bewegt sich also nach oben im Elementbaum, durchläuft alle Vorfahrenelemente des Zielelements und löst auf diesem Weg alle entsprechenden Handler aus. Dieser Vorgang wird entsprechend Bubbling-Phase genannt.
Das mag zunächst sehr abstrakt klingen, aber der Sinn und die Funktionsweise des Bubblings sind schnell erfasst. Nehmen wir folgenden HTML-Code:
<p id="absatz"> Dies ist ein Beispiel-Element mit einem <strong id="wichtig"> wichtigen Text </strong>. </p>
Nehmen wir ferner an, dass das p
-Element auf traditionelle Weise einen click
-Handler bekommt:
function clickHandler() { window.alert('Der Absatz wurde geklickt!'); } document.getElementById('absatz').onclick = clickHandler;
Das p
-Element wird vom Browser als rechteckige Box dargestellt. Bei einem Klick irgendwo in diese Box soll die Handler-Funktion ausgeführt werden.
Wenn man auf das Wort »Beispiel-Element« klickt, ist das p
-Element das Zielelement des Ereignisses. Wenn man hingegen auf »wichtigen Text« klickt, so ist das strong
-Element das Zielelement des Ereignisses, nicht das p-Element! Denn dieser Text liegt zuvorderst im strong
-Element und nur indirekt im p
-Element. Aus Sicht des DOM-Baumes ist der Text ein Textknoten, der ein Kindknoten des strong
-Elementknotens ist.
Nichtsdestoweniger erwartet man, dass ein Klick auf die Box des strong
-Elements ebenfalls den click
-Handler beim p
-Element auslöst. Und dies ist auch der Fall – dafür sorgt das Bubbling! Das Ereignis, das ursprünglich beim strong
-Element passiert ist, steigt nämlich auf, sodass der Handler des p
-Elements ausgeführt wird.
Das Bubbling ist also meist erwünscht, damit bei einem Element Ereignisse überwacht werden können, die ursprünglich bei Kindelementen passieren. Wenn Sie aber nicht damit rechnen, dass Ereignisse aufsteigen, so kann das Bubbling zu einiger Verwirrung führen und Sie werden sich wundern, woher plötzlich gewisse Ereignisse stammen.
Nicht alle Ereignisse steigen auf, denn bei manchen Ereignissen wäre es kontraproduktiv, wenn sie zentrale Handler auslösen würden.
Infolge des Bubblings kann sich das Zielelement, bei dem das Ereignis ursprünglich passiert ist, von dem verarbeitenden Element unterscheiden, dessen Handler aufgerufen wird. Letzteres Element liegt dann im DOM-Elementbaum oberhalb des Zielelements.
In vielen Fällen will man in der Handler-Funktion auf beide beteiligten Elemente zugreifen, sofern sie sich unterscheiden.
Beginnen wir mit dem Zugriff auf das verarbeitende Element: Das Element kann in der Handler-Funktion über das Schlüsselwort this
angesprochen werden. Die Handler-Funktion wird nämlich im Kontext dieses Elementobjekts ausgeführt.
Das obige Beispiel wird wieder aufgegriffen:
<p id="absatz"> Dies ist ein Beispiel-Element mit einem <strong id="wichtig"> wichtigen Text </strong>. </p>
Dem Absatz wird wieder ein click
-Handler zugewiesen:
function clickHandler() { window.alert("Element vom Typ " + this.nodeName + " wurde geklickt!"); } document.getElementById("absatz").onclick = clickHandler;
Innerhalb der Handler-Funktion können wir über this
auf das p
-Element zugreifen. Im Beispiel wird auf dessen Eigenschaft nodeName
ausgegeben, welche den Elementnamen P
enthält.
Der DOM-Standard sieht eine andere Zugriffsweise vor: Das Event-Objekt hat eine Eigenschaft namens currentTarget
, die das verarbeitende Element enthält. So wird die Eigenschaft genutzt:
function clickHandler(event) { window.alert("Element vom Typ " + event.currentTarget.nodeName + " wurde geklickt!"); }
Der Internet Explorer kennt Eigenschaft currentTarget
erst ab Version 9. Das besagte this
ist in älteren Internet-Explorer-Versionen die einzige Möglichkeit, auf das fragliche Element zuzugreifen.
Für den Zugriff auf das Zielelement besitzt das Event-Objekt die Eigenschaft target
. Diese kennen alle modernen Browser.
target
ab Version 9. Ältere Versionen kennen eine gleichwertige Eigenschaft namens srcElement
. Mithilfe einer Fähigkeitenweiche können Sie browserübergreifend das Zielelement ansprechen. Wir kennen dieses Vorgehensweise bereits vom Zugriff auf das Event-Objekt.
function clickHandler(event) { if (!event) event = window.event; // Zugriff auf das Zielelement if (event.target) { // W3C-DOM-Standard var target = event.target; } else { // Microsoft-Alternative für Internet Explorer < 9 var target = event.srcElement; } window.alert( 'Das Ereignis passierte ursprünglich beim Element ' + target.nodeName + ' und wird vom Element ' + this.nodeName + ' verarbeitet.'); ); }
Falls die Eigenschaft target
des Event-Objekts gefüllt ist, speichern wir deren Wert in der lokalen Variable target
. Andernfalls, das betrifft den Internet Explorer, wird die Eigenschaft srcElement
verwendet.
Wie beim Zugriff auf das Event-Objekt erlaubt der ||
-Operator eine Kurzschreibweise. So kommen wir zu einem Schema, dem viele Handler-Funktionen entsprechen:
function clickHandler(event) { // Vereinheitlichung von Event-Objekt und Zielelement event = event || window.event; var target = event.target || event.srcElement; // Nutzlast window.alert( 'Das Ereignis passierte ursprünglich beim Element ' + target.nodeName + ' und wird vom Element ' + this.nodeName + ' verarbeitet.'); ); }
Das folgende Beispiel ist ein div
-Element, an dem ein mouseover
-Handler registriert wurde. Darin sind verschiedene Elemente verschachtelt. Bewegen Sie die Maus über deren Boxen, so werden mouseover
-Ereignisse an diesen ausgelöst. Sie steigen im DOM-Baum auf und werden beim gemeinsamen div
-Vorfahrenelement verarbeitet.
Unter dem interaktiven Bereich werden das Zielelement und das verarbeitende Element ausgegeben. Das verarbeite Element ist immer dasselbe, nämlich das div
-Element.
Ein einfacher Textabsatz ohne weiteres.
Hier erfolgt die Ausgabe.
Der JavaScript-Quellcode dieses Beispiels sieht so aus:
var targetArea = document.getElementById('target-area'); var output = document.getElementById('target-output'); targetArea.onmouseover = function (event) { event = event || window.event; var target = event.target || event.srcElement; output.innerHTML = 'Das <code>mouseover</code>-Ereignis passierte ursprünglich beim Element ' + '<strong><code>' + target.nodeName + '</code></strong> und wird vom Element ' + '<strong><code>' + this.nodeName + '</code></strong> verarbeitet.'; };
Der Code geht davon aus, dass zwei Elemente mit den IDs target-area
und target-output
definiert sind und das Script später im Dokument notiert ist, sodass es Zugriff auf die Elemente hat.
Es gibt Fälle, in denen das Bubbling nicht gewünscht ist. Beispielsweise wenn zwei verschachtelte Elemente dasselbe Ereignis überwachen, aber nur der Handler des inneren Elements aktiv werden soll. Sie können deshalb das weitere Aufsteigen des Ereignisses im Elementbaum verhindern.
Folgendes bekannte Beispiel mit verschachtelten Elementen soll dies illustrieren:
<p id="absatz"> Dies ist ein Beispiel-Element mit einem <strong id="wichtig"> wichtigen Text </strong>. </p>
Sowohl beim strong
- als auch beim p
-Element wird ein click
-Handler registriert:
function absatzKlick() { window.alert('Klick auf das p-Element'); } document.getElementById('absatz').onclick = absatzKlick; function wichtigKlick() { window.alert('Klick auf das strong-Element'); } document.getElementById('wichtig').onclick = wichtigKlick;
Bei einem Klick auf die Fläche des strong
-Elements (»wichtigen Text«) werden beide Handler-Funktionen ausgeführt, denn das Ereignis steigt vom strong
-Element zum p
-Element auf.
Dieses Aufsteigen können Sie in der Handler-Funktion wichtigKlick
verhindern. Der DOM-Standard definiert dafür die Methode stopPropagation
(englisch für stoppe die Verbreitung des Ereignisses) beim Event-Objekt. Ein Aufruf dieser Methode unterbricht den Event-Fluss und verhindert damit das weitere Aufsteigen.
Der Internet Explorer kennt die Methode stopPropagation
erst ab Version 9. Ältere Versionen verfügen über eine gleichwertige Boolean-Eigenschaft beim Event-Objekt. Diese trägt den Namen cancelBubble
(englisch für breche das Aufsteigen ab). Weisen Sie dieser Eigenschaft den Wert true
zu, um das Aufsteigen des Ereignisses abzubrechen.
Wieder einmal nutzen wir eine Fähigkeitenerkennung, die die Verfügbarkeit der standardisierten Methode stopPropagation
prüft und im Fehlerfalle auf die Microsoft-Alternative cancelBubble
zurückfällt.
Die Handler-Funktion wichtigKlick
sieht dann so aus:
function wichtigKlick(eventObekt) { if (!event) event = window.event; // Stoppe das Aufsteigen des Ereignisses if (event.stopPropagation) { // W3C-DOM-Standard event.stopPropagation(); } else { // Microsoft-Alternative für Internet Explorer < 9 event.cancelBubble = true; } window.alert('Das Aufsteigen des Ereignisses wird unterbunden!'); }
Damit können verschachtelte Elemente denselben Ereignistyp überwachen, im Beispiel click
. Obwohl das eine Element in dem anderen enthalten ist und üblicherweise in dessen Grenzen dargestellt wird, übernimmt es die Ereignis-Verarbeitung selbstständig. Der Handler des äußeren Elements, im Beispiel absatzKlick
, wird nur bei Klicks ausgeführt, die auf seine Fläche zielen, ausgenommen die Fläche des inneren Elements.