Die Javascript Bibliothek "script.aculo.us"

Aus THM-Wiki
Wechseln zu: Navigation, Suche

Einleitung

Scriptaculous wurde von Thomas Fuchs entwickelt, einem "Javascript und Web User Interface Consultant" [1] aus Wien. Heute wird er unterstützt von einer Open Source Community. Thomas Fuchs ist außerdem an der Entwicklung des Ruby on Rails und des Prototype Framework beteiligt. Letzteres ist die Grundlage für Scriptaculous. Es nutzt alle Features, die Prototype anbietet, zum Beispiel: ein Klassenkonzept im Stile von Ruby und verkürzte Methodennamen ($('id') statt document.getElementById('id')). Scriptaculous steht unter der MIT-Lizenz und ist damit kompatibel zur verwendeten Lizenz des eStudy Portals (GPL).

Da Scriptaculous auf Prototype basiert, werden auch die gleichen Browser unterstützt [2]:

  • Microsoft Internet Explorer ab Version 6.0
  • Mozilla Firefox ab Version 1.0 / Mozilla Browser ab Version 1.7
  • Apple Safari 1.2 und höher
  • Konqueror
  • Camino
  • Opera Browser (Volle Funktionalität erst ab Version 9)

Animation Framework

Der Scriptaculous Animation Framework bietet eine vielseitige Möglichkeit, die eigene Webseite mit Effekten zu verschönern.

Kerneffekte

Die Kerneffekte bilden die Grundlage aller Effekte des Animation Framework. Die Dokumentation ist allerdings etwas widersprüchlich, was nun genau ein Kerneffekt ist. Der Blick in den Quellcode verrät aber, dass die Entwickler folgende Kerneffekte definiert haben:

  • Effect.Opacity
  • Effect.Parallel
  • Effect.Tween
  • Effect.Scale
  • Effect.Move
  • Effect.Highlight

Der Aufbau ist dabei bei allen gleich: new Effect.EffectName( element, required-params, [options] ); Mit element ist immer ein HTML-Element gemeint, entweder als String mit dessen ID, oder direkt als Javascript DOM Objekt. Der zweite Parameter required-params wird nur bei einigen Effekten benötigt (z.B. Effect.Scale), bei den übrigen entfällt er. options ist ein, wie der Name schon sagt, optionaler Parameter, über den in JSON-Notation Werte übergeben werden können. Es folgen die möglichen Bezeichner:

  • duration - Die Dauer des Effekts in Sekunden
  • fps - Die Anzahl der dargestellten Bilder pro Sekunde
  • transition - Funktion zum Ändern des aktuellen Animationsschrittes. Einige werden bereits mitgeliefert, Standardwert ist Effect.Transitions.sinoidal
  • from - Startwert des Effekts
  • to - Wert, den der Effekt erreichen soll
  • sync - Mit dieser Option können die Frames "von Hand" gerendert werden, was z.B. für Effect.Parallel wichtig ist
  • queue - Warteschlange (siehe unten)
  • delay - Hiermit kann der Start des Effekts verzögert werden
  • direction - Die Richtung des Effekts (gilt nur für Effect.Grow und Effect.Shrink)

Desweiteren gibt es noch eine Reihe von Callbacks:

  • beforeStart
  • beforeUpdate
  • afterUpdate
  • afterFinish

Beispiele

Folgender Aufruf setzt die Deckkraft des Elements mit der ID "elem" von 100% auf 30%. Der Effekt dauert eine halbe Sekunde. Das Element bleibt dabei in dem Zustand, den es beim Beenden des Effekts hat: new Effect.Opacity( 'elem', { duration: 0.5, from: 1.0, to: 0.3 } );

Der Effekt "Scale" ist einer der wenigen Effekte, die einen zusätzlichen Parameter benötigten. Sein Aufruf sieht wie folgt aus: new Effect.Scale( 'id', 120, { scaleFrom: 0 } ); Der Wert 120 besagt, dass die Größe des Elements auf 120% zur ursprünglichen Größe bei Start des Effekts gesetzt werden soll. scaleFrom gibt an, dass zur Darstellung des Effekts die Größe des Elements immer erst auf 0 gesetzt werden soll, um es dann auf die neue Größe anwachsen zu lassen. Das wiederholte Ausführen sorgt dafür, dass das Element immer weiter anwächst. Der Grund ist wieder, dass das Element in dem Endzustand des Effekts verharrt. Dies gilt für alle Effekte.

Nicht alle Effekte haben eine sichtbare optische Auswirkung. Effect.Parallel zum Beispiel hat für sich selbst keine Darstellung. Statt dessen werden andere Effekte benutzt und deren Darstellung übernommen. Konkret sorgt er dafür, dass mehrere andere Effekte parallel ausgeführt werden. Der Aufruf sieht daher auch ein wenig anders aus: new Effect.Parallel( [Array von Effekten], [options] ); Der erste Parameter ist also diesmal kein Element, sondern ein Array von verschiedenen Effekten, die parallel ausgeführt werden sollen. Diese brauchen nun unter den optionalen Parametern den Eintrag sync: true, damit Effect.Parallel die Darstellung synchronisieren kann.

Kombinierte Effekte

Mit Effect.Parallel lassen sich also die Kerneffekte miteinander kombinieren. Scriptaculous bietet bereits einige vordefinierte und kombinierte Effekte an. So ist zum Beispiel Effect.Puff das Gleiche, wie die parallel geschalteten Effekte Opacity und Scale. Hier werden nun alle kombinierten Effekte vorgestellt:

  • Effect.Appear, Effect.Fade
  • Effect.Puff
  • Effect.DropOut
  • Effect.Shake
  • Effect.Highlight
  • Effect.SwitchOff
  • Effect.BlindDown, Effect.BlindUp
  • Effect.SlideDown, Effect.SlideUp
  • Effect.Pulsate
  • Effect.Squish
  • Effect.Fold
  • Effect.Grow
  • Effect.Shrink

Dazu gibt es noch Effect.Toggle, womit sich bestimmte Effekte ein- und ausschalten lassen: new Effect.toggle( element, ['appear' | 'slide' | 'blind'], [options] ); Ein Toggle-Effekt mit dem Wert 'appear' wird also zwischen Effect.Appear und Effect.Fade umschalten. Gleiches gilt für die Blind- und Slide-Effekte.

Beispiele

Auf folgender Seite gibt es Demos für alle kombinierten Effekte: Combination Effects Demo in scriptaculous wiki

Effekt Warteschlange

Es kommt die Frage auf, wie sich mehrere Effekte nacheinander ausführen lassen. Die erste Idee ist natürlich, im Code die jeweiligen Effekte hintereinander zu schreiben, so dass zuerst der erste Effekt gestartet wird und danach der Zweite: new Effect.BlindUp( 'id' ); new Effect.BlindDown( 'id' ); Dies führt jedoch nicht zum gewünschten Ergebnis. Statt einer sequentiellen Ausführung werden beide tatsächlich gleichzeitig ausgeführt. Dadurch behindern sich beide gegenseitig, zumal sie nicht durch Effect.Parallel synchronisiert wurden. Um also Effekte geordnet nacheinander ausführen zu können, muss etwas tiefer in die Trickkiste gegriffen werden.

Mit dem optionalen Parameter "queue" kann eine Warteschlange definiert werden. Dessen Inhalt wird dann nacheinander ausgeführt. Folgende Befehle erzielen endlich den gewünschten Effekt: new Effect.BlindUp( 'id', { queue: 'front' } ); new Effect.BlindDown( 'id', { queue: 'end' } ); Es wird eine globale Warteschlange angelegt, an dessen Anfang Effect.BlindUp steht und an dessen Ende Effect.BlindDown. Die Positionsangabe kann auch über einen erweiterten Syntax angegeben werden, nämlich: new Effect.SlideUp( 'id', { queue: { position: 'end' } } ); Mithilfe dieser Syntax können der Warteschlange nun auch noch zusätzliche Parameter mitgegeben werden. Zum Beispiel kann der Warteschlange ein Name gegeben werden. Sollen auf einer Webseite nämlich mehrere Effekte unabhängig voneinander mit Warteschlangen ausgeführt werden, treten erhebliche Probleme auf. Ohne Bezeichner werden alle Effekte in die selbe Warteschlange eingefügt. Es werden alle Effekte, auch wenn sie eigentlich gar nichts miteinander zu tun haben sollen, nacheinander ausgeführt. Abhilfe schafft hier also die benannte Warteschlange, deren Syntax wie folgt aussieht: new Effect.SlideUp( 'id', { queue: { position: 'end', scope: 'bezeichner' } } ); Die Effekte können in den jeweiligen Warteschlangen gruppiert werden und es kann nicht mehr zu dem oben genannten Problem der globalen Warteschlange kommen.

Doch damit sind noch nicht alle Fallstricke aus der Welt geschafft. Was zum Beispiel, wenn bei einem Mausklick zwei Effekte nacheinander ausgeführt werden sollen? Wird nur ein einziges Mal geklickt, ist noch alles in Ordnung. Wenn nun aber mehrmals geklickt wird, bevor die Effekte zu Ende gelaufen sind, werden diese immer wieder an die Warteschlange angehängt. Dies ist möglicherweise nicht gewollt. Vielmehr sollen die Effekte durchlaufen und erst nach dem die Warteschlange abgearbeitet wurde, soll sie wieder befüllt werden dürfen. Um dieses Verhalten zu realisieren wird wieder der erweiterte Syntax der Warteschlange benötigt. Ein weiterer Parameter, der dort nun zum Einsatz kommt, trägt den Namen "limit". Damit lässt sich die maximale Größe einer Warteschlange festlegen: new Effect.BlindUp( this, { queue: { position: 'front', scope: 'begrenzteSchlange', limit: 2 } } ); Der Code erzeugt eine Warteschlange mit der Bezeichnung "begrenzteSchlange", die maximal zwei Effekten Platz bietet. Sollte die Warteschlange einmal voll sein, wird jeder weitere Effekt, der hinzugefügt werden soll, einfach ignoriert und nicht ausgeführt.

Steuerelemente

Drag & Drop

Grundlage für die meisten Steuerelemente von Scriptaculous ist Drag & Drop. Jedes HTML Element lässt sich zu einem sogenannten "Draggable" machen, welches sich mit der Maus frei verschieben lässt. Die Vesonderheit an einem Draggable ist, dass es nicht nur auf ein einzelnes Element angewendet werden kann, sondern direkt auf eine Reihe von Elementen, denen eine bestimmte CSS Klasse zugeordnet ist. Der Aufruf sieht daher wie folgt aus: new Draggable( 'id/class', [options] ); Die wichtigsten Optionen sind folgende:

  • handle - der "Griff" oder Anfasser des Elements. Wird hier ein HTML Element oder eine ID angegeben, lässt sich das gesamte Draggable nur über dieses Element verschieben
  • revert - Nach dem Loslassen geht das Element wieder an seinen Ursprungsort zurück
  • snap - Das Element bewegt sich entlang eines imaginären Rasters
  • constraint - Die Bewegung kann auf die horizontale oder vertikale Achse beschränkt werden
  • ghosting - Es wird zunächst nur eine Kopie des Elements verschoben. Erst, wenn der Drag-Vorgang beendet ist, bewegt sich das eigentliche Element an seinen neuen Platz.

Spezielle Container, auf die ein Draggable abgelegt werden kann, sind die "Droppables". Sie werden über die gleichnamige globale Hilfsklasse angelegt: Droppables.add( 'id', [options] );. Unter anderem stehen folgende Optionen zur Verfügung:

  • accept - ein String oder Array von CSS Klassen, welche akzeptiert werden sollen
  • containment - das Draggable muss ein Unterelement des hier angegebenen Elements sein
  • hoverclass - wenn ein Draggable über dem Droppable schwebt und akzeptiert werden kann, kann dies durch den Wechsel zu der hier angegebenen CSS Klasse verdeutlicht werden
  • overlap - horizontal/vertical - Akzeptiert das Element nur, wenn es mindestens zu 50% auf dem Droppable liegt

Sortables

Sortables in Aktion

Sortables sind Elemente, die sich innerhalb von (oder zwischen verschiedenen) Containern per Drag & Drop verschieben lassen. Das Sortable übernimmt dabei die gesamte Arbeit des Anlegens der verschiedenen Draggables und Droppables. Ein gutes Beispiel findet sich im Scriptaculous Wiki: Sortable Lists Demo In diesem Beispiel wird auch gleich der Unterschied klar zwischen einem Draggable mit "Griff" und ohne.

Erstellt wird ein Sortable über den Aufruf Sortable.create( 'id', [options] );. Wichtige Optionen sind:

  • tag - Gibt den Tag der Kindelemente an, welche innerhalb des Containers verschiebbar gemacht werden sollen (z.B. li oder div)
  • only - Zusätzliche Einschränkung auf die hier angegebene CSS-Klasse

Slider

Slider für die Literaturbewertung

Ein Slider ist ein Schieberegler, wie er zum Beispiel von der Lautstärkeregelung her bekannt ist. Auch dieses Steuerelement basiert auf Drag & Drop. Diesmal kommt zusätzlich die constraint-Option der Draggables zum Tragen, denn der Griff darf diesmal nur horizontal oder vertikal verschoben werden. Der Griff wird einer "Schiene" zugewiesen, auf der er sich bewegen darf. Technisch sind das lediglich zwei HTML-Elemente und ohne Styling wird der Sinn nicht ganz deutlich, der Designer muss hier also zusätzlich Hand anlegen.

Der Slider wird für die Bewertung von Büchern im Literaturmodul von eStudy eingesetzt. Mit ihm können die zur Verfügung stehenden Bewertungspunkte eingestellt und vergeben werden. Bei einer typischen Bewertungsphase stehen 1000 Punkte zur Verfügung. Jede Position des Griffs repräsentiert dabei einen bestimmten Wert, beginnend von 1 bis zum Maximum.

Der Aufruf new Control.Slider( 'id_of_slider_handle', 'id_of_slider_track', [options] ); erstellt den Slider. Auch hier wieder einige wichtige Optionen:

  • axis - 'horizontal' oder 'vertical'
  • increment - das Verhältnis von Pixel zu repräsentiertem Wert
  • range - welchen minimalen bzw. maximalen Wert der Slider darstellen soll - Angabe durch $R(min, max)
  • sliderValue - der voreingestellte Wert, den der Griff anzeigen soll
  • values - ein Integer-Array kann hier angegeben werden. Dessen Werte sind dann die einzigen Werte, die der Slider bzw. der Griff darstellen kann.

Autocompletion

Autocompleter für die Literatursuche

Die Autovervollständigung für Eingabefelder ist Ajax-Steuerelement, welches basierend auf die gerade getätigten Eingaben eine Liste mit möglichen Vervollständigungen nachlädt und diese darstellt. Die Liste der Ergänzungsworte kann beliebig gestaltet werden. Auch der Autocompleter wird im Literaturmodul eingesetzt. Dort unterstützt er bei der Suche nach neuer Literatur, bzw. dem Einstellen neuer Links anhand von bereits in anderen Kursen eingestellter Literatur.

Mit new Ajax.Autocompleter( 'id_of_text_field', 'id_of_div_to_populate', url, [options] ); wird das Objekt erzeugt. Optionen sind unter anderem:

  • paramName - Name des Eingabefeldes
  • frequency - Sekunden bis zur nächsten Eingabeprüfung und damit zum nächsten Ajax-Aufruf
  • indicator - Element zum Darstellen eines Ladebalkens während der Ajax-Aufruf noch läuft
  • afterUpdateElement - hier kann eine Funktion angegeben werden, die nach dem Einfügen des vervollständigten Textes in das Eingabefeld aufgerufen wird. Zum Beispiel kann in der Methode das zugehörige Formularfeld abgesendet werden.

Zusätzlich zur Ajax-Version gibt es auch noch eine lokale Version ohne Ajax. Dort müssen alle Einträge, die für die Autovervollständigung zur Verfügung stehen sollen, in einem Array vorgehalten werden.

In Place Editing

Um "In Place Editing" zu verstehen, folgendes Szenario: Auf einer Seite steht der Name einer Person. Um diesen Namen zu ändern, kann zum Beispiel auf ihn geklickt werden. Die Seite wird neu geladen und an Stelle des Namens wird ein Eingabefeld angezeigt. Nach der Eingabe wird das Formular abgesendet und die Seite ein zweites Mal neu geladen. Es wird wieder die Ausgangsseite angezeigt mit dem jetzt geänderten Namen. Für eine kleine Änderung musste also die komplette Seite zwei Mal neu geladen werden. Für ein solches Szenario wurde der "In Place Editor" geschaffen, mit ihm kann der Name "auf der Stelle" geändert werden. Komplett ohne ein einziges Mal die Seite neu laden zu müssen. Zum Ändern eines Wertes wird dieser versteckt und statt dessen der Wert innerhalb eines Eingabefeldes angezeigt. Soll der geänderte Wert übernommen werden, wird er mittels Ajax gespeichert, das Eingabefeld wieder versteckt und die Ausgangsansicht mit geändertem Wert wiederhergestellt.

Eingesetzt wird der In Place Editor an verschiedenen Stellen im Literaturmodul. Zum Beispiel um eine neue Kategorie zur Einsortierung von Literatur anzulegen. Oder auch zum genauen Angeben der Bewertungspunkte.

Die Objekterzeugung findet mit dem Aufruf new Ajax.InPlaceEditor( element, url, [options] ); statt. Optionen sind unter anderem:

  • okButton - ob ein Knopf zum Absenden angezeigt wird
  • okText - der Text des Knopfes

Zum herkömmlichen In Place Editor gibt es auch noch einen In Place Collection Editor, dieser zeigt ein ganzes Dropdown-Feld an.

Unit Tests

Scriptaculous wurde komplett test-getrieben entwickelt, basierend auf einem eigenen Framework. Dieser Framework kann auch für eigene Projekte verwendet werden, um die dortigen Klassen durch Testfälle abzusichern. Benötigt wird für eine Testklasse ein spezielles XHTML Grundgerüst. Innerhalb des Grundgerüsts werden die Tests angegeben, diese haben typischerweise die folgende Form:

// new Test.Unit.Runner instance
new Test.Unit.Runner({
  // optional setup function, run before each individual test case
  setup: function() { with(this) {
    // code
  }},

  // optional teardown function, run after each individual test case
  teardown: function() { with(this) {
    // code
  }},

  // test cases follow, each method which starts with "test" is considered a test case
  testATest: function() { with(this) {
    // code
  }},

  testAnotherTest: function() { with(this) {
    // code
  }}
}, { options });

Beispiel eines Testfalls:

testExample: function() { with(this) {
  var myElement = $('mydiv');
  assertEqual("DIV", myElement.tagName);
  assertEqual("DIV", myElement.tagName, "Hmm, not a DIV?");
}};

Mit with(this) wird einem das Angeben von this. erspart, was sonst vor allen Assertions stehen müsste. Verfügbar sind im übrigen die xUnit typischen Assertions, zum Beispiel assertEqual oder assertNull. Die Dokumentation zum Framework ist zwar leider recht dürftig, zahlreiche Beispiele zur Anwendung finden sich aber direkt im Quellcode von Scriptaculous. Hier können die Testfälle auch direkt ausprobiert werden: Scriptaculous Unit Tests

Literatur

Datum des letzten Besuchs aller Seiten: 18.02.2008; Da die Dokumentation ein Wiki ist, wurde auf die Angabe eines konkreten Autors verzichtet. Eine vollständige Liste aller am Wiki beteiligten Personen findet sich unter folgendem Link: Authors in scriptaculous wiki.