Nachdem wir in den ersten vier Teilen dieser Artikelserie die einzelnen Funktionen und Prinzipien von Mootools kennengelernt haben stellt sich nur noch die Frage: was kann man damit anstellen? Objekte, Klassen, Events und Dollarfunktionen schön und gut – doch was hat man im Programmieralltag davon? Diese Frage soll dieser letzte Teil der Serie anhand eines Beispiels aus dem Mootools-Core und einem Tutorial für ein selbstgebautes Plugin beantworten.

Was Klassen leisten können

Das Klassensystem von Mootools ermöglicht es, bestehende Funktionen zu verändern, ohne ihren Code anrühren zu müssen. Die Scripts bleiben sauber, modular und vor allem klein – ein Prinzip, das auch im gesamten Kerncode von Mootools seine Anwendung findet.

In Teil 3 der Artikelserie haben wir uns ja unter anderem die Request-Klasse angesehen, mit der sich bequem Ajax aus dem Ärmel schütteln lässt. Mootools hat mit Request.JSON auch eine abgewandelte Form dieser Request-Funktion dabei, die statt purem Text JSON verarbeitet. Dank des Klassensystems ist Request.JSON eine Sache von nicht mal 20 Zeilen Code:

Request.JSON = new Class({
    Extends: Request,
    options: {
        secure: true
    },

    initialize: function(options){
        this.parent(options);
        this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
    },

    success: function(text){
        this.response.json = JSON.decode(text, this.options.secure);
        this.onSuccess(this.response.json, text);
    }

});

Einfach die Ursprungsklasse um zwei Funktionen erweitern, fertig! Ganz ohne irgendwelchen Code zu hacken oder zu duplizieren – elegant und kompakt. Dieses Prinzip kann (und sollte man) auf fast jedes seiner Mootools-Scripts anwenden.

Ein Beispiel

Setzen wir uns doch mal folgende Beispielaufgabe: ein Script soll beim Klick auf einen Link ein Fenster ähnlich dem Popup der alert()-Funktion anzeigen. Spontan würde es sich anbieten, das Fenster einfach zusammenzusetzen, indem man ein entsprechendes HTML-Element erstellt und in das Dokument einfügt:

$('link').addEvent('click', function(e){
    e.stop();
    var container = $('container');
    var popup = new Element('div', {
        'class': 'popup',
        'text': 'Das hier ist der Text unseres Popup-Fensters'
    });
    popup.inject(container);
});

Das ist ganz nett und funktioniert. Aber nehmen wir doch mal an, wir würden die gleiche Funktion in eine Klasse verpacken …

Popups mit Klasse

… dann könnte das in einem ersten Schritt so aussehen:

var Popup = new Class({
    popup: null,

    // Initialisierung
    initialize: function(popupText){
        this.popup = new Element('div', {
            'class': 'popup',
            'text': popupText
        });
    },

    // Popup anzeigen
    display: function(container){
        var container = $(container);
        this.popup.inject(container);
    }
});

// Unser schönes neues Popup-Fenster
$('link').addEvent('click', function(e){
    e.stop();
    var meinPopup = new Popup('Das hier ist der Text unseres Popup-Fensters');
    meinPopup.display('container');
});

Was ist damit gewonnen außer dass die ganze Geschichte mehr Zeilen in Anspruch nimmt? Ganz einfach: Portabilität. Wollten wir unser Popup-System in eine andere Website verwenden, könnten wir jetzt die Klasse in einer JS-Datei ablegen, diese in anderen Projekten einbinden und dann am Fließband neue Popups produzieren – pro Fenster reicht dann eine einzige Code-Zeile mit new Popup('Text').display('container'). Das Ergebnis sieht am Ende gleich aus, aber ohne Klasse müssten wir jeweils den gesamten Code aufs neue einfügen.

Wo wir schon mal eine Klasse an der Hand haben bietet es sich auch an, das Popup-Fenster gleich etwas flexibler und konfigurierbarer zu gestalten:

var Popup = new Class({

    Implements: Options,
    options: {
        'className': 'popup',
        'popupText': 'Hier fehlt Text! Bitte ändern!',
        'container': document.body
    },
    popup: null,

    // Initialisierung
    initialize: function(options){
        this.setOptions(options); (unter anderem <code>document.body</code> als Container)
        this.popup = new Element('div', {
            'class': this.options.className,
            'text': this.options.popupText
        });
    },

    // Popup anzeigen
    display: function(){
        this.popup.inject(this.options.container);
    }

});

// Unser schönes neues Popup-Fenster
$('link').addEvent('click', function(e){
    e.stop();
    var meinPopup = new Popup({
        'popupText': 'Das hier ist der Text unseres Popup-Fensters'
    }).display();
});

Jetzt ist der Code noch länger, aber dafür ist das Plugin einfacher zu verwenden, bei immer noch gleichem Ergebnis. Es kann das aus Mootools bekannte Options-Objekt verwendet werden, so dass man Klasse des Popups und auch den Text einfach an einer Stelle ändern kann. Außerdem haben wir unter options Standardwerte für alle Werte notiert, so dass man bei Bedarf auch einfach new Popup.display() schreiben und trotzdem ein funktionierendes Ergebnis bekommen könnte.

Jetzt kann man die Popup-Klasse bequem in jedem Mootools-Projekt einsetzen, in dem man Popups braucht. Der Code ist portabel und flexibel. Nun könnte man sowas ähnliches vielleicht auch mit einfachen Funktionen hinbekommen, aber Mootools kann ja noch mehr.

Pimp my Popup

Unterschiedliche Websites brauchen ähnliche, aber nie ganz identische Funktionen. Deswegen ist Otto Normalwebworker oft damit beschäftigt, alten Code aus Website A in Website B zu kopieren und anzupassen. An genau dieser Stelle kann Mootools auftrumpfen – denn wir können ja unsere Klassen erweitern, ohne irgendwelchen alten Code hacken zu müssen.

Nehmen wir mal an, wir bräuchten für eine andere Website Popups mit Titelleisten und einem OK-Button um das Popup zu schließen. Die können wir ganz einfach kriegen, indem wir entsprechende Methoden in unser bewährtes Popup implementieren:

Popup.implement({

    // Eine zusätzliche Titelleiste
    addTitle: function(text){
        var titleBar = new Element('div', {
            'class': 'popupTitle',
            'text': text
        });
        titleBar.inject(this.popup, 'top');
    },

    // Und ein OK-Button
    addButton: function(text){
        var button = new Element('div', {
            'class': 'popupButton',
            'text': text
        });
        Popup.implement(Events);
        button.addEvent('click', function(){
            this.popup.destroy();
        }.bind(this));
        button.inject(this.popup, 'bottom');
    }

});

// Unser schönes neues Popup-Fenster
$('link').addEvent('click', function(e){
    e.stop();
    var meinPopup = new Popup({
        'popupText': 'Das hier ist der Text unseres Popup-Fensters'
    });
    meinPopup.addTitle('Wichtige Meldung');
    meinPopup.addButton('OK');
    meinPopup.display();
});

Damit ist unser kleines Popup ganz schön aufgemotzt – ohne dass wir ein Byte an unserer Ursprungsklasse verändern mussten. Und wenn wir wollten, könnten wir immer noch die ursprünglichen Popups ohne Extras erzeugen.

Popups der nächsten Generation

Ein verbessertes Popup-Fenster

Wenn man Erweiterungen einbaut, von denen man glaubt dass man sie später bei anderen Projekten nochmal gebrauchen kann, bietet es sich natürlich an, die Erweiterungen ihrerseits in eine Klasse zu packen, die die normale Popup-Klasse erweitert. Drag & Drop, Lightbox-Effekt und Minimieren-Button – das kann man alles umsetzen, ohne die Ursprungsklasse zu verändern.

var FancyPopup = new Class({
    Extends: Popup,
    // Code für die zusätzlichen Funktionen
});

var meinPopup = new FancyPopup({
    'popupText': 'Das hier ist der Text unseres Popup-Fensters'
});
meinPopup.addTitle('Wichtige Meldung');
meinPopup.addButton('OK');
meinPopup.addOverlay();
meinPopup.display();

Und die Erweiterungen selbst sind wieder in einer handlichen Klasse zusammengefasst, die man ihrerseits in andere Projekte portieren und dort noch mehr erweitern kann.

The Art of Moo

Die Kunst des Mootools-Programmierens liegt also einfach darin, Scripts so zu gestalten, dass sie entweder erweiterbar sind oder ihrerseits auf vorhandenen Scripts aufbauen. Das Klassensystem von Mootools macht dieses Vorhaben in der Sache einfach – man muss nur ein gewisses Maß an planerischer Weitsicht aufbringen.

Wenn man immer fleißig Klassen anlegt, baut man sich in kurzer Zeit einen Fundus an Plugins auf, die portabel und im Bedarfsfall einfach erweiterbar sind, wobei die Erweiterungen ihrerseits wiederum portabel und erweiterbar sind. Die Klassen sind zwar von der Codemenge her alle etwas länger als wenn man einfach ständig $().foo().bar() schreiben würde, aber bereits bei der ersten Portierung in ein anderes Projekt macht sich das bezahlt. Und bei der ersten Erweiterung mit einem einfachen Extend erst recht.

Und als Bonus kommt eben noch hinzu, dass man nicht alles in Klassen verpacken muss. Man kann das tun und sollte es in 90% der Fälle sicher auch tun, aber wenn man nur eben mal schnell etwas zusammenhauen möchte, geht das auch: $('foo').fade('out'); funktioniert in Mootools genau so wie bei jQuery und Konsorten. Mootools macht die einfachen Dinge einfach, hat aber auch (und vor allem) für die komplexeren Dinge alle nötigen Funktionen auf Lager. Und genau das ist der Grund, warum dieses schöne kleine Javascript-Framework unsere Aufmerksamkeit verdient.