Einfache JavaScript-Architekturen für Ihr Projekt

(jQuery-)Plugin-Architektur

7. November 2013Patrick Dahms

In meinem letzten Blog-Beitrag habe ich einige Indikatoren für eine mangelhafte JavaScript-Architektur vorgestellt. In diesem Teil meiner Serie zu JavaScript-Achitekturen zeige ich Ihnen eine Möglichkeit, mit der Code besser strukturiert und dadurch wartbarer gemacht wird.

In Projekten, in denen sehr viel jQuery eingesetzt wird, tritt oftmals folgendes Problem in Erscheinung: Trotz der guten jQuery-API ist der Code schwer verständlich oder kaum wiederverwendbar. Ein häufiger Grund dafür ist, dass Funktionalitäten oder Features oft aus mehr als einem Code-Block bestehen. Darunter leidet die Übersichtlichkeit. Schnell gelangt ein neues Feature nicht in das richtige Code-Segment oder wird einfach am Ende der JavaScript-Datei angehängt. Auch mit jQuery kann (und wird!) schlechter Code geschrieben. Betrachten wir einmal folgendes Beispiel:

function getTooltip() {
    return „Magic“;
}

$(‚#Menu .open‘).click(function() {
    var tooltip = getTooltip();
    // do Magic
});

$(‚#Menu .close‘).click(function() {
    var tooltip = getTooltip();
    // do Magic
});

An diesem Code ist prinzipiell nichts auszusetzen. Beide Operationen machen etwas Unterschiedliches, gehören aber trotzdem irgendwie zusammen – zumindest über die Hilfsfunktion. Wir gehen einmal davon aus, dass diese Hilfsfunktion nur in diesen beiden Operationen verwendet wird. Problematisch ist hier der Funktions-Scope, also die Sichtbarkeit der Hilfsfunktion.

Das bringt mich zu der Idee, das komplette Verhalten in einem Aufruf zu kapseln. Dafür bietet jQuery mittels einer Plugin-Erweiterung eine tolle Möglichkeit an. Anstatt die beiden Operationen in unserem Hauptskript auszuführen, lagern wir das Verhalten in ein jQuery-Plugin aus, sodass am Ende Folgendes herauskommt:

$(‚#Menu‘).toggleMenu();

Was haben wir dadurch gewonnen? Zuerst einmal geben wir dem zusammenhängenden Code einen konkreten Namen (toggleMenu). Dieser kann beliebig sein und soll unseren Code dokumentieren. Das wichtigste ist, dass der gekapselte Code ab jetzt einen definierten Scope hat – nämlich ausschließlich innerhalb des Plugins.

Was bedeutet das? Würde, wie ursprünglich, die Hilfsfunktion irgendwo im Hauptskript definiert werden, kann sie theoretisch von jeder Operation oder Funktion aufgerufen werden. Wenn das nicht gewollt ist, benötigen wir eine eingeschränkte Sichtbarkeit, etwa einen privaten Scope.

Wie kann dieser Aufruf erreicht werden? Hier eine mögliche Lösung:

(function($){ /* 1 */
     $.fn.toggleMenu = function() { /* 2 */
         function getTooltip() { /* 3 */
             return ‚Magic‘;
         }
         return this.each(function(){ /* 4 */
             $(this).find(‚.open‘).click(function() { /* 5 */
                 var tooltip = getTooltip();
                 // do Magic
             });
             $(this).find(‚.close‘).click(function() {
                 var tooltip = getTooltip();
                 // do Magic
             });
         });
     }
     // weitere Plugins
})(window.jQuery); /* 1 */

Dieser kurze Block-Code enthält eine Basisstruktur für ein beliebiges jQuery-Plugin. Schaut man sich den Code von anderen externen Plugins an, dann befindet sich an mindestens einer Stelle etwas Ähnliches.

Wie funktioniert dieses Skript? Hier eine Erklärung anhand der Nummern:

1. IIFE-Pattern. Hiermit stellen wir sicher, dass das Plugin immer mit der jQuery-Variable ‚$‘ arbeitet und nicht vielleicht aus Versehen mit einem anderen Framework kollidiert (MooTools verwendet zum Beispiel ebenfalls die ‚$‘-Variable).
2. Über ‚$.fn‘ bietet uns jQuery einen sehr komfortablen Weg an, wie wir eigene Plugins einbinden beziehungsweise registrieren können. Der anschließende Name, in diesem Fall toggleMenu, ist der spätere Name der Funktion, die wir aufrufen. Zur Erinnerung: „$(‚#Menu‘).toggleMenu();“
3. Hier finden wir unsere Hilfsfunktion wieder, die den gleichen Aufbau hat und keine zusätzlichen Anpassungen benötigt.
4. Mittels „return this.each();“ iterieren wir über sämtliche Selektionen des Plugin-Aufrufs. Wenn wir auf eine ID selektieren, dann befindet sich natürlich nur ein Element in der Selektion. Möchten wir allerdings über den Class- oder Tag-Selektor auswählen, dann können wir damit das Plugin-Verhalten an viele Elemente anbinden. Die ‚return‘-Anweisung ist an dieser Stelle dahingehend wichtig, dass wir später weitere jQuery-Operationen auf unserer Auswahl ausführen können. Beispielsweise: $(‚#Menu‘).toggleMenu().addClass(‚aktiv‘);
5. Dies sind die ursprünglichen Operationen, mit einem Unterschied: Wir suchen mit $(this) die beiden Elemente mit der HTML-Class ‚open‘ und ‚close‘. Das $(this) bezieht sich immer auf die jeweilige Selektion (siehe Punkt 4).

Eine Plugin-Architektur zeichnet sich demnach so aus, dass wir versuchen, möglichst viele zusammenhängende Operationen in ein Plugin auszulagern. Das schützt uns natürlich nicht vor schlechtem Code, strukturiert diesen aber wesentlich besser und gibt Hilfsfunktionen einen privaten Scope, die nur innerhalb des Plugins verwendet werden können. Die eigenen Plugins sollten natürlich einen überschaubaren Rahmen haben. Das komplette Seitenskript in ein Plugin auszulagern, macht daher keinen Sinn. Um eine weitere Vorstellung davon zu bekommen, hier noch ein paar Beispiele:

$(‚.main-menu‘).mainMenu();
$(‚.sub-navigation‘).subNavigation();
$(‚.article .article-image-gallery‘).gallery(‚small‘);
$(‚.blog .article-image-gallery‘).gallery(‚big‘);

Weitere Informationen zu der jQuery-Plugin-Architektur finden Sie in dem Online-Buch „Learning JavaScript Design Patterns“ von Addy Osmani.

In meinem nächsten Blog-Beitrag zeige ich, wie sich mit dem Module Pattern weitere Möglichkeiten ergeben, Code zu strukturieren – gerade in Bezug auf die Abhängigkeiten zu anderen Funktionen oder externen Plugins.

Haben Sie Fragen oder Anmerkungen zu jQuery oder der vorgestellten Plugin-Architektur? Ich freue mich auf Ihre Kommentare.

Patrick Dahms Patrick Dahms ist Softwareentwickler am adesso-Standort Berlin mit Fokus auf Java, HTML5 und JavaScript.
Artikel bewerten:
1 Star2 Stars3 Stars4 Stars5 Stars
Loading...

Kommentare

Siarhei 15. November 2013

gut erklärt, sogar ich hab’s verstanden:) Aber im ersten Code ist ein kleiner Tippfehler aufgetreten. ‚getTooltop();‘

Patrick Dahms 19. November 2013 Website des Autors

Vielen Dank für den Hinweis. Wir haben die Stelle angepasst.

Kommentar hinzufügen:

Ihr Kommentar: