Das JavaScript-Framework MooTools Beitrag zum Seminar Internet, Sommersemester 2007 von Mathias Palme und Simon Jockers JavaScript hat eine bewegte Vergangenheit hinter sich. Nachdem es bereits Mitte der Neunziger eingeführt worden war und sich zwischenzeitlich großer Beliebtheit erfreute, war es später als Sicherheitslücke und Technologie für nervige Popups verschrien. Ewiger Kritikpunkt ist auch die mangelnde Zugänglichkeit vieler Seiten, die JavaScript verwenden. In den letzten Jahren hat JavaScript durch den Web2.0-Hype wieder an Bedeutung gewonnen. Traditioneller Einsatzzweck sind die Überprüfung von Formulardaten, Rollover-Effekte, PopupFenster und ähnliches. Im Zuge der Popularität von Rich-Client-Anwendungen wird JavaScript für zunehmend anspruchsvollere Aufgaben verwendet: In Verbindung mit dem XmlHttpRequest ermöglicht JavaScript Web-Applikationen, die sich wie lokal laufende Anwendung bedienen lassen. Damit bildet JavaScript die Basis für Ajax. Inzwischen hat man auch Techniken entwickelt, um mit Problemen der Zugänglichkeit vermeiden (Stichwort „graceful degradation“). JavaScript um 1995 mit dem Netscape Navigator 2.0 eingeführt. Im darauf folgenden Jahr erschien der Internet Explorer 3 mit eingebauter Unterstützung für JScript, eine Scriptsprache, die sich an JavaScript orientiert. Die beiden Sprachen sind zwar ähnlich aber nicht vollständig kompatibel. Mit späteren Versionen ihrer Browser führten Microsoft und Netscape immer wieder neue Erweiterungen für Ihre jeweilige Scriptsprache ein. Der Höhepunkt dieses Konkurrenzkampfes wird heute manchmal als Browserkrieg bezeichnet. Damals wie heute ist JavaScript die wichtigste clientseitig interpretierte Scriptsprache im Web. Es ist inzwischen als ECMAScript standardisiert, durch die ISO genormt und seine Kernfunktionalität wird von allen wichtigen Browsern unterstützt. Zudem hat das W3C das Document Object Model (DOM) standardisiert – eine API über die man mit Scriptsprachen XML- und speziell XHTMLDokumente lesen und manipulieren kann. Die Syntax von JavaScript ist leicht an der von Java angelehnt, es handelt sich aber um zwei grundlegend verschiedene Sprachen. Der Name JavaScript wurde wohl aus Vermarktungsgründen 1/10 gewählt, um von der enormen Popularität von Java zu profitieren. Allerdings wurde Sprachkern von JavaScript inzwischen wiederum in viele andere Technologien übernommen. In Microsofts .Net-Umgebung und mit Apache Cocoon kann JavaScript für serverseitiges Scripting verwendet werden. Adobe Flash verwendet mit ActionScript eine stark an ECMAScript angelehnte Skriptsprache und auch in Lingo (Adobe Director) kann mit JavaScript programmiert werden. Trotz allem ist die Implementierung in den Browsern bis heute nicht einheitlich. Gerade komplexe Anwendungen erfordern ein großes Maß an Anpassung für einzelne Browser und damit verbundene Tests. Hier kommen JavaScript-Bibliotheken ins Spiel. Die Idee ist folgende: Immer wieder benötigte Funktionalität wird in einer Sammlung von Funktionen gekapselt und unter einer Open Source Lizenz veröffentlicht. Die daraus resultierende große Entwickler- und Nutzergemeinschaft stellt sicher, dass die Funktionen in möglichst vielen Umgebungen getestet werden und ihre Funktionsfähigkeit sichergestellt ist. Inzwischen steht eine große Zahl solcher Bibliotheken zur Verfügung. Je nach Umfang bezeichnet man diese auch als Toolkits oder Frameworks. Toolkits bieten normalerweise Funktionen für spezielle Einsatzbereiche, wie Animation oder UI-Komponenten. Wenn der Umfang des Toolkits darüber hinausgeht und eine grundlegend andere Art der Programmierung erfordert (oder ermöglicht) spricht man auch von Framework. Bekannte Bibliotheken sind die Yahoo! UI Library, prototype.js, Script.aculo.us, openrico und das Dojo Toolkit. Einführung in MooTools Ein sehr junges Projekt ist MooTools, auch bekannt als Moo.js. Dessen Ursprung ist Moo.Fx – eine Effektbibliothek die ursprünglich auf dem bekannten Framework prototype.js basierte. Nachdem sich zu dem Fx-Modul von Moo weitere Komponenten hinzugesellten, wurde im September 2006 ein komplettes, von prototype.js unabhängiges Paket veröffentlicht. Moo.js steht für My Object Orientated JavaScript und wird von den Entwicklern als ein ultra compact java script framework bezeichnet. Der Kern von Moo ist die Unterstützung des traditionellen Klassenkonzepts der objektorienten Programmierung – JavaScript kennt von Haus aus keine Klassen. Wie andere 2/10 Bibliotheken bietet MooTools außerdem eine Reihe von Abkürzungen zu sehr häufig benötigten Funktionen. So kann die Funktion document.getElementById(...) beispielsweise durch $(...) ersetzt werden. Darauf aufbauend enthält MooTools eine große Zahl von Klassen und Funktionen für alle Bereiche der clientseitigen Webprogrammierung. Das Framework ist dabei modular aufgebaut. Je nach Anwendungszweck kann man sich unter mootools.net eine maßgeschneiderte Distribution zusammenstellen. Somit wird sichergestellt, dass die Skripte klein und übersichtlich gehalten werden. MooTools umfasst die folgenden Komponnten, jeweils in weitere Module unterteilt: Core Der Kern von MooTools. Die Core-Komponente Moo.js kapselt die Implementierung des Klassenkonzepts. Außerdem enthält es einige allgemeine Hilfsfunktionen („utility functions“) Alle anderen Komponenten bauen auf diesem Kern auf. Native Die Native-Komponente erweitert die default-Objekte von JavaScript um zusätzliche Methoden. Sie umfasst Erweiterungen für Arrays, Elemente, Events, Funktionen und Strings. Addons Die Addons-Komponente führt eine Reihe von neuen Klassen ein: Dom bietet eine Reihe von Funktionen für den Zugriff auf Elemente einer Seite über das Document Object Model. Hash implementiert eine Map, über die mit Methoden wie zum Beispiel get() und set() zugegriffen wird. Sie bietet damit eine Alternative zur Verwendung eines assoziativen Arrays. Die Color-Klasse liefert ein Objekt zurück, das eine Farbe repräsentiert. Es bietet verschiedene Methoden zur Manipulation und zur Umrechnung des Farbwerts. Common umfasst wiederum weitere Hilfsklassen: Chain zur Verkettung von Funktionsaufrufen und 3/10 Events für das Hinzufügen von Events zu anderen Klassen. Window Window erweitert das vorgefertigte Window-Objekt von JavaScript um einige Hilfsfunktionen. Window.Size bietet beispielsweise verschiedene Methoden, um die Ausmaße des Browserfensters zu bestimmen. Remote Die Klassen der Remote-Komponente kapseln sämtliche Funktionen die für die Kommunikation über den XmlHttpRequest benötigt werden. Die Klasse XHR ist eine einfache Implementierung aller Funktionen des XMLHttpRequest-Objekts. Die Ajax-Klasse erweitert XHR wiederum um zusätzliche Funktionalität. Außerdem gibt es drei weitere Hilfsklassen: Assets, Json und Cookie. Mit Hilfe von Assets lassen sich Stylesheets, Bilder und externe JS-Dateien während der Laufzeitin eine Seite laden. Json ist eine einfache Klasse, die Methoden bietet um Objekte in JSON-Strings und umgekehrt zu konvertieren. Cookie bietet Methoden, um Cookies zu lesen und zu schreiben. Effects Die Klassen in Effects ermöglichen es, verschiedene Arten von Übergängen zwischen zwei zuständen der CSS-Eigenschaften eines Elements zu erstellen. Die Klasse Fx.Base enthält die Grundfunktionalität für solche Übergänge. Von ihr werden verschiedene andere Klassen abgeleitet. Mit Fx.Style kann beispielsweise eine einzelne CSS-Eigenschaft verändert werden, während sich Fx.Styles auf mehrere Eigenschaften auswirkt. Mit Fx.Slide können Elemente im Browserfenster bewegt werden. Mit Fx.Scroll kann man Elemente scrollen, die einen Overflow besitzen. Über die Klasse Fx.Transitions können zudem verschiedene Übergänge gewählt werden, beispielsweise Animationen denen quadratische oder exponentielle Funktionen zugrunde liegen. Das Effects-Modul stellte als Erweiterung für prototype.js den Ursprung von MooTools dar. Moo.Fx ist weiterhin auch als Version für prototype.js verfügbar. Drag 4/10 Drag enthällt zwei Klassen mit Funktionalität für Drag-and-Drop-Anwendungen. Mit Drag.Base lassen sich zwei beliebige CSS-Eigenschaften in Abhängigkeit von der Mausposition verändern. Drag.Move erweitert Drag.Base. Es kann benutzt werden, um mit dem Mousecursor ein beliebiges Element im Browserfenster zu bewegen. Plugins Plugins ist eine Sammlung von vorgefertigten „Widgets“, die ohne viel Code direkt in eine Webseite eingebunden werden können. Dabei handelt es sich vor allem um häufig benötigte Elemente für das User Interface einer Webseite. Beispiele dafür sind Akkordeon-Elemente, Schieberegler und ähnliches. Objektorientierte Programmierung in JavaScript Um zu verstehen, welche Vorzüge das Klassenkonzept von MooTools hat, ist es wichtig zu verstehen wie objektorientierte Programmierung in JavaScript grundsätzlich funktioniert. In den hier genannten Beispiele verwenden wir die Konsole der Firefox-Erweiterung Firebug (getfirebug.com). Firebug ist ein wertvolles Hilfmittel für die Entwicklung mit JavaScript. Die hier verwendete Methode console.log('irgendein string') gibt den übergebenen String auf der Konsole aus. JavaScript ist eine objektbasierte Sprache. Es kennt zwar den Begriff des Objekts, allerdings keine Klassen wie vollwertige objektorientierte Programmiersprachen. Bei klassenbasierten Sprachen wie Java haben Klassen Konstruktor-Funktionen, mit denen sich Instanzen dieser Klassen erzeugen lassen. Auch in JavaScript werden Objekte durch KonstruktorFunktionen erzeugt. Konstruktoren sind allerdings keine Methoden einer Klasse die sie instantiieren – sie sind lediglich Funktionen die ein neues Objekt zurückgeben, das ihrer eigenen Struktur entspricht. Denn in JavaScript sind Funktionen selbst auch Objekte. Das ist die Basis des etwas ungewöhnlichen Vererbungskonzepts von JavaScript. Sowohl bei der Instantiierung als auch bei der Erweiterung eines Objekts wird über das Schlüsselwort new die Konstruktorfunktion aufgerufen. Der Vorfahre eines Objekts wird als Prototyp bezeichnet, analog dazu spricht man von 5/10 prototypbasierter Vererbung. Das folgende Beispiel soll dieses Konzept der Konstruktorfunktion verdeutlichen. Ein Objekt wird erstellt, indem über new sein Konstruktor aufgerufen wird. function Himmelskoerper(galaxis){ this.galaxis = galaxis; } var sonne = new Himmelskoerper("Milchstrasse"); console.log(sonne.galaxis); //Ausgabe: Milchstrasse Jede Konstruktorfunktion verfügt über die implizite Eigenschaft prototype. Diese Eigenschaft stellt einen Verweis auf den Prototyp dieses Objekts dar. Sie definiert gemeinsame Methoden und Eigenschaften aller Konstruktorfunktionen, die diesen Prototyp erweitern. Innerhalb einer Konstruktorfunktion kann außerdem über constructor()der Konstruktor des Prototyps aufgerufen werden. Mit diesen Werkzeugen ermöglicht JavaScript über Verkettung von Konstruktorfunktionen eine Art von mehrstufiger Vererbung. Hier eine Modifikation des ersten Beispiels unter Verwendung von prototypbasierter Vererbung. Es werden zwei Konstruktorfunktionen definiert (Himmelskoerper und Planet). Durch die Zeile „Planet.prototype = new Himmelskoerper()“ erbt die Konstruktorfunktion Planet die Eigenschaften und Methoden von Himmelskoerper. Aus der Konstruktorfunktion Planet wird der Parameter galaxis an den Konstruktor seines Prototyps (also Himmelskoerper) übergeben: „this.constructor(galaxis)“. function Himmelskoerper(galaxis){ this.galaxis = galaxis; } function Planet(galaxis, atmosphaere){ this.constructor(galaxis); this.atmosphaere = atmosphaere; } Planet.prototype = new Himmelskoerper(); var saturn = new Planet("Milchstrasse", "Helium"); 6/10 console.log(saturn.galaxis()); //Ausgabe: Milchstrasse console.log(saturn.atmosphaere); //Ausgabe: Helium Das Arbeiten mit Objekten in JavaScript hat weitere Besonderheiten. So ist es möglich, einem Objekt nach dessen Erstellung neue Eigenschaften und Methoden zuzuweisen. Hier wird das Beispiel vom Anfang erweitert, indem einem erstellten Objekt eine Methode zugewiesen und diese danach aufgerufen wird: function Himmelskoerper(atmosphaere){ this.atmosphaere = atmosphaere; } saturn = new Himmelskoerper("Helium"); saturn.getAtmosphaere = function(){ return this.atmosphaere; } console.log(saturn.getAtmosphaere()); Neben der in diesem Beispiel verwendeten Notation kann für die Darstellung von Objekten (oder Datenstrukturen im allgemeinen) eine alternative Kurzschreibweise verwendet werden. Weitere Informationen zu diesem Thema gibt es unter json.org. var erde = { atmosphaere:'luft', galaxis:'milchstrasse', monde:1 } Objektorientierte Programmierung mit Moo.js Moo.js ist das Kernmodul von MooTools. Es bringt ein Klassen- und Vererbungskonzept mit, das sich grundlegend von der prototypbasierten Vererbung unterscheidet. Auf diesem Ansatz basieren alle anderen Komponenten des MooTools-Pakets. Moo.js wird einfach, wie jedes andere Skript auch, mit einem script-Element in den Header eines HTML-Dokuments eingebunden. Alle Komponenten benötigen dieses Kernmodul. Wenn man nur das Klassenkonzept und nicht die Funktionalität der anderen Komponenten benötigt, kann man den Kern allerdings auch völlig allein nutzen. 7/10 Hier zeigen wir noch einmal das bekannte Beispiel – dieses Mal unter Verwendung von Moo.js. Ein Objekt wird instantiiert, indem über new der Konstruktor seiner Klasse aufgerufen wird. Die Methode initialize hat dabei die Funktion des Konstruktors. var Himmelskoerper = new Class({ initialize: function(galaxis){ this.galaxis = galaxis; } }); var sonne = new Himmelskoerper("milchstrasse"); console.log(sonne.galaxis); Das nächste Beispiel soll zeigen, wie das Vererbungskonzept von MooTools funktioniert. Es werden zwei Klassen definiert (Himmelskoerper und Planet). Durch die Zeile var Planet = Himmelskoerper.extend({... }) erbt die Klasse Planet die Eigenschaften und Methoden von Himmelskoerper. Aus der Methode initialize der Klasse Planet wird einer der im Konstruktoraufruf übergebenen Parameter an den Konstruktor von Himmelskoerper übergeben: „this.parent(options.galaxis);“ var Himmelskoerper = new Class({ initialize: function(galaxis){ this.galaxis = galaxis; } }); var Planet = Himmelskoerper.extend({ initialize: function(options){ this.parent(options.galaxis); this.atmosphaere = options.atmosphaere; this.monde = options.monde; } }); var Saturn = new Planet({ galaxis:'Milchstrasse', atmosphaere:'Helium', monde:30 }); console.log(Saturn.galaxis); console.log(Saturn.atmosphaere); console.log(Saturn.monde); 8/10 In JavaScript kann man Objekten zur Laufzeit neue Eigenschaften und Funktionen zuweisen. In MooTools ist darüber hinaus auch möglich, bestehenden Klassen zusätzliche Eigenschaften und Methoden zu übergeben. Das wird vor allem dazu genutzt, um Klassen der Komponente Native, also beispielsweise String oder Array, mit neuen Methoden zu erweitern. Im nächsten Beispiel fügen wir der bestehenden Klasse Planet mit Hilfe von implement() eine zusätzliche Funktion hinzu. Planet.implement({ getAtmosphaere: function() { return this.atmosphaere; } }); SuggestSearch: Ein einfaches Praxisbeispiel Zum Abschluss haben wir nun noch ein kleines, stark vereinfachtes Praxisbeispiel zusammengestellt. Es zeigt eine beliebte Anwendung für „Ajax“-Technologie: Eine Suchfunktion, die bereits während der Eingabe eines Suchbegriffs Vorschläge für dessen Vervollständigung anzeigt: var ajaxBeispiel; function suche(){ var suchbegriff = $("suchfeld").getValue(); if(suchbegriff.length > 0){ ajaxBeispiel = new Ajax( "suggestSearchBackend.php?suche=" + suchbegriff, { method: 'get', update:$("ergebnisliste") } ); ajaxBeispiel.request() }else { $("ergebnisliste").setHTML(); } } window.onload = function(){ $("suchfeld").onkeyup = suche; } Hier das entsprechende HTML-Formular: 9/10 <div id="frage"></div> <form> <label for="suchfeld" >Suche</label> <input id="suchfeld" type="text"> </form> <div id="ergebnisliste"></div> Nach jeder Eingabe in das Textfeld wird die Funktion suche() aufgerufen. In Suche wird eine Instanz der Ajax-Klasse erstellt, über die dann mit einem XmlHttpRequest ein PHP-Skript aufgerufen wird, das eine Ergebnisliste zurück gibt. Diese wird direkt in das Element ergebnisliste eingesetzt. Weblinks http://mootools.net Offizielle Webseite mit Downloads, Beispielen und der Dokumentation http://moofx.mad4milk.net/ Offizielle Webseite zu Moo.Fx http://clientside.cnet.com/wiki/mootorial/ Mootorial – das Mootools Tutorial 10/10