Details
Eine Client-Klasse
Die clients sind Objekte, die auf vom Controller weitergeleitete Events reagieren können. Dazu sind zwei Dinge notwendig: zum einen müssen die Client-Klassen Methoden für die Verarbeitung bestimmter Event-Typen zur Verfügung stellen, und zum anderen müssen sie in der Lage sein, zu entscheiden, ob sie für ein Ereignis tatsächlich zuständig sind.
Wie dies aussieht, soll nachfolgend an einem sehr einfachen, aber praxisnahen Beispiel demonstriert werden, der Klasse Popup, die auch Bestandteil der JS-Pocket-Lib ist. Diese Klasse ermöglicht es, bei Klick auf Links mit bestimmten Klassen ein Popup-Fenster zu öffnen, also etwa solche Links:
Suchen bei Google (Mini-Popup) (<a href="http://www.google.de/" class="popup micro">Suchen bei Google (Mini-Popup)</a>)
Suchen bei Google (schmales vertikales Popup) (<a href="http://www.google.de/" class="popup vertical">Suchen bei Google (schmales vertikales Popup)</a>)
Suchen bei Google (Standard-Popup) (<a href="http://www.google.de/" class="popup search">Suchen bei Google (Standard-Popup)</a>).
Eigentlich sehen diese Links sehr unspektakulär aus, und genau das ist auch der Vorteil: wir haben sauberes HTML-Markup ohne Inline-Javascript (also ohne Event-Handler wie onclick), und dennoch wird durch den Einsatz des Controllers und unserer Klasse Popup bei Benutzern mit aktiviertem Javascript unser maßgeschneidertes Popup-Fenster geöffnet!
Eigenschaften der Klasse Popup Quellcode zeigenQuellcode ausblenden
function Popup() {
var self = this;
self.currentType = '';
self.currentHref = 'about:blank';
self.types = {
micro: { windowName: 'search', windowSettings: 'width=450,height=300,left=120,top=120,resizable=yes,scrollbars=yes' },
vertical: { windowName: 'search', windowSettings: 'width=100,height=800,left=20,top=20,scrollbars=yes' },
// more types ...
standard: { windowName: 'popup', windowSettings: 'width=800,height=600' }
}
// methods to follow ...
}
Die Eigenschaft currentType enthält bei Klick auf einen Popup-Link den Typ dieses Links, das heißt bei dem ersten Google-Link im obigen Beispiel den Wert "micro". Die Eigenschaft currentHref speichert entsprechend das Verweisziel des angeklickten Links. Die Eigenschaft types enthält alle definierten Popup-Typen Eurer Wahl in Form eines Arrays, der oben in Kurzform deklariert wird.
Die Methoden zur Event-Verarbeitung
Wie geht nun eine Instanz der Popup-Klasse mit den Events um, die der Controller verteilt? Beziehungsweise: wie teilt das Popup-Objekt dem Controller mit, welche Events es haben will?
Methoden click() und keypress() der Klasse Popup Quellcode zeigenQuellcode ausblenden
self.click = function(evt) {
var srcElement = (evt.srcElement)? evt.srcElement : evt.target;
if(self.isResponsible(srcElement)) {
self.popup();
return false;
} else {
return true;
}
return false;
}
self.keypress = self.click;
Unser Popup-Objekt soll sich darum kümmern, wenn auf einen Link geklickt wird, das heißt, es soll sich nur für den click-Event und den keypress-Event interessieren. Wir definieren daher einfach Methoden dieses Namens, also click() und keypress() für die Popup-Klasse, die dann vom Controller aufgerufen werden können. Da auf beide Events gleichermaßen reagiert werden soll, reicht es, wenn wir die zweite Methode einfach auf die erste verweisen lassen: self.keypress = self.click;.
Wie beim Controller tritt auch hier wieder das scope-Problem auf: bei der Verarbeitung des Events werden wir aus dem Namensraum unseres Popup-Objekts geworfen. Um dies zu korrigieren, wird die bereits beim Controller eingesetzte Lösung verwendet.
- "Preserving Scope in JavaScript"
- Beschreibung des scope-Problems und einiger Lösungsansätze (englisch).
Die eigentliche Funktionalität der Methode click() ist einfach. Wir holen uns das DOM-Objekt, von dem der Event ausgelöst wurde. Dabei müssen wieder die unterschiedlichen Event-Modelle von Microsoft (Event.srcElement) und W3C (Event.target) beachtet werden. Dieses Objekt wird an die Methode isResponsible() übergeben, die überprüft, ob unser Popup-Objekt überhaupt für diesen Klick verantwortlich ist, das heißt ob der Klick auf einen Link mit der Klasse "popup [typ]" erfolgte. Ist dies der Fall, so wird durch Aufruf der Methode popup() - wer hätte das gedacht? - das Popup-Fenster geöffnet und die Methode click() durch Rückgabe des Wertes false verlassen. Dieser Rückgabewert sorgt dafür, daß dieser Klick vom Controller nicht weitergereicht wird an andere Objekte. Gäben wir true zurück, dann würde nicht nur das Popup-Fenster geöffnet werden, sondern das Link-Element selbst würde auch auf den Klick reagieren und das Verweisziel zusätzlich im Mutterfenster öffnen.
Liefert isResponsible() den Wert false, dann ist das Popup-Objekt nicht für den Klick zuständig (z.B. wenn der Benutzer gar keinen Link angeklickt hat). In diesem Fall gibt unsere Methode click() an den Controller den Wert true zurück und teilt ihm so mit, daß er den Event weitergeben kann.
- "Event order"
- Beschreibung des "capturings" und "bubblings" von Events (englisch).
Verantwortlich oder nicht?
Um zu entscheiden, ob das Popup-Objekt zuständig für das vom Controller übergebene click-Ereignis ist, muß zunächst geprüft werden, ob der Link aus einem Link heraus erfolgte. Da der Klick auch von einem Element innerhalb eines Verweisankers kommen kann, etwa von einem strong-Element innerhalb eines Links, schauen wir auch, ob vielleicht ein übergeordneter DOM-Knoten des angeklickten Elements ein Link ist.
Methode isResponsible() der Klasse Popup Quellcode zeigenQuellcode ausblenden
self.isResponsible = function(srcElement) {
var classRegExp;
// Check if the click came from an anchor node or from a node within an anchor:
while(srcElement.tagName != "BODY" && srcElement.tagName != "A") {
srcElement = srcElement.parentNode;
}
if(srcElement.tagName != "A") {
return false;
}
for(type in self.types) {
classRegExp = new RegExp("popup\\s+" + type);
if(srcElement.className.match(classRegExp)) {
self.currentType = type;
self.currentHref = srcElement.getAttribute("href");
srcElement.blur();
return true;
}
}
return false;
}
Handelt es sich um einen Link, so ist zusätzlich noch zu prüfen, ob er die Klasse "popup [typ]" hat, wobei [typ] einer der Popup-Typen sein muß, die wir in den Eigenschaften unserer Popup-Klasse definiert haben, also etwa "popup micro". Diese Prüfung erfolgt durch einen einfachen regulären Ausdruck.
Voilà - le Popup!
Ist das Popup-Objekt verantwortlich, so wird aus click() bzw. aus keypress() heraus die Methode popup() aufgerufen.
Methode popup() der Klasse Popup Quellcode zeigenQuellcode ausblenden
self.popup = function() {
window.open(self.currentHref, self.types[self.currentType]["windowName"], self.types[self.currentType]["windowSettings"]);
return false;
}
Das ist alles - schon haben wir eine kleine handliche Klasse, mit der aus jedem "normalen" Link ohne Inline-Javascript ein Popup-Link werden kann. Eine sinnvolle Ergänzung der Popup-Klasse könnte eine Methode addType() sein, mit der man "on the fly" neue Popup-Typen hinzufügen kann.
Kommentare zu dieser Seite
classRegExp = new RegExp("(?:^|\\s+)popup\\s+(?:[^\"]+\\s+)*" + type + "(?:$|\\s+)");.
Andreas Dölling
Kommentarfunktion ausgeschaltet
Ich würde eigentlich gerne Benutzern der JS-Pocket-Lib und anderen Entwicklern die Möglichkeit bieten, schnell und bequem Kommentare und Anregungen hier zu hinterlassen, denn so ein Feedback ist sehr wichtig für die Verbesserung der Bibliothek. Da aber bereits nach wenigen Tagen die Kommentarfunktion, die ich eingebaut hatte, von zerstörungswütigen Vollidioten massivst zum Spamming mißbraucht wurde und ich auf der anderen Seite daraus auch keinen Hochsicherheitstrakt machen wollte, habe ich die Kommentarfunktion wieder entfernt. Sorry an alle normalen Besucher und fuck yourself, Spammers!