Anzeige
Tutorialbeschreibung

Javascript und Ajax - Ereignisbehandlung

Javascript und Ajax - Ereignisbehandlung

In diesem Kapitel geht es um die Ereignisbehandlung. Diese spezialisiert sich besonders auf den Internet Explorer.


Das nun folgende Tutorial ist ein Auszug aus der 7. Auflage des Buches: JavaScript und Ajax von Christian Wenz.

Kapitel 14 Ereignisbehandlung

Die Behandlung von Ereignissen, also die Reaktion auf Benutzereingaben oder -aktionen, gestaltet sich bis jetzt noch ziemlich überschaubar. Es gibt Event-Handler, die in den entsprechenden HTML-Tags eingesetzt werden und beim Eintreten des entsprechenden Ereignisses eine JavaScript-Aktion ausführen (können). In diesem Kapitel wird dieses Konzept noch einmal behandelt. Es werden Erweiterungen daran in der JavaScript-Version 1.2 gezeigt, und es wird eine alternative Methode vorgestellt, um Event-Handler zu setzen. Dann wird es erst richtig interessant: Sowohl Netscape als auch Microsoft haben seinerzeit für die Version 4 ihrer Browser das Event-Konzept erweitert und bieten ausgefeiltere Möglichkeiten an. Eine praxisnahe Anwendung dafür zu finden fällt nicht immer leicht, aber beispielsweise können Tastatureingaben abgefangen und zum Teil auch erkannt werden. Im letzten Abschnitt dieses Kapitels werden dann Verfahren aufgezeigt, wie Sie Ihre Abfragen für beide Browsertypen funktionsfähig machen können – ab Version 4, wohlgemerkt. Warum ist das (im Falle) von Netscape überhaupt noch relevant? Ganz einfach: Am Schluss sehen wir uns die aktuellen Mozilla-Browser an, die sich kräftig beim alten Netscape bedient haben.


14.1 Events mit dem Netscape Navigator 
topBilder

Im Folgenden wird – der Einfachheit halber – auf Ereignisse mit möglichst simplen Aktionen reagiert, im Normalfall mit window.alert(). Das hat den Vorteil, dass man bei diesem neuen Stoff nicht den Überblick innerhalb eines längeren, ausführlicheren Beispiels verliert, sondern sich auf das Wesentliche konzentrieren kann.

Die bisherige Methode, auf ein Ereignis zu reagieren, bestand darin, einen HTML-Event-Handler dementsprechend zu setzen:

<html>
<head>
<title>Events</title>
<script type="text/javascript"><!--
function hamlet() {
   alert("To be, or not to be");
}

//--></script>
</head>
<body onload="hamlet();">
<h1>Events mit Netscape</h1>
</body>
</html>


14.1.1 Neue Ereignisse  

In JavaScript Version 1.2 wurden neue Ereignisse und dazugehörige Event-Handler eingeführt. Diese betreffen Maus- und Tastatureingaben.

Bei einem Mausklick trat in den älteren Versionen nach dem Drücken und Loslassen der Maustaste das Ereignis click ein, und dann konnte mit dem Event-Handler onclick reagiert werden. Ab Netscape Navigator 4.0 wurde dieses Verfahren etwas verfeinert. Es gibt die zusätzlichen Ereignisse mousedown und mouseup, die eintreten, wenn die Maustaste gedrückt respektive wieder losgelassen wird. Ein Mausklick lässt also drei Ereignisse hintereinander eintreten, und zwar in dieser Reihenfolge:

gpBilder
 
mousedown
gpBilder
 
mouseup
gpBilder
 
click

Am folgenden Beispiel können Sie die Reihenfolge ausprobieren. Klicken Sie auf die Formular-Schaltfläche, und beobachten Sie, wie sich die Statuszeile verändert, wenn Sie die Schaltfläche anklicken und dann die Maustaste wieder loslassen.

<html>
<head>
<title>Neue Mausereignisse</title>
</head>
<body onload="window.status='';">
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" value="Klick mich!"
  onmousedown="window.status+='[mousedown]';"
  onmouseup="window.status+='[mouseup]';"
  onclick="window.status+='[click]';" />
</form>
</body>
</html>

Bilder

Abbildung 14.1     Die neuen Mausereignisse

Ein weiteres neues Ereignis ist dblclick, das einen Doppelklick darstellt. Neu ist auch das Ereignis mousemove, das die Bewegungen der Maus erfasst. Wir werden später darauf zurückkommen.

Auch Tastatureingaben können abgefangen werden; dazu erfahren Sie in Abschnitt 14.1.7, »Tastatureingaben«, mehr.


14.1.2 Ereignisse als Objekteigenschaften  

Event-Handler können auch ohne HTML-Markup besetzt werden. Kommen wir zu dem Code vom Anfang des Kapitels zurück:

<body onload="hamlet();">

Dies kann auch folgendermaßen ausgedrückt werden:

window.onload = hamlet;

Hierbei sind zwei Dinge bemerkenswert. Zum einen fehlen die Klammern hinter hamlet; das liegt daran, dass kein Funktionsaufruf angegeben werden darf, sondern eine Funktionsreferenz, also ein Funktionsname ohne Klammern angegeben wird. Zum anderen erfolgt die Zuweisung anders: Da <body onload="..."> beim Laden des Fensters ausgeführt wird, muss die onload-Eigenschaft des window-Objekts gesetzt werden.

Durch dieses Vorgehen gewinnt man an Flexibilität, da man den Event-Handler an jeder Stelle im Dokument setzen kann. Ebenso kann – wenn es die Applikation erfordert – der Event-Handler gelöscht werden:

window.onload = null;

In diesem Fall ist das natürlich eher unsinnig, da das Ereignis load ohnehin nur einmal auftritt, beim Laden der Seite nämlich.

Das oben angeführte Beispiel mit den neuen Mausereignissen kann wie folgt äquivalent umgeschrieben werden:

<html>
<head>
<title>Neue Mausereignisse</title>
<script type="text/javascript"><!--
function mclick() { window.status += "[click]"; }
function mup() { window.status += "[mouseup]"; }
function mdown() { window.status += "[mousedown]"; }
function init() {
   window.status = '';
   with (document.forms[0].elements[0]) {
      onclick = mclick;
      onmouseup = mup;
      onmousedown = mdown;
  }
}
window.onload = init;
//--></script>
</head>
<body>
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" value="Klick mich!" />
</form>
</body>
</html>

Es ist hierbei sehr wichtig, dass die Funktion init() erst beim Eintreten des load-Ereignisses aufgerufen wird. Das Dokument muss vollständig geladen worden sein, denn davor existiert die Schaltfläche noch nicht, ihr können also auch keine Eigenschaften gegeben werden.

Mit anonymen Funktionen können Sie ein wenig Tipparbeit einsparen. Beim Setzen von window.onload (oder einer anderen Ereignis-Eigenschaft) geben Sie einfach den Funktionscode an, ohne extra eine (benannte) Funktion definieren zu müssen:

<html>
<head>
<title>Neue Mausereignisse</title>
<script type="text/javascript"><!--
window.onload = function() {
window.status = ''; with (document.forms[0].elements[0]) { onclick = function () { window.status += "[click]"; }; onmouseup = function () { window.status += "[mouseup]"; }; onmousedown = function () { window.status += "[mousedown]"; }; }
};
//--></script> </head> <body> <h1>Neue Mausereignisse</h1> <form> <input type="button" value="Klick mich!" /> </form> </body> </html>


14.1.3 Ereignisse abfangen  

Wie oben zu sehen ist, sind ein Ereignis und ein Event-Handler immer einem bestimmten Element zugewiesen. Die Funktion init() ist dem onload-Event-Handler des Fensters zugewiesen, die anderen Funktionen den mausbezogenen Event-Handlern der bestimmten Schaltfläche. Rein theoretisch kann es aber ganz geschickt sein, wenn nicht die Schaltfläche die Abarbeitung des Ereignisses übernimmt, sondern vielleicht das Fenster, das das »Oberobjekt« der Schaltfläche ist (die Schaltfläche kann als window.document.forms[0].elements[0] angesprochen werden). Dazu gibt es die Methode captureEvents(), mit der man festlegen kann, dass bestimmte Objekte Ereignisse abfangen, die in deren Unterobjekten auftreten. Als Parameter werden die zu überwachenden Ereignisse übergeben. Mit dem folgenden Befehl fängt das Objekt window alle click-Ereignisse in seinen Unterobjekten ab:

window.captureEvents(Event.CLICK);

Hierbei bezeichnet Event.CLICK das click-Ereignis. Die Schreibweise (insbesondere, dass das Ereignis komplett in Großbuchstaben erscheint) ist verpflichtend. Das Event-Objekt wird in diesem Kapitel noch etwas ausführlicher behandelt.

Der Opera-Browser unterstützt window.captureEvents() nicht; hier müssen Sie document.captureEvents() verwenden!

Sollen mehrere Ereignisse überwacht werden, so werden diese durch den |-Operator getrennt angegeben. Der |-Operator steht für das bitweise Oder, was an dieser Stelle aber nicht sonderlich interessant ist; es kommt vielmehr darauf an, wie dieser Operator für die Ereignisbehandlung eingesetzt wird. Wenn das window-Objekt die Ereignisse click, mousedown und mouseup überprüfen soll, muss der folgende Befehl ausgeführt werden:

window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);

Alternativ kann man auch mehrere captureEvents()-Aufrufe hintereinander schalten, wenn man sich mit dem |-Operator nicht wohlfühlt (die Verwechslungsgefahr mit || ist recht groß). Die Methode capture-Events() kann nicht nur beim window-Objekt eingesetzt werden, sondern auch beim document-Objekt. Natürlich kann man das frühe Abfangen von Ereignissen auch wieder ausschalten; hierzu dient die Methode releaseEvents(). Die folgenden Codezeilen heben sich gegenseitig auf:

window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);
window.releaseEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);


14.1.4 Ereignisbehandlung  

Wenn ein übergeordnetes Projekt Ereignisse abfangen soll, so muss man feststellen können, welches Ereignis wo eingetreten ist, um darauf reagieren zu können. Im Folgenden wird so etwas am bekannten Beispiel mit der Schaltfläche exemplarisch vorgeführt. Die Anwendung soll insofern erweitert werden, als dass eine zweite Schaltfläche eingeführt wird, die auch mit Event-Handlern bestückt werden soll. Um Schreibarbeit zu sparen, soll die Ereignisbehandlung nicht direkt in dem HTML-Code für die Schaltflächen definiert, sondern »von oben« gesteuert werden.

Zunächst einmal muss man mit captureEvents() die Ereignisse an das window-Objekt binden (zumindest in diesem Beispiel). Sodann muss man für die Ereignisse entsprechende Verarbeitungsfunktionen erstellen und zuweisen. Das Beispiel sieht dann ungefähr folgendermaßen aus:

<html>
<head>
<title>Neue Mausereignisse</title>
<script type="text/javascript"><!--
function mclick(e) {}
function mup(e) {}
function mdown(e) {}
window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);
window.onclick = mclick;
window.onmousedown = mdown;
window.onmouseup = mup;
//--></script>
</head>
<body>
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" name="Button1" value="Button1" />
<input type="button" name="Button2" value="Button2" />
</form>
</body>
</html>

Beachten Sie die leeren Funktionsaufrufe:

function mclick(e) {}
function mup(e) {}
function mdown(e) {}

Anscheinend wird ein Parameter e übergeben, doch woher kommt dieser? Wenn man Event-Handler mittels JavaScript-Eigenschaften setzt, kann man ja keine Parameter übergeben, da die Klammern fehlen. In diesem Fall wird jedoch das Ereignis selbst, eine Instanz des Objekts Event, an die Behandlungsfunktion übergeben. Die Bezeichnung e ist hierbei zwar frei wählbar, das e hat sich aber mittlerweile in Programmiererkreisen durchgesetzt. Solche ungeschriebenen Gesetze haben den Vorteil, dass man fremden Code auch ohne ausreichende Dokumentation recht schnell verstehen kann – unter Umständen.

Das Event-Objekt hat eine ganze Reihe von Eigenschaften, die in der Referenz vollständig vorgestellt werden. Für unsere Zwecke verwenden wir zunächst die folgenden:

gpBilder
 
target: Das Ziel des Ereignisses (als Referenz), also das Objekt, für das das Ereignis bestimmt war.
gpBilder
 
type: Die Art des Ereignisses als Zeichenkette.

Sie sehen, dass es eigentlich völlig unnötig war, gleich drei Funktionen (mclick(), mdown() und mup()), zu verwenden, da sich der Typ des eingetretenen Ereignisses bestimmen lässt. Zur Übung können Sie diesen »Missstand« ja einmal beheben.

Die drei Behandlungsfunktionen werden nun so umgeschrieben, dass in der Statuszeile nicht nur der Name des Ereignisses angegeben wird, sondern auch der Name des Objekts, für das dieses Ereignis bestimmt war. Da man mit der Eigenschaft target eben dieses Objekt erhält, kann man mit der Objekteigenschaft name den gewünschten Wert erhalten.

function mclick(e) { window.status += "[click@"+e.target.name+"]"; }

function mup(e) { window.status += "[mouseup@"+e.target.name+"]"; }

function mdown(e) { window.status += "[mousedown@"+e.target.name+"]"; }

 

Bilder

Abbildung 14.2     Die angeklickte Schaltfläche wird identifiziert.


14.1.5 Ereignisse umleiten  

Es ist nicht nur möglich, Ereignisse abzufangen und abzuarbeiten, bevor sie bei dem eigentlichen Zielobjekt eintreffen; man kann die Ereignisse auch auf andere Objekte umleiten. Beispielsweise ist es möglich, ein click-Ereignis einer Schaltfläche auf einen Link umzuleiten. Wenn das click-Ereignis bei einem Link eintrifft, wird dieser Link aktiviert (das muss natürlich noch programmiert werden). Die entsprechende Methode heißt handleEvent(), und als Parameter muss das Ereignis explizit übergeben werden.

Wird im folgenden Code die erste Schaltfläche angeklickt, so wird das Ereignis an die Methode handleEvent() des Links übergeben (alle Links eines Dokuments werden im Array document.links gespeichert) und die Website von Galileo Press wird aufgerufen. Wird dagegen die zweite Schaltfläche angeklickt, so wird wie gewöhnlich die Statuszeile geändert.

<html>
<head>
<title>Neue Mausereignisse</title>
<script type="text/javascript"><!--
function mclick(e) {
   if (e.target.name=="Button1") {
      document.links[0].handleEvent(e);
   } else {
      window.status += "[click@"+e.target.name+"]";
   }
}
function mup(e) { window.status +=
   "[mouseup@"+e.target.name+"]"; }
function mdown(e) { window.status +=
   "[mousedown@"+e.target.name+"]"; }
window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);
window.onclick = mclick;
window.onmousedown = mdown;
window.onmouseup = mup;
function galileo() { location.href = "http://www.galileo-press.de"; }
function init() {
   document.links[0].onclick = galileo;
   window.status = "";
}
window.onload = init;
//--></script>
</head>
<body>
<h1>Neue Mausereignisse</h1>

<form>
<input type="button" name="Button1" value="Button1" />
<input type="button" name="Button2" value="Button2" />
</form>
<hr />
<a href="http://www.galileo-press.de">Galileo Press</a>
</body>
</html>


14.1.6 Ereignisse durchleiten  

Ein Problem hat das Netscape-System noch: Wenn dem window-Objekt ein Ereignis zugeordnet wird (durch captureEvents()), aber dieses Objekt damit nichts tut, ist das Ereignis verloren oder muss manuell an ein anderes Objekt weitergeleitet werden (mit handleEvent()). Das muss aber noch nicht das Ende der Fahnenstange sein; und in der Tat gibt es noch eine weitere Methode, routeEvent(). Dieser Methode wird auch das Ereignis als Parameter übergeben, aber man kann mit ihr das Ereignis nicht an ein fixes Objekt weitergeben, sondern muss es in der Hierarchie nach unten durchreichen. Wird also eine Schaltfläche angeklickt, das click-Ereignis vom window-Objekt abgefangen und dann routeEvent(e) aufgerufen, so wird zuerst nachgeschaut, ob das document-Objekt (das nächste Objekt in der Hierarchie) einen Event-Handler für das Ereignis (hier: click) hat. Falls nicht, wird – in diesem Beispiel – zunächst das Formular überprüft, aber das hat beim Netscape Navigator ja ohnehin kein click-Ereignis. Zuletzt wird die Schaltfläche selbst überprüft.

Dieses Vorgehen hat seine Vorteile. So kann man explizit prüfen, wo ein Ereignis herkommt, und wenn man damit nichts anfangen kann, wird es einfach weitergereicht. Im folgenden Beispiel, der nächsten Erweiterung unseres Codes, wird dieses Verfahren an zwei Stellen eingesetzt:

gpBilder
 
Wenn der Benutzer irgendwo in das Fenster klickt, tritt auch ein click-Ereignis auf und wird vom window-Objekt (dank captureEvents()) abgefangen. Mitunter erscheint dann Müll in der Statuszeile. Das können Sie verhindern, indem Sie überprüfen, ob der Name des aufrufenden Objekts mit "Button" anfängt.
gpBilder
 
Klickt der Benutzer auf den Link, wird auch nichts in die Statuszeile geschrieben, und das Ereignis wird direkt weitergereicht, so dass das Ziel des Links auch angezeigt wird, ohne dass groß mit handleEvent() umgeleitet werden muss.
<html>
<head>
<title>Neue Mausereignisse</title>
<script type="text/javascript"><!--
function mclick(e) {
   if (e.target.name.indexOf("Button") == 0) {
      window.status += "[click@"+e.target.name+"]";
   }
   routeEvent(e);
}
function mup(e) { window.status +=
   "[mouseup@"+e.target.name+"]"; }
function mdown(e) { window.status +=
   "[mousedown@"+e.target.name+"]"; }
window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP);
window.onclick = mclick;
window.onmousedown = mdown;
window.onmouseup = mup;
function galileo() { location.href = 
  "http://www.galileo-press.de"; }
function init() {
   document.links[0].onclick = galileo;
   window.status = "";
}
window.onload = init;
//--></script>
</head>
<body>
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" name="Button1" value="Button1" />
<input type="button" name="Button2" value="Button2" />
</form>
<hr />
<a href="http://www.galileo-press.de" >Galileo Press</a>
</body>
</html>


14.1.7 Tastatureingaben 
topBilder

Für Tastatureingaben wurden mit JavaScript 1.2 ebenfalls neue Ereignisse eingeführt. Bei einem Tastendruck tritt das Ereignis keypress ein. Wie bei einem Mausklick besteht auch dieser Tastendruck aus zwei Teilen, und zwar aus keydown (Taste drücken) und keyup (Taste wieder loslassen).

Das W3C-DOM Level 2 kennt keine Tastatureingaben. Sprich, die Browserhersteller mussten hier Pionierarbeit leisten – was dazu führt, dass verschiedene Browser einige Unterschiede in der Tastaturhandhabung aufweisen.

Bei Tastatureingaben gewinnt das Ereignis besonders an Bedeutung. In der Eigenschaft which steht der ASCII-Code des Zeichens. So kann ein einfaches Ratespiel implementiert werden. Der Benutzer kann in das folgende Texteingabefeld beliebige Buchstaben eingeben. Nach jedem Tastendruck wird der eingegebene Buchstabe mit dem zu erratenden Buchstaben verglichen. Hat der Benutzer richtig geraten, so wird dies ausgegeben. Bei jedem Rateversuch wird ein Zähler um eins erhöht, denn man kann nicht unendlich oft raten, sondern (bei uns) maximal zehnmal. Beachten Sie, wie im folgenden Code die Funktion immer mit return true verlassen wird, außer wenn das Ratespiel zu Ende ist: Dann endet die Funktion mit return false. Der Grund ist folgender: Wie Sie bereits an diversen Beispielen in anderen Kapiteln gesehen haben, wird die weitere Abarbeitung des Ereignisses im Browser abgebrochen, wenn ein Event-Handler mit return false endet. In diesem Fall soll verhindert werden, dass die zusätzlich eingetippten Buchstaben im Eingabefeld erscheinen.

<html>
<head>
<title>Tastatureingaben mit Mozilla</title>
<script type="text/javascript"><!--
var versuche = 0;
var geheim = "K"  //Geheimes Zeichen
function taste(e) {
   versuche = 0;
   //Umwandlung von ASCII-Code in Zeichen
   var zeichen = String.fromCharCode(e.which);
   if (zeichen.toUpperCase() == geheim) {
      alert("Richtig geraten, mit genau " + versuche +
         " Versuchen");
      versuche = 10;
   }
   return (versuche<10);
}
document.captureEvents(Event.KEYPRESS);
document.onkeypress = taste;
//--></script>
</head>
<body>
<form>
<input type="text" size="10" />
</form>
</body>
</html>

Ein Hinweis noch am Rande: Mit einer einzigen zusätzlichen Zeile kann man zumindest verhindern, dass auf Nicht-Netscape-Browsern eine Fehlermeldung ausgegeben wird. Nicht vorhandene Eigenschaften kann man ohne Probleme setzen, die einzige Gefahrenquelle ist die folgende Zeile:

document.captureEvents(Event.KEYPRESS);

Mit einem kleinen Trick kann man das verhindern:

if (document.captureEvents) {
   document.captureEvents(Event.KEYPRESS);
}

 

14.2 Events mit dem Internet Explorer 
topBilder

Wie bereits eingangs erwähnt wurde, hat auch der Internet Explorer mit seiner Version 4 ein neues Ereignismodell eingeführt. Leider ist dieses Modell fast vollständig inkompatibel mit der Variante von Netscape, aber gegen Ende dieses Kapitels werden wir uns auch dieses Problems annehmen. Galten alle vorherigen Beispiele in diesem Kapitel nur für den Netscape Navigator 4 oder höher, so funktionieren die folgenden Beispiele nur ab dem Internet Explorer Version 4. Zur Vereinigung dieser beiden Browser gelangen wir wie gesagt später.

Ganz allgemein kann man sagen, dass das DOM (Document Object Model) des Internet Explorers ausgereifter ist (sprich: den Vorgaben des W3-Konsortiums deutlich mehr entspricht) als die Netscape-Variante. So kann beispielsweise jedes Tag mit Event-Handlern bestückt werden, beispielsweise auch <p>, <i> und <b>.


14.2.1 Neue Ereignisse  

Auch der Internet Explorer hat in Version 4 neue Ereignisse eingeführt. Für Mausbewegungen sind das mousedown, mouseup und dblclick; bei der Tastatur handelt es sich um keydown, keyup und keypress. Bei der Bedeutung dieser Ereignisse gibt es keinen Unterschied zu Netscape, so dass die Ereignisse an dieser Stelle nicht noch einmal ausführlich erklärt werden. Der Unterschied liegt in der Behandlung der Maus- und Tastatureingaben, aber dazu später mehr.


14.2.2 Ereignisse als Objekteigenschaften  

Der Internet Explorer kann statt HTML-Event-Handlern JavaScript-Objekteigenschaften benutzen. Das Beispiel von oben kann also wiederverwendet werden:

<html>
<head>
<title>Events</title>
<script type="text/javascript"><!--
window.onload = function() {
   alert("To be, or not to be");
};
//--></script>
</head>
<body>
<h1>Events mit dem Internet Explorer</h1>
</body>
</html>


14.2.3 Spezielle Skripten  

Es gibt beim Internet Explorer eine spezielle Möglichkeit, Event-Handler zu erzeugen. Dazu verwendet man ein gewöhnliches <script>-Tag und setzt die Parameter event und for. Dabei enthält event den Namen des Event-Handlers (beispielsweise onload oder onclick) und for den Identifikator des Objekts oder HTML-Elements, für das der Event-Handler eingesetzt werden soll. Das sieht dann folgendermaßen aus:

<html>
<head>
<title>Spezielle Skripten</title>
<script type="text/javascript" event="onclick" for="Button1"><!--
window.status = "Schaltfläche angeklickt!";
//--></script>
</head>
<body>
<h1>Spezielle Skripten</h1>
<form>
<input type="button" name="Button1" value="Klick mich an" />
</form>
</body>
</html>

So schön die Idee auch ist – in der Praxis ist dieses Vorgehen völlig verfehlt. Alle anderen Browser außer dem Internet Explorer ignorieren die Parameter event und for des <script>-Tags und führen die Befehle darin schon beim Laden der Seite aus. Aus diesem Grund werden Sie die obige Methode nur auf schlechten Seiten finden, und auch das nur äußerst selten.


14.2.4 Ereignisse abfangen  

Beim Netscape Navigator kann man mit captueEvents() und releaseEvents() Ereignisse abfangen, die in Unterobjekten auftreten. Das liegt daran, dass beim Netscape Navigator Ereignisse in der Regel nur an das Objekt weitergeleitet werden, für das das jeweilige Ereignis bestimmt ist. Beim Internet Explorer funktioniert es etwas anders: Tritt bei einem Objekt ein Ereignis auf und ist dazu kein Ereignis-Handler definiert, so wird zum übergeordneten Objekt weitergegangen und nachgeschaut, ob zu diesem vielleicht ein Event-Handler definiert ist. Um auf das in diesem Kapitel schon öfter verwendete Formularbeispiel zurückzukommen: Im folgenden Code werden alle Mausklicks in das Dokument abgefangen – also insbesondere auch die Klicks auf eine Schaltfläche:

<html>
<head>
<title>Ereignisse mit dem Internet Explorer</title>
<script type="text/javascript"><!--
function mclick() {
   window.status += "[Mausklick]";
}
document.onclick = mclick;
//--></script>
</head>
<body onload="window.status='';">
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" name="Button1" value="Button1" />
</form>
</body>
</html>

Auch für die Methode releaseEvents() gibt es eine äquivalente Anweisung – die Objekteigenschaft muss einfach auf null gesetzt werden:

document.onclick = null;


14.2.5 Bubbling  

Der zuvor skizzierte Ablauf beim Eintreten eines Ereignisses wird nun noch etwas genauer dargestellt. Tritt bei einem Objekt ein Ereignis auf, so wird bei diesem Objekt und dann nacheinander bei allen übergeordneten Objekten nachgeschaut, ob einer der beiden folgenden Fälle eintritt:

gpBilder
 
Es gibt einen Event-Handler, und dieser wird mit return false abgeschlossen.
gpBilder
 
Man ist beim obersten Objekt angelangt, dem document-Objekt.

Wenn man das oben dargestellte Programm leicht modifiziert bzw. ergänzt, kann man diesen Effekt sehr gut beobachten:

<html>
<head>
<title>Ereignisse mit dem Internet Explorer</title>
<script type="text/javascript"><!--
function mclick() {
   window.status += "[Mausklick]";
}
function buttonclick() {
   window.status += "[Buttonclick]";
}
document.onclick = mclick;
//--></script>
</head>
<body onload="window.status='';">
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" name="Button1" value="Button1" onclick="buttonclick();" />
</form>
</body>
</html>

Wenn Sie das Skript ausführen und auf die Schaltfläche klicken, werden sowohl die Funktion buttonclick() als auch mclick() aufgerufen.

Endet jedoch buttonclick() mit return false, so wird mclick() nicht mehr ausgeführt, wenn auf die Schaltfläche geklickt wird:

<html>
<head>
<title>Ereignisse mit dem Internet Explorer</title>
<script type="text/javascript"><!--
function mclick() {
   window.status += "[Mausklick]";
}

function buttonclick() {
   window.status += "[Buttonclick]";
   return false;
}
document.onclick = mclick;
//--></script>
</head>
<body onload="window.status='';">
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" name="Button1" value="Button1" onclick="buttonclick();" />
</form>
</body>
</html>

Bilder

Abbildung 14.3     Die neuen Mausereignisse

Das hierarchische Vorgehen von unten nach oben nennt man im Englischen »Bubbling«, weil das Ereignis wie eine Blase (bubble) nach oben steigt.


14.2.6 Das Event-Objekt 
topBilder

Schon im Abschnitt über den Netscape Navigator wurde das Event-Objekt vorgestellt. Die gute Nachricht: Beim Internet Explorer verhält sich dieses Objekt recht ähnlich. Die schlechte Nachricht: Es gibt ganz am Anfang einen entscheidenden Unterschied – Kompatibilität adé! Während beim Netscape Navigator das aktuelle Ereignis als Parameter an den Event-Handler übergeben wird, ist das beim Internet Explorer nicht so – es ist nicht einmal nötig. Auf das aktuelle Ereignis kann mit window.event oder einfach nur mit event zugegriffen werden.

Auch hier gibt es wieder die Eigenschaft type, die die Art des Ereignisses als Zeichenkette angibt (z.  B. "load" oder "click"). Nur event.target gibt es nicht mehr, beim Internet Explorer heißt diese Eigenschaft srcElement und enthält (wie target beim Netscape Navigator) eine Referenz auf das Objekt, für das das Ereignis bestimmt war.

Beachten Sie auf jeden Fall, dass beim Netscape Navigator Event mit großem E geschrieben wird, beim Internet Explorer mit kleinem e.

Im folgenden Beispiel wird der name-Parameter derjenigen Schaltfläche angegeben, die angeklickt worden ist:

<html>
<head>
<title>Ereignisse mit dem Internet Explorer</title>
<script type="text/javascript"><!--
function mclick() {
   window.status +=  "[" + event.type + "@" +
      event.srcElement.name+"]";
}
document.onclick = mclick
//--></script>
</head>
<body onload="window.status='';">
<h1>Neue Mausereignisse</h1>
<form>
<input type="button" name="Button1" value="Button1" />
<input type="button" name="Button2" value="Button2" />
</form>
</body>
</html>

Das Prinzip des Event-Bubblings kann nicht nur generell abgeschaltet werden – etwa durch ein return false –, sondern auch explizit für genau ein Ereignis. Dazu verwenden Sie das folgende Kommando:

event.cancelBubble = true;

Bilder

Abbildung 14.4     Die angeklickte Schaltfläche wird identifiziert.

 

14.3 Events mit beiden Browsern 
downBilder
topBilder

In diesem Abschnitt werden noch kurz Strategien vorgestellt, wie Sie Ihre Skripten auf beiden Browsern ohne Fehler benutzen können – vielleicht auf älteren Browsern nicht mit der vollen Funktionalität, aber immerhin ohne Fehlermeldungen. Außerdem werden kurz die Unterschiede bei der Abfrage von gedrückten Tastatur- und Maustasten bei den beiden »großen« Browsern beleuchtet.


14.3.1 Browserunabhängigkeit  

Im Folgenden sollen einige allgemeine Konzepte vorgestellt werden, die bei der fortgeschrittenen, browserunabhängigen Ereignisbehandlung von Nutzen sein können.

Netscape oder nicht?

In Sachen Ereignis fällt es ziemlich leicht, den Netscape Navigator vom Internet Explorer zu unterscheiden. Sie verwenden dazu die Objektüberprüfung:

if (window.Event) {
   //Code für den Netscape
} else {
   //Code für den Internet Explorer
}

Ereignisse abfangen

Wie ich bereits erwähnt habe, kennt der Internet Explorer die Methode captureEvents() nicht; durch das Bubbling-Prinzip ist sie ja auch nicht nötig, da Ereignisse nach oben weitergereicht werden. Soll etwa ein Mausklick abgefangen werden, so kann man sich mit folgendem Code behelfen:

function mclick() {
   window.status += "[Mausklick]";
}
if (window.Event) {
   document.captureEvents(Event.MOUSEUP);
}
document.onclick = mclick;

Denken Sie daran, dass der Internet Explorer beim document-Objekt aufhört und nicht mehr weiter nach oben gehen kann; Folgendes wäre also falsch:

if (window.Event) {
   window.captureEvents(Event.MOUSEUP);
}
window.onclick = mclick;

Ereigniseigenschaften

Wie bereits angedeutet wurde, gibt es bei der Abfrage von Ereignissen Unterschiede zwischen den beiden Browsern: angefangen bei der Übergabe als Parameter bis hin zur Schreibweise. Auch hier kann man sich wieder mit einer Objektüberprüfung behelfen:

function mclick(e) {
   var ev = (window.Event) ? e : window.event;
   window.status += "[" + ev.type + "]";
}

if (window.Event) {
   document.captureEvents(Event.MOUSEUP);
}
document.onclick = mclick;

Beim Netscape Navigator wird auf den Parameter e zugegriffen, beim Internet Explorer auf window.event (oder event). Die Eigenschaft type ist bei beiden Browsern identisch, kann also problemlos verwendet werden. Bei dem Ziel des Ereignisses ist das etwas aufwändiger. Man benötigt eine eigene Anweisung:

function mclick(e) {
   var ev = (window.Event) ? e : window.event;

   var ziel = (window.Event) ? ev.target : ev.srcElement;
   window.status += "[" + ev.type + "@" + ziel.name + "]";
}
if (window.Event) {
   document.captureEvents(Event.MOUSEUP);
}
document.onclick = mclick;


14.3.2 Benutzereingaben 
topBilder

In den vorherigen Abschnitten wurden Tastatureingaben und Mausklicks eher stiefmütterlich behandelt; beispielsweise wurden Sondertasten wie etwa (Alt) und (Strg) nicht erwähnt. Das soll sich an dieser Stelle ändern. Zwar unterscheiden sich auch hier die beiden Browser, aber inzwischen sollten Sie so abgehärtet sein, dafür eine Lösung zu finden.

Mausklicks

PC-Mäuse haben zwei oder drei Tasten. Da ist es doch interessant herauszufinden, welche dieser Tasten gedrückt worden ist. Bevor Sie sich irrwitzige Anwendungsmöglichkeiten ausdenken, eine kleine Warnung: Beispielsweise ruft die rechte Maustaste ein Kontextmenü auf; das Ereignis tritt (für den Browser) also gar nicht ein. Viele Maustreiber belegen die mittlere Maustaste mit einem Doppelklick, und damit ist die Taste für den differenzierten Einsatz auch nicht sonderlich geeignet.

Beim Netscape Navigator erhält man die Nummer der gedrückten Maustaste aus der which-Eigenschaft des aufgetretenen Ereignisses. Beim Internet Explorer dagegen gibt es hierfür eine gesonderte Eigenschaft, und zwar die Eigenschaft button des Ereignisses.

<html>
<head>
<title>Maustasten</title>
<script type="text/javascript"><!--
function taste(e) {
   if (window.Event) {
      var maustaste = e.which;
   } else {
      var maustaste = e.button;
   }

   window.status += "[geklickt:" + maustaste + "]";
}
//--></script>
</head>
<body onload="document.forms[0].elements[0].onclick=taste; window.status='';">
<h1>Maustasten</h1>
<form>
<input type="button" name="Button1" value="Klick mich" />
</form>
</body>
</html>

Tasten

Wie Sie bereits gesehen haben, erhält man den ASCII-Code beim Netscape Navigator aus der which-Eigenschaft des Ereignisses; beim Internet Explorer gibt es wieder eine eigene Eigenschaft, nämlich keyCode. Bei verschiedenen Plattformen gibt es hin und wieder Bugs in der Umsetzung, die aber mit der Methode String.fromCharCode() zusammenhängen. Bei manchen Internet Explorer-Versionen für den Macintosh werden einige seltenere Zeichen falsch zurückgegeben. Verlassen Sie sich im Zweifel also auf den ASCII-Code; im Produktionsbetrieb können Sie dennoch immer auf fromCharCode() zurückgreifen.

<html>
<head>
<title>Tastatur</title>
<script type="text/javascript"><!--
function taste(e) {
   if (window.Event) {
      var tastatur = e.which;
   } else {
      var tastatur = e.keyCode;
   }

   window.status += "[getippt:" +
      String.from CharCode(tastatur) + "]";

//--></script>
</head>
<body onload="document.forms[0].elements
[0].onclick=taste; window.status='';">
<h1>Tastatur</h1>

<form>
<input type="text" name="text1" />
</form>
</body>
</html>

Sondertasten

Mit den Sondertasten verhält es sich vom Prinzip her ähnlich wie mit Tastatureingaben und Mausklicks. Während beim Netscape Navigator möglichst viel in eine Eigenschaft oder Variable gepackt worden ist (ressourcensparend), kann man beim Internet Explorer explizit auf eine bestimmte Eigenschaft pro Sondertaste zugreifen (benutzerfreundlich). Eine Einschränkung gibt es jedoch: Beim Netscape Navigator können die Tasten (Alt), (Strg), (ª) und (€) abgefragt werden, beim Internet Explorer lediglich (Alt), (Strg) und (ª). Die (Ctrl)-Taste, das Macintosh-Äquivalent zur (Strg)-Taste, kann beim Macintosh-IE ebenfalls nicht abgefragt werden.

Anstelle von langen Erklärungen folgt ein selbsterklärendes Beispiel. Während beim Internet Explorer einzelne Eigenschaften abgefragt werden, muss man beim Netscape wieder eine bitweise Operation, diesmal UND (&), verwenden.

<html>
<head>
<title>Sondertasten</title>
<script type="text/javascript"><!--
function taste(e) {
   window.status += "[";
   if (window.Event && parseInt(navigator.appVersion)==4) {
      //Netscape 4.x
      if (e.modifiers & Event.ALT_MASK) {
         window.status += "{Alt}";
      }
      if (e.modifiers & Event.CONTROL_MASK) {
         window.status += "{Strg}";
      }
      if (e.modifiers & Event.SHIFT_MASK) {
         window.status += "{Umschalt}";
      }
      if (e.modifiers & Event.META_MASK) {
         window.status += "{Meta}";
      }
  } else { //IE
      if (!e) {
         e = window.event;
      }
      if (e.altKey) {
         window.status += "{Alt}";
      }
      if (e.ctrlKey) {
         window.status += "{Strg}";
      }
      if (e.shiftKey) {
         window.status += "{Umschalt}";
      }
   }
   window.status += "]";
}
//--></script>
</head>
<body onload="document.forms[0].elements[0].onclick=taste; window.status='';">
<h1>Sondertasten</h1>
<form>
<input type="button" name="Button1" value="Klick mich an!" />
</form>
</body>
</html>

Bilder

Abbildung 14.5     Auch gedrückte Sondertasten können abgefragt werden.

Testen Sie dieses Programm, indem Sie ein paar der Sondertasten gedrückt halten und dann mit der Maus auf die Schaltfläche klicken.

 

14.4 Andere (und moderne) Browser 
topBilder
topBilder

Die Ereignisbehandlung in aktuellen Mozilla-Browsern ist eine Mischung aus den Konzepten des Netscape Navigator 4.x und des Internet Explorer ab Version 4. Während der Netscape Navigator ein Ereignis nur beim »Eintauchen« in eine Website abfangen kann (mit captureEvent()), stößt der Internet Explorer auf dasselbe Ereignis, wenn es via Bubbling nach oben steigt. Der Mozilla versteht beide Richtungen, die Ansteuerung ist identisch.

An einer Stelle mussten sich die Software-Architekten von Mozilla jedoch entscheiden, welches Modell sie für das Ereignis-Objekt wählen sollten. Sie entschieden sich für die Netscape-Variante: An jeden Event-Handler wird eine Variable übergeben (sie wird meistens im Funktionskopf mit e bezeichnet), auf deren Eigenschaften (z.  B. e.target etc.) im Funktionsrumpf zugegriffen werden kann. Sie sehen also: Wenn Sie das Netscape-Konzept erst einmal begriffen haben, können Sie auch für Mozilla-Browser programmieren.

Allerdings gibt es auch eine neue Möglichkeit, auf Ereignisse zu reagieren, die alle modernen Browser außer dem Internet Explorer unterstützen. Die Rede ist von Event-Listenern – »Ereignis-Lauschern«. Mit JavaScript können Sie einen solchen Lauscher an jedes Element auf der HTML-Seite anhängen. Und das sieht dann so aus:

var schaltflaeche = document.forms[0].elements[0];
schaltflaeche.addEventListener(
   "click",
   function() {
      alert("Geklickt!");
   },
   false);

Die Methode addEventListener() fügt einen Lauscher dem entsprechenden Element hinzu. Der erste Parameter ist der Name des Ereignisses (also ohne »on« am Anfang), und der zweite Parameter ist die Funktion, die beim Eintritt des Ereignisses zur Ausführung kommen soll. Sie können entweder eine anonyme Funktion wie im vorherigen Code verwenden oder eine Referenz auf eine benannte Funktion irgendwo anders auf der Seite.

Im dritten Parameter geben Sie an, ob das Ereignis abgefangen werden soll oder weitergeleitet werden darf. Hier ein komplettes Listing:

<html>
<head>
<title>Events</title>
<script type="text/javascript"><!--
window.onload = function() {
   var schaltflaeche = document.forms[0].elements[0];
   schaltflaeche.addEventListener(
      "click",
      function() {
         alert("Geklickt!");
      },
      false);
};
//--></script>
</head>
<body>
<h1>Event-Listener</h1>
<form>
<input type="button" value="Klick mich!" />
</body>
</html>

Das Gegenstück zu addEventListener() ist removeEventListener(); damit entfernen Sie die Funktionalität vom HTML-Element wieder.

Bilder

Abbildung 14.6     Die Schaltfläche hat jetzt eine Funktion.

Wie gesagt, das unterstützen leider nicht alle Browser, aber immerhin fast alle – lediglich der momentane Marktführer (Internet Explorer) nicht. Dieser hat eine eigene Variante zum Hinzufügen und Entfernen von Event-Listenern: attachEvent() und detachEvent(). Dabei gibt es jedoch erhebliche Unterschiede bei den Parametern (im Vergleich zu vorher). Sie müssen nur zwei Parameter angeben, nämlich das Ereignis und die Handler-Funktion. Das Ereignis allerdings muss mit "on" beginnen. Hier das vorherige Beispiel, angepasst für den Internet Explorer:

<html>
<head>
<title>Events</title>
<script type="text/javascript"><!--
window.onload = function() {
   var schaltflaeche = document.forms[0].elements[0];
   schaltflaeche.attachEvent(
      "onclick",
      function() {
         alert("Geklickt!");
      });
};
//--></script>
</head>
<body>
<h1>Event-Listener</h1>
<form>
<input type="button" value="Klick mich!" />
</body>
</html>

Es ist dann nur noch eine Fingerübung, das Beispiel browserunabhängig zu implementieren. Sie prüfen dazu lediglich, ob der Browser die entsprechende Methode unterstützt oder nicht. Das ist deutlich besser, als etwa den Browsertyp zu ermitteln und dann eventuell bei einem exotischeren Browser Pech zu haben.

if (schaltflaeche.attachEvent) {
   // Verwendung von attachEvent()/detachEvent()
} else if (schaltflaeche.addEventListener) {
   // Verwendung von addEventListener()/removeEventListener()
}

Das Tutorial ist ein Auszug aus der 7. Auflage des Buches von Christian Wenz:

JavaScript und Ajax - Das umfassende Handbuch
Galileo Computing, 853 Seiten, 8. aktualisierte Auflage

Die Veröffentlichung des Kapitels erfolgt mit freundlicher Genehmigung von Galileo Press.

Mehr Informationen zum aktualisierten Fachbuch für Webentwickler gibt es hier: JavaScript und Ajax
 
Alle Kapitel des Buches:
1 Grundlagen
2 JavaScript-Praxis
3 Professionelles JavaScript
4 Web 2.0 und Ajax
5 Kommunikation
6 Serverseitige Programmierung
7 Sicherheit
8 Praxis
9 Anhang
Bilder


 

DVD-Werbung
Kommentare
Achtung: Du kannst den Inhalt erst nach dem Login kommentieren.
Portrait von tanzfrosch
  • 11.01.2018 - 14:55

Dankeschön, tolles Tut. !

x
×
×