JavaScript-Bibliotheken-Übersicht: jQuery, Dojo, YUI
Aspekte einer sinnigen JavaScript-Bibliothek
Durch die Verwendung einer JavaScript-Library gestaltet sich die tägliche Arbeit eines Web-Entwicklers wesentlich angenehmer und vor allem auch produktiver. Allerdings stellt sich einem zu Beginn eines neuen Projekts generell die Frage: Welche der tausend verfügbaren Libraries soll man eigentlich verwenden? In dem Artikel wird die Arbeit mit drei etablierten JavaScript-Bibliotheken an einfachen Beispielen gezeigt, nämlich: jQuery, Dojo und YUI. Die Auswahl erfolgte hauptsächlich anhand ihrer Reife und Etabliertheit, d.h. wie umfassend ist eine Library und wie häufig trifft man sie im Netz an.
Gewisse Merkmale gelten als Grundvoraussetzung für moderne und ausgereifte
Bibliotheken. Es muss eine browserübergreifende Unterstützung gewährleistet sein. Egal,
in welchem der Browser der JavaScript-Code ausgeführt wird, es muss dasselbe
Verhalten beobachtbar sein. Bekanntermaßen werden längst nicht alle JavaScript-API-Aufrufe von jedem Browser, bzw. jeder Version eines Browsers, unterstützt. Selbst wenn eine Unterstützung erfolgt, so heißt das noch lange nicht, dass der Aufruf einer gleichnamigen Funktion in unterschiedlichen Browsern das gleiche bewirkt. Als typisches Beispiel gilt XMLHttpRequest
, ein zentraler Bestandteil des Ajax-Ansatzes. Diese Schnittstelle hieß im Internet-Explorer einer Version kleiner 7 noch ActiveXObject
. Wollte man somit dynamisch Daten über Ajax nachladen, so musste man selbst dafür sorgen, dass die richtige Methode im jeweiligen Browser des Clients aufgerufen wurde. Mit Hilfe einer Library sollte man sich um solche browserspezifischen Merkmale nicht
kümmern müssen. Man benutzt dazu eine von der JavaScript-Bibliothek bereitgestellte Methode. Dabei ist diese Kapselung von browserspezifischen Methoden durchaus nicht trivial. Eine browserübergreifende Unterstützung kann sowohl über Feature-Detection
als auch über eine allgemeine Erkennung des Browsers erfolgen. Bei ersterem wird überprüft, ob gewisse API-Funktionalitäten von einem Browser unterstützt werden. Bei letzterem erfolgt anhand der Browser-Version ein geeigneter Aufruf. Eine detaillierte Beschreibung, wann welche Methode die beste ist, sprengt offensichtlich den Rahmen dieses Artikels. In einer Diskussion auf Stack Overflow finden sich dazu weiterführende Links [1].
Die Library soll zudem keine nativen JavaScript-Objekte direkt verändern – wie man es beispielsweise vom guten alten Prototype her kennt. Ausnahme hierbei sind Erweiterungen zur Rückwärtskompatibilität von Funktionen. Zugleich soll der globale Namespace so wenig wie möglich verschmutzt werden, d.h. es wird maximal eine globale Variable erzeugt. Implizit wird angenommen, dass geeignete Hilfsfunktionen existieren, wie beispielsweise eine each-Methode, die das Iterieren über eine Reihe von Elementen erleichtert. Eine ausreichend ausführliche und aktuelle Dokumentation darf ebenfalls nicht fehlen. Wenn die Nutzergemeinschaft groß genug ist, so wird man viele Beispiele und Anleitungen im Netz finden.
Was eine gelungene JavaScript-Library ausmacht ist Folgendes: „Mache die
einfachen Sachen einfach, die schweren möglich.“ Eine gute JavaScript-Library
hilft, den eigenen Code möglichst knapp, simpel und leicht
lesbar und daraus resultierend pflegbar zu halten.
Es gibt dabei mehrere entscheidende praktische Kriterien:
- Wie wird der Umgang mit dem DOM erleichtert?
- Welche fertigen User-Interface-Element gibt es?
- Wie einfach kann man die Library mit eigenen Funktionalitäten erweitern?
Die vorgestellten Bibliotheken werden in ihrer jeweils neuesten Version über ein CDN (Content Delivery Network) eingebunden. Folgende Versionen sind im Moment verfügbar: Dojo 1.7.1 [2], jQuery 1.7 [3] und YUI 3.4.1 [4]
Es wird in diesem Artikel immer zuerst ein Grundproblem erklärt und anhand von Beispiel-Code jeweils eine mögliche Umsetzung für die drei Libraries gezeigt. Alle Beispiele liegen mit vollständigem Quellcode auf Github [5].
Arbeiten mit dem DOM
Für die DOM-Beispiele wird von einem einfachen HTML-5-Dokument ausgegangen, das unten aufgeführt wird. Jede Library wird über
ein so genanntes Content Delivery Network (CDN) eingebunden. Das hat
mehrere Vorteile: Die Library befindet sich eventuell schon im Browser-Cache, da der Besucher auf einer Seite war, die dieselbe Datei aus demselben CDN verwendet hat. Man verwendet einen weiteren Server mit einer anderen Domain (es
sind nur eine begrenzte Anzahl von parallelen HTTP-Anfragen für eine
Domain erlaubt). Und man muss sich nicht um das Hosting
kümmern, was unter Umständen einiges an Bandbreite sparen kann. In der Regel gibt das im Ganzen einen Vorteil für die Ladezeiten
einer Page. Im Beispiel wird Dojo
über Googles, jQuery
über Microsofts und YUI
natürlich über Yahoo!s CDN eingebunden.
<!doctype html> <head> <meta charset="utf-8"> <title>JS Library Demo</title> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.xd.js"></script> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script> <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script> </head> <body> <header> <h1>JS Library Demo</h1> </header> <div id="main" role="main"> Eine Liste von wichtigen JS Leuten – nicht vollständig: <ul> <li><a href="http://brendaneich.com/">Brendan Eich</a></li> <li><a href="http://www.crockford.com/">Douglas Crockford</a></li> <li><a href="http://ejohn.org/">John Resig</a></li> </ul> </div> <script> // hier die weiteren Code-Beispiele einfügen </script> </body> </html>
Finden eines bestimmten DOM-Elements
Zum problemlosen Navigieren innerhalb des DOM kann man mittlerweile
auf CSS 3 Selektoren zurückgreifen [6]. Diese
werden von allen drei Bibliotheken standardmäßig unterstützt. Somit kann man eine beliebige CSS 3 Referenz für die richtige Syntax
zu Rate ziehen. Als
einfaches Beispiel dient hier, den Text für das zweite Element einer
ul
herausfinden – siehe obiges Beispiel-Dokument. Man will also den
Text „Douglas Crockford“ als String zurückbekommen.
Mittels CSS 3 Selektoren funktioniert das über folgenden Ausdruck:
#listOfJSPeople>li:nth-child(2)>a:first-child
Zuerst wird nach dem Element mit der ID „listOfJSPeople“ gesucht, davon das 2. Element in den n-Kindknoten von Typ li und davon der erste Kindknoten von Typ a. Was bleibt, ist aus dem Knoten den Text herauszulösen. Das sollte aber über die Library, bzw. mit Standard-JavaScript, kein Problem sein.
Dojo
In Dojo gibt es die Funktion dojo.query
var douglasCrockford = dojo.query("#listOfJSPeople>li:nth-child(2)>a:first-child")[0].innerHTML; console.log(douglasCrockford);
jQuery
jQuery unterstützt ebenfalls CSS 3 Selektoren. Eine Abfrage erfolgt folgendermaßen:
var douglasCrockford = $("#listOfJSPeople>li:nth-child(2)>a:first-child")[0].innerHTML; console.log(douglasCrockford);
YUI
YUI ist sehr modular aufgebaut, d.h. man muss genau spezifizieren,
welche Module benötigt werden. Für DOM Anfragen benutzt man das Module
„node“ und die enthaltenen Funktionen one
oder all
, d.h. genau ein
(das erste) oder alle Treffer.
CSS 3 Selektoren sind nicht im Haupt-Modul für DOM-Elemente enthalten, sondern müssen als gesondertes
Modul namens „selector-css3“ angefordert werden.
YUI().use('node', 'selector-css3', function (Y) { var douglasCrockford = Y.one("#listOfJSPeople>li:nth-child(2)>a:first-child").getContent(); console.log(douglasCrockford); });
Erzeugen eines DOM-Elements
Zum Arbeiten mit dem DOM gehört auch die dynamische Erzeugung von DOM-Elementen.
Zumeist ist das der Fall, wenn JSON-codierte Daten
innerhalb der Seite nachgeladen und gerendert werden sollen.
Angenommen man stellt fest, dass bei der Liste der JavaScript-Leute ein paar
Namen vergessen wurden, sollte man diese möglichst leicht ergänzen können. Ergänzungen geladener Webseiten um weitere Element sind ein wichtiger Bestandteil für eine fließende Benutzerinteraktion. Twitter und Facebook sind Paradebeispiele für das Nachladen von aktualisierten Informationen. Damit in der Browser-Historie das Vor- und Zurück-Navigieren bei einem dynamischen Seiten-Aufbau funktioniert, wird seit HTML-5 auf die pushState
-Funktion zurückgegriffen. Zusätzlich ist es nun erlaubt, die URL dynamisch zu ändern, sofern man sich innerhalb derselben Domain bewegt. GitHub macht von dieser Kombination bei der Navigation durch Quelltexte anschaulich Gebrauch. Für das Beispiel wird auf diese Möglichkeiten der Einfachheit halber verzichtet.
Man bekommt bespielsweise über einen Ajax-Aufruf folgendes Array von Objekten zurück – Personen, die man der Liste hinzufügen will:
var otherJSPeople = [{ "name": "Paul Irish", "url": "http://paulirish.com" }, { "name": "Ryan Dahl", "url": "http://tinyclouds.org/" }, { "name": "Dave Herman", "url": "http://www.ccs.neu.edu/home/dherman/" }];
Für die Beipiele wird davon ausgegangen, dass ein Array otherJSPeople
als Variable existiert, das mit den entsprechenden Personen-Objekten gefüllt ist. Mittels der jeweiligen Bibliothek soll die ungeordnete Liste um die weiteren Personen ergänzt werden.
Auf ausgefeilte Templating-Mechanismen wird hier der Kürze halber nicht zurückgegriffen.
jQuery
In jQuery wird eine each
-Funktion in Kombination mit einem append
verwendet.
$.each(otherJSPeople, function (index, value) {
$("#listOfJSPeople").append("<li><a href='" + value.url + "'>" + value['name'] + "</a></li>");
})
jQuery beherrscht neben append
weitere Funktionen zur
DOM-Manipulation, beispielsweise prepend
oder insertAfter
. Diese
Funktionen sind sehr hilfreich und intuitiv zu verwenden. Man findet
sie in der API-Dokumentation unter dem Stichwort „manipulation“.
Dojo
Dojo enthält eine Methode create
. Mit dieser können beliebige DOM-Elemente erzeugt werden und zu einem anderen DOM-Element hinzugefügt werden.
var jsPeople = dojo.byId("listOfJSPeople");
dojo.forEach(otherJSPeople, function (value, index) {
dojo.create("li", {
innerHTML: "<a href='" + value.url + "'>" + value['name'] + "</a>"
}, jsPeople);
});
Es wird mittels forEach
für jede Person ein neues Listen-Element erzeugt und als Kind von jsPeople
angehängt.
YUI
YUI stellt eine Methode insert
zur Verfügung, die es erlaubt, zu einem vorher gesuchten Element ein weiteres Element hinzuzufügen.
YUI().use('node', function (Y) {
var jsPeople = Y.one("#listOfJSPeople");
Y.each(otherJSPeople, function (value, key, currentObject) {
jsPeople.insert("<li><a href='" + value.url + "'>" + value['name'] + "</a></li>"); });
});
User-Interface-Komponenten
Wer jemals versucht hat, etwas komplexere User-Interface-Bausteine (UI-Bausteine) von Grund auf neu zu erstellen – auch so, dass diese produktreif in allen Browsern funktionieren – wird wissen, welche Qualen zumeist dahinter stecken und wie lange eine solche Eigenentwicklung dauern kann. Es ist daher ein sehr großer Bonus für eine JavaScript-Bibliothek, wenn sie viele hübsche, durchdachte, leicht zu benutzende und wohl getestete UI-Komponenten bereitstellt. jQuery bietet im Gegensatz zu Dojo und YUI von sich aus keine vorgefertigten UI-Komponenten an. Allerdings gibt es das jQuery-UI-Projekt [7], das diese Lücke schließt. Im Grunde ist die UI bei Dojo auch vom Kern getrennt. Dojo ist unterteilt in drei Unterprojekte: Dojo, Dijit, Dojox. Dojo ist der Kern der Library mit den Standardfunktionalitäten wie Loader oder Hilfsfunktionen, etwa Iteratoren oder Ajax-Methoden. Dijit ist die UI-Library, die Widgets und andere UI-Funktionalitäten zur Verfügung stellt. Dojox fasst stabile Erweiterungen zusammen, die (noch) nicht in Dojo oder Dijit übernommen wurden, unter anderem Incubator- oder experimenteller Code. Bei YUI findet sich keine Gruppierung von Modulen in UI-Komponenten und andere Funktionalitäten.
Ein Autocompletion-Textfeld
In diesem Beispiel soll ein Textfeld um eine einfache Auto-Vervollständigungsfunktion erweitert werden, wie man sie beispielsweise von Googles Diensten wie Mail oder Suche her kennt. In den nachfolgenden Beispielen wird von einem Array mit einschlägigen Programmiersprachen ausgegangen:
var progLangArray = ['C', 'Java', 'C++', 'PHP', 'JavaScript', 'Python', 'C#', 'Perl', 'SQL', 'Ruby', 'Shell', 'Visual Basic', 'Assembly', 'Actionscript', 'Objective C'];
Ein Nutzer soll beim Tippen in einem Textfeld passende Vorschläge bekommen.
jQuery
Für jQuery gibt es im jQuery-UI-Projekt ein Plugin, das die gewünschte Funktionalität anbietet. Dazu wird zunächst ein Element aus dem DOM selektiert und mittels der Methode autocomplete
um die Autovervollständingsfunktionalität erweitert. Es wird hierbei ein Objekt mit den beiden Feldern source
und select
übergeben. source
stellt die Daten bereit. select
weist auf eine Funktion, die aufgerufen wird, wenn ein Element aus der Liste ausgewählt wurde. In dem Beispiel wird ein alert
mit der Auswahl erzeugt.
<!doctype html> <head> <title>jQuery AutoCompletion Demo</title> <script src="../programming-languages.js"></script> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script> <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"> </script> <script> $(document).ready(function () { $("input#progLangSelect").autocomplete({ source: progLangArray, select: function (event, ui) { alert("Label: " + ui.item.label + ", Wert: " + ui.item.value); } }); }); </script> </head> <body> <label for="progLangSelect">Welche Programmiersprache?</label> <input id="progLangSelect" name="progLangSelect"> </body> </html>
Dojo
In Dijit gibt es standardmäßig keine Autovervollständigungs-UI-Komponente für ein Eingabefeld, lediglich eine UI-Element mit dieser Funktionalität für ein Drop-Down-Menu. Da dieses im Grunde genommen eine analoge, wenn nicht sogar gleiche Aufgabe hat, wird sie hier als Beispiel verwendet.
Es gibt zudem keine geradlinige Methode, um das Array mit den Programmiersprachen als Quelle zu verwenden. Hier wird daher auf einen ItemFileReadStore
zurückgegriffen, der sich um die Verwaltung von Daten kümmert.
Dazu Bedarf es einer leicht anderen Form, wie die Liste der Programmiersprachen modelliert wird:
var programmingLanguages = { label: 'name', items: [{"name": "C"}, {"name": "Java"}, {"name": "C++"}, {"name": "PHP"}, {"name": "JavaScript"}, {"name": "Python"}, {"name": "C#"}, {"name": "Perl"}, {"name": "SQL"}, {"name": "Ruby"}, {"name": "Shell"}, {"name": "Visual Basic"}, {"name": "Assembly"}, {"name": "Actionscript"}, {"name": "Objective C"}] };
Nun kann diese folgendermaßen eingebunden werden. Zunächst wird ein dojo.data.ItemFileReadStore
mit programmingLanguages
als Daten gefüllt. Über ein dijit.form.FilteringSelect
mit entsprechenden Initialisierungsparametern wird ein Drop-Down-Menu mit Autovervollständingsfunktionalität erzeugt.
<!doctype html> <html> <head> <title>Dojo AutoCompletion Demo</title> <script src="../programming-languages.js" ></script> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojo/dojo.xd.js"> </script> <script type="text/javascript"> dojo.require("dijit.form.FilteringSelect"); dojo.require("dojo.data.ItemFileReadStore"); </script> <script type="text/javascript"> dojo.addOnLoad(function() { var progLangStore = new dojo.data.ItemFileReadStore({ data : programmingLanguages }); var progLangSelect = new dijit.form.FilteringSelect({ store: progLangStore, searchAttr: "name" }, "progLangSelect"); }); </script> <link rel="stylesheet" type="text/css" href= "http://ajax.googleapis.com/ajax/libs/dojo/1.6/dijit/themes/tundra/tundra.css" /> </head> <body class="tundra"> <label for="progLangSelect">Welche Programmiersprache? </label> <input id="progLangSelect" name="progLangSelect"> <p> <button onClick="alert(dijit.byId('progLangSelect').get('value'))"> Wert in der Liste </button> <button onClick="alert(dijit.byId('progLangSelect').get('displayedValue'))"> Dargestellter Wert </button> </p> </body> </html>
YUI
YUI bietet ein Autocomplete-Eingabefeld als Modul an. Es lässt sich folgendermaßen einbinden:
<!doctype html> <html> <head> <title>YUI AutoCompletion Demo</title> <script src="../programming-languages.js" ></script> <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script> <script> YUI().use('autocomplete', 'autocomplete-highlighters', function (Y) { Y.one('body').addClass('yui3-skin-sam'); Y.one('#progLangSelect').plug(Y.Plugin.AutoComplete, { resultHighlighter: 'phraseMatch', source: progLangArray, maxResults: 7 }); }); </script> </head> <body> <label for="progLangSelect">Welche Programmiersprache? </label> <input id="progLangSelect" name="progLangSelect" value=""> </body> </html>
Es wird zu einem über Y.one
gefundenen Element ein AutoComplete
-Plugin über die Methode Y.plug
mit den in ein Objekt gekapselten Konfigurationsparametern verknüpft. Die Option resultHighlighter
gibt den Typ des Highlightings an. „phraseMatch“ bewirkt, dass die komplette Anfrage im Ergebnis hervorgehoben wird. Die Quelle wird über source
übergeben, dabei kann beispielsweise auch eine JSONP-Schnittstelle übergeben werden. maxResults
gibt an, wie viele Treffer maximal angezeigt werden.
Ein Kalender-Popup
Ein Kalender-Widget ist mittlerweile ein Element, das bei der Eingabe von Daten erwartet wird. Dadurch werden Fehleingaben reduziert. Als Beispiel kann die Buchung eines Hotels oder Flugs oder die Reservierung eine Mietwagens dienen. In den folgenden Beispielen soll ein Datum von einem Benutzer ausgewählt und das Ergebnis in einem Span-Element wiedergegeben werden. Ein passendes Widget wird von allen drei Libraries zur Verfügung gestellt.
jQuery
Wiederum wird bei jQuery auf das jQuery-UI-Projekt zurückgegriffen. Ein Kalender-Popup oder auf Englisch "date picker" lässt sich folgendermaßen einbinden:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery Calendar Demo</title> <script src="../programming-languages.js" ></script> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script> <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"> </script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"> </script> <script> $(document).ready(function() { $("#myCalendar").datepicker({ onSelect : function(dateText, inst) { $("#selectedDate").text(dateText); } }); }); </script> <style>/* eigenes CSS... */</style> </head> <body> <div id="myCalendar"></div> Ausgewähltes Datum: <span id="selectedDate"></span> </body> </html>
Über die Methode datepicker
wird ein DOM-Element um das Kalender-Widget erweitert. Im Parameter-Objekt ist über den Schlüssel onSelect
eine Funktion definiert, die aufgerufen wird, sobald ein Datum ausgewählt wurde. Diese setzt den Inhalt vom Span-Element mit der Id „selectedDate“ auf das gewählte Datum.
Dojo
Im Falle von Dojo wird eine Kalender-Komponente aus Dojox verwendet. Der Code dazu sieht folgendermaßen aus:
<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Dojo Calendar Demo</title> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js"> </script> <link href= "http://ajax.googleapis.com/ajax/libs/dojo/1.6/dojox/widget/Calendar/Calendar.css" rel="stylesheet" type="text/css"/> <script> dojo.require("dojox.widget.Calendar"); dojo.addOnLoad(function () { var cal = new dojox.widget.Calendar({}, dojo.byId("myCalendar")); dojo.connect(cal, "onValueSelected", function (date) { dojo.byId("selectedDate").innerHTML = dojo.date.locale.format(date, { selector: "date", formatLength: "short" });; }); }); </script> <style>/* eigenes CSS... */</style> </head> <body> <header> <h1>Dojo demo</h1> </header> <div id="myCalendar"></div> Ausgewähltes Datum: <span id="selectedDate"></span> </body> </html>
Beim Dojo-Beispiel wird über dojo.connect
ein Eventlistener mit der Kalenderkomponente verknüpft. Für das Event „onValueSelected
“ wird eine Callback-Funktion definiert, die den Datumswert in das entsprechende Span-Element schreibt. Das Datum wird vorher mit der Hilfsmethode dojo.date.locale.format
in ein adäquates Format gebracht.
YUI
YUI bietet das Modul calendar
an. Ein Kalender wird folgendermaßen erzeugt:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>YUI Calendar Demo</title> <script src="../programming-languages.js" ></script> <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script> <script> YUI().use('calendar', 'datatype-date', function(Y) { var calendar = new Y.Calendar({ contentBox : "#myCalendar", width : '340px', showPrevMonth : true, showNextMonth : true, date : new Date(2012, 00, 01) }).render(); // Eine Referenz zu Y.DataType.Date merken var dtdate = Y.DataType.Date; // Sich für das selectionChange event registrieren //- ein Datum wurde gewählt calendar.on("selectionChange", function(ev) { // es können je nach Einstellungen mehrere Dati gewählt // werden hier wird das erste verwendet var newDate = ev.newSelection[0]; // Das Datum formatiert rausschreiben Y.one("#selectedDate").setContent(dtdate.format(newDate)); }); }); </script> <style>/* eigenes CSS... */ </style> </head> <body> <div id="myCalendar"></div> Ausgewähltes Datum: <span id="selectedDate"></span> </body> </html>
Bei YUI wird zunächst ein Kalender-Widget an entsprechender Stelle erzeugt. Über die Methode on
wird der Kalender mit dem „selectionChange
“-Event verknüpft. Analog zu den anderen Libraries wird eine entsprechende Funktion übergeben.
Ein Slider
Slider sind sehr nützlich, wenn man einem Benutzer das mühsame Eingeben von einzelnen Werten ersparen will. Typische Anwendungsfälle sind Online-Shops, wobei der Kunde die Möglichkeit hat, das Ergebnisse einer Produktsuche über verschiedene Kombinationen aus Facetten einzuschränken. In den folgenden Beispielen soll der Wert des Sliders nach Auswahl durch einen Nutzer in ein Span-Element übertragen werden.
jQuery
Ein Slider wird über jQuery-UI folgendermaßen erzeugt:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery Slider Demo</title> <script src="../programming-languages.js"></script> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"> </script> <link type="text/css" rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css"/> <script> $(document).ready(function () { $("#slider").slider({ min: 1, max: 10, change: function (event, ui) { $("#sliderValue").text(ui.value); } }); }); </script> <style>/* eigenes CSS... */</style> </head> <body> <div id="slider"></div>Ausgewählter Wert: <span id="sliderValue"></span> </body> </html>
Der Slider wird, sobald das DOM vom Browser komplett erzeugt wurde, an dem DOM-Element mit der Id „slider“ gerendert. Als Konfigurationsparameter werden ein minimaler Wert von 1 und ein Maximum von 10 festgesetzt. Als Voreinstellung sind nur diskrete Werte erlaubt. Zugleich wird eine Callback-Funktion über den Schlüssel „change“ definiert. Diese wird aufgerufen, wenn sich der Wert des Sliders ändert. In diesem Fall wird das Span-Element mit der Id „sliderValue
“ auf den gewählten Wert aktualisiert.
Dojo
Mittels Dijit lässt sich ein Slider für Dojo so erzeugen:
<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Dojo Slider Demo</title> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js"> </script> <script type="text/javascript"> dojo.require("dijit.form.Slider"); dojo.addOnLoad(function() { var slider = new dijit.form.HorizontalSlider({ name : "slider", value : 1, minimum : 1, maximum : 10, discreteValues : 10, intermediateChanges : true, onChange : function(value) { dojo.byId("sliderValue").innerHTML = value; } }, "slider"); }); </script> <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.6/dijit/themes/tundra/tundra.css" /> <style>/* eigenes CSS... */ </style> </head> <body class="tundra"> <div id="slider"></div> Ausgewählter Wert: <span id="sliderValue"></span> </body> </html>
Beim Konstruktor der „Klasse“ dijit.form.HorizontalSlider
werden die passenden Werte gesetzt. Über den Parameter discreteValues
wird die Anzahl der diskreten Werte festgelegt. In dem Falle genau 10, minimum
1 und maximium
10. Über intermediateChange
wird definiert, dass auf Änderungen des Sliders sofort reagiert werden soll. Mittels onChange
wird eine Callback-Funktion übergeben, die bei Änderungen des Slider-Werts aufgerufen wird. Der zweite Parameter des Konstruktor gibt die id des Elements an, in dem der Slider erzeugt werden soll.
YUI
YUI besitzt ein Modul "slider", das wie folgt eingebunden wird:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>YUI Slider Demo</title> <script src="../programming-languages.js" ></script> <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script> <script> YUI().use('slider', function(Y) { var slider = new Y.Slider({min : 1, max : 10, value : 1}).render("#slider"); slider.on("slideEnd", function(event) { Y.one("#sliderValue").setContent(slider.getValue()); }); }); </script> <style>/* eigenes CSS... */ </style> </head> <body class="yui3-skin-sam"> <div id="slider"></div> Ausgewählter Wert: <span id="sliderValue"></span> </body> </html>
Zunächst wird eine Slider mit der gewünschten Konfiguration erzeugt. Ähnlich wie in den vorherigen YUI-Beispielen wird über on
ein Event mit einer Callback-Funktion verknüpft. In dem Fall des Sliders hat das gewünschte Event den Namen slideEnd
, d.h. der Slide-Vorgang ist zu Ende und der Benutzer hat den Slider „losgelassen“.
Eine eigene Erweiterung, bzw. Plugins/Module für die Library erstellen
Nun wird an einem einfachen Beispiel gezeigt, wie die Libraries individuell erweitert werden können. Dazu wird eine Library um ein Tag-Cloud-Modul erweitert.
Bei einer Tag-Cloud handelt es sich um eine sehr beliebte und einfache Methode, um die wichtigsten Tags einer Applikation, wie z.B. die Tags eines Blogs, grafisch darzustellen. Dabei werden Tags typischerweise alphabetisch sortiert und in einem Rechteck angeordnet ausgegeben. Die verwendete Schriftgröße für den einzelnen Tag ist proportional zu seiner Häufigkeit in der gesamten Tagmenge. Üblicherweise wird hierbei eine lineare Skalierung verwendet.
Angenommen man hat 20 Tag-Objekte mit Bezeichnung (name
) und entsprechender Häufigkeit (freq
). Diese Liste könnte die Tags eines Blogs enthalten, das sich mit Web-Entwicklung beschäftigt. Das Tag mit den Namen „programming“ wurde zum Beispiel 52-mal für Einträge verwendet.
var tags = [ {"name" : "javascript", "freq" : 80}, {"name" : "programming", "freq" : 52}, {"name" : "js", "freq" : 45}, {"name" : "web", "freq" : 20}, {"name" : "technology", "freq" : 44}, {"name" : "script", "freq" : 13}, {"name" : "git", "freq" : 31}, {"name" : "webdesign", "freq" : 38}, {"name" : "tools", "freq" : 43}, {"name" : "photoshop", "freq" : 10}, // usw. siehe Code auf github ];
Ohne Verwendung einer Library wird nun von folgendem Basis-Code ausgegangen:
/** * @param tags ein array mit Tag-Objekten mit jeweils * zwei Attributen: name der Name und freq die Häufigkeit * @param maxFontSize optional die Schriftgöße des häufigsten Tags * @param minFontSize optional die Schriftgröße des wenigsthäufigsten Tags */ var TagCloud = function () { // maxFontSize und minFontSize sind optional var maxDataValue = Number.MIN_VALUE, minDataValue = Number.MAX_VALUE, maxFontSize, minFontSize, i = 0, numberTags = 0, tagName1, tagName2, denominator, curTagFreq, resultHTML = ""; // Hilfsfunktion um die Tags alphapethisch zu sortieren function tagSortLexical(tag1, tag2) { tagName1 = tag1.name.toLowerCase(); tagName2 = tag2.name.toLowerCase(); if(tagName1 === tagName2) return 0; return tagName1 > tagName2 ? 1 : -1; } // suche das häufigste und wenigsthäufigste Tag function determineMinMaxDenominator(tags) { numberTags = tags.length; for (; i < numberTags; i += 1) { curTagFreq = tags[i].freq; if (maxDataValue < curTagFreq) { maxDataValue = curTagFreq; } if (minDataValue > curTagFreq) { minDataValue = curTagFreq; } } denominator = maxDataValue - minDataValue; } // Hilsfunktion, um die Schriftgröße abhängig // von der Häufigkeit eines Tags zu bestimmen function fontSizeLinear(val, maxFontSize, minFontSize) { if (denominator === 0) { return maxFontSize; } return Math.round(((val - minDataValue) / denominator) * (maxFontSize - minFontSize) + minFontSize); } /** * Rendert die Tags in einer Tag-Cloud. * * @param elementId die DOM ID des Elements, * in dem die Tag Cloud gerendert werden soll. * @param tags das Array von Tagobjekten mit name und freq * @param maxFontSize die maximale Schriftgröße * @param minFontSize die minimale Schriftgröße */ this.renderTagCloud = function (elementId, tags, maxFontSize, minFontSize) { var maxF = maxFontSize || 30; var minF = minFontSize || 10; determineMinMaxDenominator(tags); // sotiere die Tags alphabetisch tags.sort(tagSortLexical); resultHTML += "<div class='tagCloud'>"; for (var i = 0; i < numberTags; i += 1) { resultHTML += "<span class='tag' style='font-size: " + fontSizeLinear(tags[i].freq, maxF, minF) + "pt;'>" + tags[i].name + " </span>"; } resultHTML += "</div>"; document.getElementById(elementId).innerHTML = resultHTML; } }
Die Kommentare sind in der JSDoc-Notation, die an JavaDoc angelehnt ist. Es wird eine Konstruktor-Funktion TagCloud
für den entsprechenden Objekttyp definiert. Auf den „prototype“-Mechanismus wird hier der Lesbarkeit halber verzichtet. Performance-Nachteile sind dabei nicht zu erwarten. Der Compiler bzw. Interpreter sorgt für ein entsprechende Optimierung.
Die Berechnung der Tag-Cloud erfolgt über folgende Schritte, die beim Aufruf von renderTagCloud
ausgeführt werden:
- Bestimmung des häufigsten und am wenigsten häufigen Tags
- Sortierung des Tag-Arrays in alphabetischer Ordnung
- Iterieren über das sortierte Tag-Array und Erzeugen des HTML-Codes für das entsprechende Span-Element mit der berechneten Schriftgröße als CSS-Eigenschaft
- Den HTML-Code in das DOM-Element mit der übergebenen ID schreiben
Die Schriftgröße ist abhängig von der übergebenen maximalen ("maxFontSize") respektive minimalen ("minFontSize") Schriftgröße. Das häufigste Tag erhält die maximale Schriftgröße, das seltenste die minimale Schriftgröße. Zwischenwerte werden linear skaliert.
Eingebettet wird der Code in folgendes HTML-Dokument:
<!doctype html> <head> <meta charset="utf-8"> <title>Tag Cloud Plain</title> <script src="../tags.js" ></script> <script src="font-size-util-plain.js" ></script> <style type="text/css"> #tag-cloud { width: 300px; margin: 10px; padding: 10px; border: 2px dotted #999; } </style> </head> <body> <div id="container"> <header> <h1>Tag Cloud demo plain</h1> </header> <div id="tag-cloud" role="main"></div> <script> new TagCloud().renderTagCloud("tag-cloud", tags, 10, 30); </script> </body> </html>
Der obige Code wird für die weiteren Implementierungen als Basis verwendet. Komentare zu den ursprünglichen Funktionen treffen auch auf die angepassten Funktionen zu und werden der Kürze und damit Übersichtlichkeit halber weggelassen.
jQuery
jQuery lässt sich relativ einfach erweitern. Dies geschieht über $.fn.erweiterungsname
, womit jQuery um eine Methode ergänzt werden kann.
(function($) { $.fn.tagCloud = function(options) { var that = this, maxDataValue = Number.MIN_VALUE, minDataValue = Number.MAX_VALUE, i = 0, numberTags = 0, tagName1, tagName2, denominator, curTagFreq, resultHTML = ""; // enthält die Einstellungen default-Werte werden durch options überschrieben var settings = $.extend({ 'maxFontSize' : 10, 'minFontSize' : 30, 'tags' : [] }, options); function tagSortLexical(tag1, tag2) { // siehe Ausgangsbeispiel } function determineMinMaxDenominator(tags) { numberTags = settings.tags.length; for(; i < numberTags; i += 1) { curTagFreq = settings.tags[i].freq; if(maxDataValue < curTagFreq) { maxDataValue = curTagFreq; } if(minDataValue > curTagFreq) { minDataValue = curTagFreq; } } denominator = maxDataValue - minDataValue; } function fontSizeLinear(val) { if(denominator === 0) { return maxFontSize; } return Math.round(((val - minDataValue) / denominator) * (settings.maxFontSize - settings.minFontSize) + settings.minFontSize); } function renderTagCloud() { determineMinMaxDenominator(); settings.tags.sort(tagSortLexical); resultHTML += "<div class='tagCloud'>"; for(var i = 0; i < numberTags; i += 1) { resultHTML += "<span class='tag' style='font-size: " + fontSizeLinear(settings.tags[i].freq) + "pt;'>" + settings.tags[i].name + " </span>"; } resultHTML += "</div>"; that.html(resultHTML); } renderTagCloud(); return this; }; })(jQuery);
Eine Einbindung erfolgt folgendermaßen:
<!doctype html> <head> <meta charset="utf-8"> <title>jQuery Tag Cloud Plugin</title> <script src="../tags.js" ></script> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script> <script src="jquery-tag-cloud.js"></script> <style type="text/css"> #tag-cloud { width: 300px; margin: 10px; padding: 10px; border: 2px dotted #999; } </style> <script> $(document).ready(function() { $("#tag-cloud").tagCloud({ minFontSize : 10, maxFontSize : 40, tags : tags}); }); </script> </head> <body> <header> <h1>jQuery Tag Cloud Plugin</h1> </header> <div id="tag-cloud"></div> </body> </html>
Beim Aufruf von tagCloud
zu dem ausgewählten Element wird intern die Funktion renderTagCloud
aufgerufen und jQuery mittels this
zurückgegeben. Das ist ein bewährtes Pattern, um das Verknüpfen von verschiedenen jQuery-Methoden zu ermöglichen.
Dojo
Dojo unterstützt offiziell seit der 1.6er Version ein Modulsystem Namens AMD (Asynchronous Module Definition) [8]. Die Idee hinter AMD ist, dass Module und ihre Abhängigkeiten asynchron geladen werden können. Durch dieses asynchrone Modell verspricht man sich, insbesondere in einer Browserumgebung, unter anderem Vorteile bezüglich der Performanz und Testbarkeit.
Ein Modul wird durch eine define
-Methode gekapselt:
define(id?, dependencies?, factory);
Die ersten beiden Parameter sind optional. Über id
kann optional eine Modul-ID definiert werden. Diese kann sich im Falle von Dojo auch implizit aus dem Pfad zu einer Datei und einer expliziten Modul-Konfiguration ergeben – siehe dazu das HTML-Dokument weiter unten. dependecies
enthält ein Array mit Abhängigkeiten, also Module, die für das zu definierende Modul benötigt werden. Das dritte Argument, factory
, beinhaltet den eigentlichen Code für das Modul.
Folgendes Beispiel zeigt ein anonymes Modul, das ein Objekt zurückgibt.
// Import all modules in a single location define(["libs/otherModule"], function (otherModule) { // Exportieren des eigenen Modules als Objekt return { myFunction: function () { otherModule.otherExportedFunction() + 1; } }; });
Es wird das Modul otherModule
importiert. Die Funktion otherExportedFunction
aus diesem Modul kann daher für die eigene Funktion myFunction
verwendet werden.
Für das Tag-Cloud-Beispiel wird die ID implizit durch den Dateinamen festgelegt. Die Datei heißt TagCloud.js
und liegt unterhalb ./demo/util/
. Abhängigkeiten gibt es keine und es resultiert folgende Modul-Factory:
define(function() { return function() { var maxDataValue = Number.MIN_VALUE, minDataValue = Number.MAX_VALUE, maxFontSize, minFontSize, i = 0, numberTags = 0, tagName1, tagName2, denominator, curTagFreq, resultHTML = ""; function tagSortLexical(tag1, tag2) { // siehe Ausgangsbeispiel } function determineMinMaxDenominator(tags) { numberTags = tags.length; for(; i < numberTags; i += 1) { curTagFreq = tags[i].freq; if(maxDataValue < curTagFreq) { maxDataValue = curTagFreq; } if(minDataValue > curTagFreq) { minDataValue = curTagFreq; } } denominator = maxDataValue - minDataValue; } function fontSizeLinear(val, maxFontSize, minFontSize) { if(denominator === 0) { return maxFontSize; } return Math.round(((val - minDataValue) / denominator) * (maxFontSize - minFontSize) + minFontSize); } this.renderTagCloud = function(elementId, tags, maxFontSize, minFontSize) { var maxF = maxFontSize || 30; var minF = minFontSize || 10; determineMinMaxDenominator(tags); // sotiere die Tags alphabetisch tags.sort(tagSortLexical); resultHTML += "<div class='tagCloud'>"; for(var i = 0; i < numberTags; i += 1) { resultHTML += "<span class='tag' style='font-size: " + fontSizeLinear(tags[i].freq, maxF, minF) + "pt;'>" + tags[i].name + " </span>"; } resultHTML += "</div>"; document.getElementById(elementId).innerHTML = resultHTML; } } });
In der Factory wird eine Konstruktor-Funktion für ein Tag-Cloud-Objekt übergeben. Von diesem Objekt ist nur die Funktion renderTagCloud
von außerhalb sichtbar.
Das zugehörige HTML-Dokument hat folgende Form:
<!doctype html> <head> <title>Dojo Tag Cloud Plugin</title> <script src="../tags.js" ></script> <script type="text/javascript"> var dojoConfig = { async : true, packages : [{ name : "demo", // ein kleiner Hack, um an den *obligatorischen* absoluten Pfad zu kommen location : location.pathname.replace(/\/[^/]+$/, "") + "/demo" }] }; </script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js"></script> <script> require(["demo/util/TagCloud", "dojo"], function(Tagcloud, dojo) { dojo.addOnLoad(function() { new Tagcloud().renderTagCloud("tag-cloud", tags, 36, 12); }) }); </script> <style> #tag-cloud { width: 300px; margin: 10px; padding: 10px; border: 2px dotted #999; } </style> </head> <body> <header> <h1>Dojo Tag Cloud Plugin</h1> </header> <div id="tag-cloud"></div> </body> </html>
Zu beachten ist, dass bei der Dojo-Konfiguration ein Modul mit Namen "demo" auf ein Verzeichnis relativ zur Domain gesetzt wurde, also einen zum Domainnamen absoluten Pfad. Ein absoulter Pfad ist seit der 1.7er Version verpflichtend. Durch die Konfiguration werden Komponenten, die zum Modul "demo" gehören, in dieser Verzeichnisstruktur gesucht.
YUI
YUI hat ähnlich wie Dojo ein ausgefeiltes Modul-System. Über YUI.add
wird einem Modul weitere Funktionalität hinzugefügt. Als erster Parameter wird der Modul-Namespace übergeben. Als zweiter Parameter wird die gewünschte Umsetzung übergeben. Der dritte Parameter ist die Versionsnummer, im Beispiel mit 0.1 angeben. Als letzter Parameter werden Details, unter anderem Abhängigkeiten, festgelegt.
YUI.add('tagcloud-plugin', function(Y) { Y.namespace('Examples').TagCloudPlugin = Y.Base.create("TagCloudPlugin", Y.Plugin.Base, [], { initializer : function() { var div = this.get("host"), tags = this.get("tags"), maxDataValue = Number.MIN_VALUE, minDataValue = Number.MAX_VALUE, minFontSize = this.get("minFontSize"), maxFontSize = this.get("maxFontSize"), i = 0, numberTags = 0, tagName1, tagName2, denominator, curTagFreq, resultHTML = ""; function tagSortLexical(tag1, tag2) { // siehe Ausgangsbeispiel } function determineMinMaxDenominator() { numberTags = tags.length; for(; i < numberTags; i += 1) { curTagFreq = tags[i].freq; if(maxDataValue < curTagFreq) { maxDataValue = curTagFreq; } if(minDataValue > curTagFreq) { minDataValue = curTagFreq; } } denominator = maxDataValue - minDataValue; } function fontSizeLinear(val) { if(denominator === 0) { return maxFontSize; } return Math.round(((val - minDataValue) / denominator) * (maxFontSize - minFontSize) + minFontSize); } function renderTagCloud() { determineMinMaxDenominator(); // sotiere die Tags alphabetisch tags.sort(tagSortLexical); resultHTML += "<div class='tagCloud'>"; for(var i = 0; i < numberTags; i += 1) { resultHTML += "<span class='tag' style='font-size: " + fontSizeLinear(tags[i].freq) + "pt;'>" + tags[i].name + " </span>"; } resultHTML += "</div>"; console.log(div); div.setContent(resultHTML); } renderTagCloud(); }, }, { ATTRS : { tags : { value : []}, minFontSize : {value : 10}, maxFontSize : {value : 36} } }); }, "0.1", { requires : ["base", "plugin", "node"] });
Es wird ein TagCloudPlugin
definiert, das unterhalb YUI.Examples
als Plugin zur Verfügung steht. initializer
wird beim Aufruf von TagCloudPlugin
ausgeführt. ATTRS
enthält die Parameter mit entsprechend gesetzten Ausgangswerten.
Das zugehörige HTML-Dokument hat folgenden Code:
<!doctype html> <head> <meta charset="utf-8"> <title>YUI Tag Cloud Plugin</title> <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script> <script src="../tags.js" ></script> <script src="yui-tagcloud.js"></script> <script> YUI().use("node", "tagcloud-plugin", function(Y) { Y.one("#tag-cloud").plug(Y.Examples.TagCloudPlugin, { minFontSize : 10, maxFontSize : 40,tags : tags }); }); </script> <style> #tag-cloud { width: 300px; margin: 10px; padding: 10px; border: 2px dotted #999; } </style> </head> <body> <header> <h1>YUI Tag Cloud Plugin</h1> </header> <div id="tag-cloud"></div> </body> </html>
Das Plugin wird analog zu den anderen YUI-Widgets eingebunden. Als Abhängigkeit muss dazu das Modul tagcloud-plugin
angegeben werden.
Fazit
Zusammenfassend lässt sich sagen: Alle drei Libraries sind für den Produktiveinsatz durchwegs geeignet. Sie sind ausgereift und werden auf vielerlei großen Web-Sites eingesetzt. IBM verwendet Dojo in einigen Produkten und ist deshalb auch Hauptunterstützer. jQuery hat viele Freunde, unter anderem Microsoft, und ist am weitesten verbreitet – sofern man den gängigen Webseiten glauben darf, die Statistiken erheben. YUI scheint eine kleinere Community um sich zu scharen. Allerdings spendiert Yahoo! einiges an Ressourcen für die Entwicklung und Pflege. Das merkt man vor allem an der sehr detaillierten Dokumentation. jQuery ist ebenfalls ausführlich dokumentiert, und man findet kleine Code-Beispiele eingebettet in der API-Dokumentation. Diese Beispiele vermisst man bei der API-Dokumentation von YUI. Bei Dojo enthält die Dokumentation an einigen Stellen (noch?) Lücken. So manches ist exzellent beschrieben und mit ausführlichen Bespielen versehen, andere Funktionen sind hingegen sehr bruchstückhaft dokumentiert und man kann so manche Stunde damit verbringen, sich durch Foreneinträge "zu googlen". Frühere Versionen von Dojo waren ziemlich aufgebläht und wirkten eher schwergewichtig. Mit der 1.7er bzw. teilweise schon 1.6er Version wurde Dojo komplett überarbeitet und fühlt sich leichter an als die Vorgängerversionen. Wenn jemand also früher schon einmal mit Dojo gearbeitet hat und wegen der aufgeblähten Code-Basis zu einer anderen Bibliothek gewechselt ist, so könnte die aktuelle Version durchaus wieder einen Blick Wert sein.
Das Arbeiten mit dem DOM wird von allen dreien gleichermaßen vereinfacht. Auch bieten alle drei einen ähnlichen Umfang an UI-Komponenten, wobei es für jQuery sicherlich mehr von der Entwicklergemeinde bereitgestellte Plugins gibt. Das mag daran liegen, dass der Einstieg in jQuery "gefühlt" einfacher ist. Dazu gibt es eine interessante Diskussion (von November 2010, also nicht mehr komplett frisch) bei Quora, bei der John Resig (der Erfinder von jQuery) eine Frage zum Thema, wie YUI sein Image verbessern kann, beantwortet [9]. Er macht zwei Hauptpunkte aus. Der erste Punkt ist die Einfachheit aller Aspekte einer Bibliothek, das schließt die Webseite und Dokumentation mit ein – zusätzlich zum geradlinigen Umgang mit der Library. Als zweiten Punkt nennt er die Community als entscheidenden Faktor. Die Community soll intensiv in die Entwicklung mit eingebunden werden. Dadurch wird mehr Resonanz erzeugt, mehr Entwickler werden beteiligt und letztlich wird auch mehr Werbung für die Bibliothek gemacht.
Um die jeweilige Library zu erweitern, verfolgen alle drei unterschiedliche Ansätze. Erweiterungen von jQuery ergänzen traditionell die globale jQuery-Variable um weitere Funktionen. Dieses Vorgehen ist relativ intuitiv und geradlinig. Bei größeren Projekten hat man allerdings schnell das Problem, dass es typischerweise Abhängigkeiten zwischen einzelnen Plugins gibt, also z.B. ein Widget A von einem weiteren Widget B abhängig ist. Man muss dann selbst dafür sorgen, dass die Plugins in der richtigen Reihenfolge geladen werden. Bei einer größeren Code-Basis kann das ohne ausgefeilte Build-Tools sehr komplex werden. Abhilfe schaffen dabei Modul-Systeme, die sich um das Auflösen von Abhängigkeiten kümmern. Dojo setzt auf AMD und den Loader RequireJS. Die Vorteile von AMD gegenüber anderen Ansätzen werden auf der RequireJS-Homepage [10] ausführlich erläutert. Einer der Hauptgründe ist die erwähnte Verwaltung von Abhängigkeiten. Im Vergleich zu z.B. CommonJS [11] funktioniert AMD besser im Browser-Kontext. YUI hat ein ähnliches Modulsystem wie Dojo und hilft somit ebenfalls, mit Abhängigkeiten umzugehen. Allerdings ist das Modulsystem YUI-spezifisch. Mit AMD versucht man hingegen, einen gemeinsamen Nenner für die Kapselung in Module zu finden. So unterstützt RequireJS mittels AMD beispielsweise auch jQuery. Allerdings gibt es noch nicht viele jQuery-Plugins, die in der AMD Struktur definiert wurden. Die mangelnde AMD-Ünterstützung könnte YUI somit im Moment (noch) als Nachteil ausgelegt werden.
Welche Library man als geeignetste für das individuelle Projekt auswählt, ist letztendlich Geschmackssache. Allerdings lässt sich sagen, dass YUI und Dojo für komplexere Anwendungen, also so genannte richtige Fat-Clients, eine gute Wahl sind. Für kleinere Dinge mit eher leichtgewichtiger Interaktion führt jQuery wahrscheinlich schneller zum gewünschten Ergebnis.
Quellenangaben
- „Browser detection versus feature detection“, auf Stack Overflow. stackoverflow.com/questions/1294586/browser-detection-versus-feature-detection
- Dojo-Homepage. dojotoolkit.org
- jQuery-Homepage. jquery.com
- YUI-Homepage. yuilibrary.com
- Code der Beispiele des Artikels auf GitHub. github.com/woidda/mag.js-Artikel-1
- „Selectors Level 3“, Çelik et al. www.w3.org/TR/selectors/
- jQuery-UI-Homepage. jqueryui.com
- AMD. github.com/amdjs/amdjs-api/wiki/AMD
- „How could YUI3 improve its image compared to jQuery, MooTools, etc.?“ auf Quora. www.quora.com/How-could-YUI3-improve-its-image-compared-to-jQuery-MooTools-etc#ans143293
- RequireJS: Why AMD? requirejs.org/docs/whyamd.html
- CommonJS: JavaScript Standard Library. www.commonjs.org
Walter Christian Kammergruber hat an der LMU in München und an der University of Queensland in Brisbane (AU) Informatik studiert. Im Moment arbeitet er neben seiner Tätigkeit als Freiberufler an der Vervollständigung seiner Promotion an der TU München. Er versteht sich als Vollblut-Softwareentwickler und beschäftigt sich seit Jahren mit Web-Technologien. Dabei fühlt er sich im kompletten Stack von Data-Mining bis User-Interface-Design zuhause. Seine Forschungen sind auf den Bereich Social Software unter anderem im Unternehmenseinsatz ausgerichtet. Durch Ajax und neuerdings HTML5 ergeben sich mannigfaltige, interessante Interaktionsmöglichkeiten für ursprünglich von Tim Berners-Lee als statisch konzipierte Web-Seiten.