Die Ereignisbehandlung in JavaScript unter Firefox 49
Ereignisse
Bestimmte Vorgänge werden als Ereignisse bezeichnet. Die JavaScript -Implementation kann auf solche Ereignisse reagieren, so daß sie bestimmte Reaktionen hervorrufen können, die teilweise vom Programmieren festgelegt werden können. Eine für ein Ereignis festgelegte Reaktionen nennen wir auch eine Ereignis-Behandlung.
Das Klick-Ereignis
Wird etwas angeklickt, so nennt man dies auch ein Klick-Ereignis.
Das Hinzufügen eines Ereignisempfängers
Eine Funktion die aufgerufen wird, wann immer ein bestimmtes Ereignis eintritt, nennen wir auch einen Ereignisempfänger.
Ein Ereignisempfänger kann mit Hilfe der Funktion »addEventListener« zu einem Ereignis hinzugefügt werden.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
a = window.document.getElementById( "a" );
a.addEventListener( "click", ()=> window.alert( "click" ) );
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [click]
- Navigation zu http://example.invalid
Im obigen Programm wird die Funktion »addEventListener« des Verzeichnisses »a«, welches das a-Element der Webseite repräsentiert, aufgerufen. Das erste Argument »"click"« gibt den Typ des Ereignisses an, für das der Empfänger hinzugefügt werden soll. Das zweite Argument gibt die Funktion an, die beim Auftreten des Ereignisses aufgerufen werden soll.
Beim Klicken auf den Link erscheint nun zuerst eine Meldungsfenster mit dem Text »click«, erst danach wird zur angeklickten Webseite navigiert.
Das Ereignisverzeichnis
Der Ereignisempfänger erhält ein Verzeichnis als Argument übergeben, das Ereignisverzeichnis. Das Ereignisverzeichnis enthält verschiedene Einträge zu dem Ereignis, wie beispielsweise »type« mit einer Zeichenfolge, welche den Typ des Ereignisses angibt.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
a = window.document.getElementById( "a" );
a.addEventListener( "click", event => window.alert( event.type ) );
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [click]
- Navigation zu http://example.invalid
Das mehrfache Hinzufügen eines Empfängers
Es ist auch möglich, mehrfach einen Empfänger für ein Ereignis festzulegen. Alle festgelegten Empfänger werden dann in der Reihenfolge ihres Hinzufügens aufgerufen.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
a = window.document.getElementById( "a" );
a.addEventListener( "click", event => window.alert( "A" ));
a.addEventListener( "click", event => window.alert( "B" ));
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [A]
- [B]
- Navigation zu http://example.invalid
Das Sperren der Weiterleitung
Ein Ereignisempfänger kann es mit Hilfe der stopImmediatePropagation-Funktion des erhaltenen Ereignisses auch verhindern, daß weitere Empfänger aktiviert werden.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
a = window.document.getElementById( "a" );
a.addEventListener
( "click", event =>{ window.alert( "A" ); event.stopImmediatePropagation(); });a.addEventListener( "click", event => window.alert( "B" ));
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [A]
- Navigation zu http://example.invalid
Das Aufsteigen eines Ereignisses
Wenn ein Link angeklickt wurde, der in einem Absatz enthalten ist, dann ist es nicht ganz einfach zu sagen, ob nun der Absatz oder der Link angeklickt wurde. Man könnte auch sagen, daß beide angeklickt wurden.
Entsprechend werden zuerst die Ereignisempfänger des inneren und dann auch noch die des äußeren Elements aufgerufen.
Wenn »event.eventPhase« gleich »2« ist, so ist das Ereignis gerade beim innersten vom Ereignis betroffenen Element angelangt, ist es gleich »3« so ist das Ereignis beim Aufsteigen in umgebende Elemente.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p id="p"><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
p = window.document.getElementById( "p" );
a = window.document.getElementById( "a" );
p.addEventListener( "click", event =>{ window.alert( event.eventPhase ); });
a.addEventListener( "click", event =>{ window.alert( event.eventPhase ); });
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [2]
- [3]
- Navigation zu http://example.invalid
Das Festlegen der Phase
Die Ereignisbehandlung findet eigentlich in drei Phase statt: Bei der ersten Phase werden die Elemente, die das innerste betroffene Element enthalten, von außen nach innen „absteigend“ benachrichtigt. Bei der zweiten Phase wird das innerste Element benachrichtig, und bei der dritten Phase werden die umgebenden Elemente dann noch einmal von innen nach außen „aufsteigend“ benachrichtigt.
Beim Anklicken des Links, werden der Absatz und der Link in der folgenden Reihenfolge benachrichtigt:
- Der Absatz in der absteigenden Phase
- Der Link
- Der Absatz in der aufsteigenden Phase
Durch ein drittes Argument von »addEventListener« können wir festlegen, ob die angegebenen Funktion in der ersten Phase aktiviert werden soll. Wenn dieses Argument fehlt, so gilt es als falsch. Wenn das dritte Argument wahr ist, so wird eine Aktivierung der Funktion für die absteigende Phase hinzugefügt, sonst für die aufsteigende.
In dem folgenden Programm wird ein Ereignisempfänger für den Absatz und die erste Phase festgelegt, weswegen die angegebene Funktion nun vor der für den Link aktiviert wird, wenn der Link angeklickt wird.
Wenn »event.eventPhase« gleich »1« ist, so ist das Ereignis gerade beim Absteigen.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p id="p"><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
p = window.document.getElementById( "p" );
a = window.document.getElementById( "a" );
p.addEventListener( "click", event =>{ window.alert( event.eventPhase ); }, true );
a.addEventListener( "click", event =>{ window.alert( event.eventPhase ); });
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [1]
- [2]
- Navigation zu http://example.invalid
Das Sperren der Ereignisweitergabe
Durch »stopPropagation()« wird die Weiterleitung eines Ereignisses an Ereignisempfänger anderer Elemente unterbunden (sei es beim Absteigen oder erst beim Aufsteigen). Die Ereignisempfänger desselben Elements werden aber noch benachrichtigt (im Gegensatz zu »stopImmediatePropagation()«).
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p id="p"><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
p = window.document.getElementById( "p" );
a = window.document.getElementById( "a" );
p.addEventListener( "click", event =>{ window.alert( "P" ); event.stopPropagation(); }, true );
p.addEventListener( "click", event =>{ window.alert( "P1" ); }, true );
a.addEventListener( "click", event =>{ window.alert( "A" ); });
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [P]
- [P1]
- Navigation zu http://example.invalid
Die Standardbehandlung eines Ereignisses
Für einige Ereignisse gibt es eine Standardbehandlung, beim Anklicken eines Links wird beispielsweise zu der Adresse des Links navigiert.
Das Unterbinden der Standardbehandlung
Für einige Ereignisse ist »cancelable« gleich »true«. In diesem Fall kann die Standardbehandlung mit »preventDefault()« unterbunden werden. Dies beeinträchtigt aber nicht die Weiterleitung an andere JavaScript -Ereignisempfänger.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p><a id="a" href="http://example.invalid">example</a></p>
<pre><code><script>/*<![CDATA[*/
a = window.document.getElementById( "a" );
a.addEventListener( "click", event => { window.alert( event.cancelable ); event.preventDefault(); });
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [click]
- (keine Navigation)
Das onclick-Attribut
Mit einem onclick-Attribut eines Elements kann man eine Sequenz angeben, die beim Anklicken eines Elements ausgewertet werden soll.
Main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css"></style></head><body>
<p><a href="http://example.invalid" onclick="alert( event.type ); f(); return false;">example</a></p>
<pre><code><script>/*<![CDATA[*/
f = () => window.alert( "f!" );
/*]]>*/</script></code></pre></body></html>
- Protokoll
- [click]
- [f!]
- (keine Navigation)
Es gibt verschiedene Möglichkeiten, eine Behandlung für ein Ereignis in JavaScript festzulegen, beispielsweise die mit »addEventListener« und die mit einem on-Attribut. In diesem Kurs konzentrieren wir und hauptsächlich auf ein Möglichkeit, nämlich die mit »addEventListener«. Weil man aber beim Lesen anderer Quellen mit dem on-Attribut konfrontiert werden könnte, folgen hier einige Erläuterungen dazu:
- Der angegebene Code wird in einer Umgebung ausgeführt, in der das Ereignis durch den Ausdruck »event« angegeben werden kann.
- Der angegebene Code wird in einer Art von Funktion ausgeführt, so daß auch »return false;« darin verwendet werden kann. Dies wirkt sich wie eine Auswertung der Sequenz »event.stopPropagation(); event.preventDefault();« aus. Die Rückgabe von »false« aus einer Funktion, die mit »addEventListener« registriert wurde, hat hingegen nicht diese Wirkung.
Die Verwendung von »addEventListener« hat folgende Vorteile gegenüber der Verwendung eines on-Attributs.
- »addEventListener« erlaubt es, Ereignisempfänger festzulegen, wenn der HTML-Quelltext so weit wie möglich von JavaScript -Bestandteilen freigehalten werden soll.
- »addEventListener« erlaubt es, mehrfach damit Ereignisempfänger hinzuzufügen
(Die Definition der Funktion »f« wird oben erst gelesen nachdem »f()« schon verwendet wurde. Der Zeitpunkt des Aufrufs jener Funktion liegt aber meist nach dem Zeitpunkt zu dem die Definition gelesen wurde, so daß die Funktion zum Zeitpunkt ihres Aufrufs wahrscheinlich meist definiert ist.)
Aus der freien Wildbahn Das Sperren des Kontextmenüs
Wir habe Webseiten beobachtet, welche das Kontextmenü, das normalerweise das Kopieren einer Adresse erlaubt, sperren. Das heißt, daß der Benutzer das Kontextmenü zu einem Link nicht mehr verwenden kann (falls er JavaScript aktiviert hat). Auch, wenn diese Technik nicht unbedingt zur Nachahmung empfohlen wird, zeigen wir hier wie so etwas verwirklicht werden kann.
In dem untenstehenden Beispielskript geschieht folgendes:
- Der Anker wird an einen globalen Eintrag »a« gebunden.
- Es wird eine Ereignisfunktion »cm« definiert, die weiter unten ausführlicher erklärt werden wird.
- Es wird festgelegt, daß die Ereignisfunktion »cm« im Falle des Ereignisses »contextmenu« des Ankers »a« aktiviert wird, also wenn ein Kontextmenü zu dem Anker vom Benutzer verlangt wird.
main.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
<head><meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Main</title><style type="text/css">#span { font-size: 500% }
</style></head><body>
<h1>Nicht kopierbarer Link</h1>
<p><a id="a" href="http://example.com">Link</span></p>
<pre><code><script>/*<![CDATA[*/
a = window.document.getElementById( "a" );
cm =( event )=>{ event.preventDefault(); }
a.addEventListener( "contextmenu", cm );
/*]]>*/</script></code></pre></body></html>
Der Browser übergibt der Funktion »cm« beim Eintreffen des Ereignisses ein Verzeichnis »event«, welches verschiedene Funktionen für das eingetretene Ereignis enthält.
Die Funktion »cm« ruft nun die Funktion »preventDefault« aus diesem Verzeichnis auf. Dadurch erfährt der Browser, daß er die normale Ereignisbehandlung, also die Anzeige des Kontextmenüs, unterdrücken soll.
Übungsaufgaben
Zur Erleichterung der Bearbeitung können Sie an Stelle von »document.getElementById« auch weiterhin NAOTWO zum Elementzugriff verwenden, oder erst zum Schluß – nachdem die Aufgabe gelöst ist – NAOTWO durch den Einsatz von »document.getElementById« ersetzen.
Zur Erleichterung der Bearbeitung können Sie an Stelle von »addEventListener« auch weiterhin on-Attribute verwenden, also beispielsweise an Stelle von »span.addEventListener( "click", () => span.innerHTML = 1 );« in einem Skript-Element »onclick="span.innerHTML=1"« als Attribut des span-Elements.
- Durch NAOTWO (“named access on the window object ”, NAOTWO, sprich “now two ”) werden die im HTML-Quelltext mit »id=« festgelegten Elementkennungen, falls möglich, in JavaScript als globale Namen zur Verfügung gestellt. (Siehe auch: https://www.w3.org/TR/html5/browsers.html#named-access-on-the-window-object).
- Unter HTML-Quelltext versteht man Text, der in HTML geschrieben wurde – also beispielsweise »id=« enthält. – Etwa im Gegensatz zu der von einem Browser angezeigten Webseite.
Zur Erleichterung der Bearbeitung können Sie bei Übungsaufgaben in den folgenden Lektionen an Stelle von polyglottem HTML auch weiterhin SIL verwenden. Dabei kann vor den SIL-Code ein style-Element gesetzt werden und hinter den SIL-Code ein script-Element. Die drei leeren Zeilen in dem folgenden SIL-Dokument können jeweils mit CSS -Code, HTML-Code beziehungsweise JavaScript -Code befüllt werden.
/ Übungsaufgabe
Schreiben Sie einen Taschenrechner mit drei Eingabefeldern, die untereinander angeordnet sind. Wir nennen das unterste Eingabefeld X, das mittlere Y und das oberste Z.
Die Felder sollen zunächst alle 0 enthalten.
Fügen Sie dann folgende Tasten hinzu:
- Input: Liest eine Zahl mit »window.prompt« ein. Kopiert den Wert von von Y nach Z, von X nach Y und schreibt die eingegebene Zahl dann nach X.
- +: Addiert die Zahlen aus X und Y und schreibt das Ergebnis nach X. Kopiert den Wert von Z nach Y und schreibt 0 in Z.
- Berechnen Sie nun mit dem Tachenrechner 2+3.
- √: Berechnet die Quadratwurzel der Zahl in X und ersetzt die Zahl in X durch das Ergebnis.
- Fügen Sie Tasten für die weiteren drei Grundrechenarten hinzu, die wie die Addition arbeiten.
- Berechnen Sie nun mit dem Tachenrechner 2+3*4 und (2+3)*4.
- Vorzeichenwechsel: Ersetzt X durch das Negative des bisherigen Wertes.
- XY: Vertauscht X und Y.
- XX: Kopiert X nach Y.
- aufwärts: Kopiert Y nach Z und X nach Y und 0 nach X.
- abwärts: Kopiert Y nach X und Z nach Y und 0 nach Z.
- Wenn Sie tatsächlich manchmal einen Tastenrechner benötigen, fügen Sie bei Bedarf noch andere benötigte Operationen hinzu.
/ Aus der freien Wildbahn Ein nicht-kopierbarer Link
Wir habe Webseiten beobachtet, die eine Adresse in der Statusleiste des Browsers (unten) anzeigen, wenn man die Spitze des Mauszeigers auf einen Link positioniert. Das Kopieren dieser Adresse mit der rechten Maustaste wird aber verhindert, indem diese beim Öffnen des Kontextmenüs durch eine zwar verwendbare, aber weniger schöne Variante ersetzt wird. Diese weniger schöne Variante führt zunächst auf eine Umleitung, statt zu der ursprünglich angezeigten Webseite. Man kann die schöne Variante der Adresse aber bei solchen Webseiten auch nicht einfach kopieren, indem man JavaScript ganz deaktiviert, denn die schöne Variante wird erst von JavaScript in die Webseite eingesetzt.
Bauen Sie solch eine Webseite nach, indem Sie eine Seite mit einem Anker erstellen und folgende Punkte umsetzen:
- Das Skript legt eine bestimmte Adresse für den Anker fest.
- Falls der Benutzer des Kontextmenü des Ankers verwenden will, wird die Adresse des Ankers durch eine andere Adresse ersetzt. Die normale Anzeige des Kontextmenüs wird aber nicht unterdrückt, so daß der Benutzer das Kontextmenü grundsätzlich schon verwenden kann, aber der Anker dann eine andere als die zuerst festgelegte Adresse enthält.
Zusatzanforderung Erschweren Sie das Kopieren der Adresse aus dem JavaScript -Quelltext, indem Sie diese darin verschleiern.
Quellen *
- Quellen *
developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers
Web application APIs https://html.spec.whatwg.org/multipage/webappapis.html