protofunc()

leichtgewichtiges, barrierearmes, generisches Hover-Plugin für jQuery

Tags: accessibility, deutsch, javascript, jquery, tutorial

Die hover-Methode von jQuery gehört wohl zu einer der beliebtesten Methoden von jQuery. Leider besitzt sie das Problem nicht barrierefrei zu sein, da die jeweils zugrunde liegenden Events (mouseenter/mouseleave) (eingabe-)geräteabhängig sind.

Die mit dem mouseenter/mouseleave häufig (aber nicht immer) korrespondierenden Events focus/blur werden dagegen selten eingesetzt. Andererseits gibt es einige fertige Scripte, die sich des Problems für bestimmte Teilbereiche angenommen haben. Sie setzen allerdings mehr oder weniger feste HTML-Strukturen voraus (In der Regel Flyout Menue Scripte mit verschachtelten Listen und darin befindlichen Links).

Ein Script, welches dieses - eigentlich einfache Problem - etwas generischer versucht zu lösen, ist mir nicht bekannt. Ich habe versucht dies vor einiger Zeit zu lösen. Hier eine Demo dieses Lösungsansatzes. Bei der Implementierung stößt man in der Regel auf mehrere Probleme:

  1. Das “Mausinteraktionselement” kann muß aber nicht immer das “Tastaturinteraktionselement”
  2. In einem “Mausinteraktionselement können mehrere “Tastaturinteraktionselemente” vorkommen
  3. Die Art der “Tastaturinteraktionselemente” ist nicht bekannt (jedes Element kann tastaturbenutzbar gemacht werden
  4. Wurde die Interaktion bereits durch die Maus getriggert, darf sie nicht nochmal durch ein focus getriggert werden und umgekehrt.

Kern der Lösung stellen hierbei die focusin/focusout Events dar, welche bubbelnde focus/blur Events darstellen und von Microsoft erfunden wurden. In standardkonformen Browsern läßt sich dieses Event dadurch nachstellen, daß man gefeuerte Events bereits in der Capturing-Phase abfängt.

Jörn Zaefferer hat für sein simples delegate-Plugin eine entsprechende focusin/focusout-Implementierung geschrieben, welche er auch in seinem bekannten Validation-Plugin nutzt. Jörn´s focusin/focusout Code sieht wie folgt aus:

Unser barrierearmes hover-Plugin soll identisch zum hover-Plugin aufgerufen werden können. Als kleinen Bonus kann man als dritten Parameter Optionen angeben:

Hierauf aufbauend nun die Grundkonstruktion unseres jQuery-Plugins:

Wir definieren als erstes ein handler-Objekt hier werden wir unsere handler-Methoden einfügen, die dann entscheiden, ob und wann die übergebenen Callback-Funktionen aufgerufen werden. Danach erstellen wir eine ID. Die ID werden wir später benötigen, da wir stark mit der data-Methode von jQuery arbeiten, um die einzelnen Zustände an den Elementen zu speichern. Für den seltenen Fall, daß man das Plugin mehrfach auf das selbe DOM-Element anwenden will, werden wir diese ID mit jedem Durchlauf des Plugins verändern. Hiernach kommt das eigentliche Plugin-Grundgerüst sowie die Default-Einstellungen.

Mit mouseDelay kann entschieden werden nach welchem Delay die jeweilige mouseenter/mouseleave Funktion auferufen werden sollen. Wir können dies sehr gut für benutzerfreundliche Flyout-Menues verwenden. Mit keyDelay können wir einen entsprechenden Delay für Tastaturuser einbauen. Allerdings macht dies hier weniger Sinn. Mit dem booleschen Wert bothOut können wir bestimmen, ob die leave-Methode nur dann aufgerufen werden soll, wenn weder das jeweilige Interaktionselement fokusiert noch die Maus drüber liegt. Mit der Option ‘useEventTypes’ können wir entscheiden, ob hover tatsächlich mit focus/blur gleichgesetzt werden soll (both). Anderenfalls können wir das Handling nur auf focus/blur (focus) oder nur auf mouseenter/mouseleave (mouse) beschränken.

Nun zum Inhalt unseres jQuery Plugins:

Nun müssen wir noch die enter-/out-Methoden definieren. Als 1. die enter-Methode (wenn die Maus drüberwandert oder ein Element fokusiert wird):

Nun müssen wir noch die leave-Methode erstellen, welche wie folgt aussieht:

Wie sich zeigt sind die enter-/leave-Methoden praktischen identisch. Die Unterschiede im Überblick: Dort wo die enter-Methode einen Wert hochzählt (inEvents), zählt die leave-Methode eins runter und dort wo die enter-Methode den Wert auf true setzt, setzt die leave-Methode den Wert false (inOutState). Daneben wird ausschließlich in der leave-Methode die Option bothOut verarbeitet.

Aus diesen geringen Unterschieden läßt sich eine kleine Funktion bauen, welche uns beide Handler-Methoden dynamisch erstellt.

Abschließend nochmals das gesamte Script sowie die Demo:

Angesichts der Tatsache, daß dieses Script nicht nur die meisten 08/15 Menue-Scripte überflüßig macht, sondern eine wirklich universell einsetzbare Lösung für unser hover-Problem darstellt, ist es wirklich klein und leichtgewichtig geraten :-).

Written March 29, 2009 by
alexander farkas

Animation-Ajax-Queue erstellen

Tags: ajax, deutsch, javascript, jquery, tutorial, video

Eine häufigere Aufgabe ist es bei Ajax Requests den alten Content durch eine Animation zu verstecken, dann den Content auszutauschen und diesen dann durch eine weitere Animation anzuzeigen.

Hierbei stellt sich das Problem, daß die genaue Dauer der Ajax-Response unbekannt ist. Ist die Response deutlich kürzer als die Verstecken-Animation, kann der Inhalt nicht ausgetauscht werden, da der User dann während dieser Animation plötzlich bereits den neuen Content sieht.

Um dieses Problem zu lösen, eignet sich jQuery´s queue Methode sehr gut, welche bereits genutzt wird, um Animationen in eine Warteschlange zu stellen. Um diesen Animationsqueue zu nutzen, muß der Ajax-Callback sich zu diesem Queue hinzufügen. Wird die Animation noch ausgeführt, wird dessen Ende abgewartet ansonsten wird die Callback-Funktion sofort ausgeführt.

Eine kleine Funktion, die aus einer Callback-Funktion eine sich in die Effekt-Warteschlange “anstellende” Funktion macht, könnte wie folgt aussehen:

Dieser Methode wird als 1. Parameter ein jQuery-DOM-Object, ein DOM-Objekt selbst oder ein CSS-Selektor, als 2. Parameter die Callback-Funktion und als 3. optionaler Parameter der Name der Warteschlange übergeben. Die Benutzung könnte so aussehen:

Folgend findet ihr eine Ajax-FX-Queue-Demo als Download.

Written March 15, 2009 by
alexander farkas

IE8, excanvas und dynamisch kreierte Canvas-Elemente

Tags: deutsch, javascript

Wer die offizielle ExplorerCanvas Version einsetzt, sollte bald gezwungen sein, diese upzudaten, da diese Version aus dem Jahr 2007 nicht den Windows Internet Explorer 8 im Standard-Modus unterstützt. Abhilfe schafft die aktuelle Revision 44 aus dem excanvas subversion repository.

Leider unterstützt diese Version wiederum nicht das dynamische Erstellen von canvas-Elementen. Abhilfe verschafft die Methode fixElement_, welche leider zugunsten des createElement-Workarounds gestrichen wurde. Eine Erweiterung der aktuellen excanvas-Revision, um wieder dynamisch kreierte canvas-Elemente im IE fixen zu können, könnte wie folgt aussehen:

Hiermit kann dann einfach das canvas-Element mit der Methode fixDynamicElement gefixt werden:

Ein Update lohnt in jedem Fall. Die Revision scheint nicht nur recht stabil zu sein, sondern enthält viele gute Bugfixes.

Hinweis: Inzwischen gibt es ein offizielles Release (3.) auf Grundlage der Revision 48. Das Projekt wird nun bei Google Code weiter vorangetrieben. Der oben genannte Fix für das dynamische Hinzufügen von Canvas-Elementen scheint aber weiterhin nötig zu sein :-(.

Written February 12, 2009 by
alexander farkas

jQuery´s step-Methode erweitern: synchrone Width-Animation

Tags: deutsch, javascript, jquery, tutorial

jQuery bietet viele Möglichkeiten der Erweiterung. Neben der relativ gut bekannten Möglichkeit das jQuery Prototype Objekt mit weiteren Methoden zu versorgen ($.fn ist ein alias für jQuery.prototype). Gibt es APIs für die Erweiterung von Events, Selektoren, easing, Ajax und der step-Methode. Letztere spielt -wie das easing - eine zentrale Rolle bei Animationen. Die bekannteste step-Erweiterung ist wohl das Color-Animation-Plugin, welches es erlaubt auch Farbwerte zu animieren.

Wir wollen eine sehr simple step-Erweiterung schreiben. Bei diesem Tutorial geht es weniger darum eine tolle Custom-Animation zu schreiben, sondern zu lernen, wie man schnell und einfach eine solche Erstellen könnte. Möchte man 2 oder mehrere unterschiedliche Elemente mit unterschiedlichen Werten synchron animieren, kann man in der Regel einfach diese Animationen gleichzeitig starten. Hierbei kommt in der Regel nichts 100%ig synchrones heraus, jedoch fällt dies dem User nicht auf.

In manchen Fällen benötigt man jedoch eine wirklich 100%ige Synchronisierung von Effekten. In diesen Fällen kann eine Erweiterung der step-Methode der mögliche Weg zum Ziel sein. Die step-Methode wird – wie der Name bereits aussagt – bei einer Animation ständig aufgerufen. Nachfolgendend findet ihr eine Demo.

Unsere Step-Methode werden wir syncWidth nennen, so dass sie bei einer Animierung der Eigenschaft syncWidth jedes mal aufgerufen wird:

Damit unsere Erweiterung weiß, welche Elemente synchronisiert werden und wieviel Platz für diese Elemente zur Verfügung stehen soll, geben wir in den Optionen zwei weitere Eigenschaften an:

Das Grundgerüst unseres Step-Plugins sieht wie folgt aus:

Als 1. Parameter übergibt jQuery uns das fx-Objekt der jeweiligen Animation. Mit Firebug können wir die in diesem Objekt zur Verfügung stehenden Informationen anschauen. Der gesamte weitere Code findet innerhalb dieses Grundgerüst statt.

Am Anfang kümmern wir uns um die initialen Berechnungen, die nur einmal ganz am Anfang durchgeführt werden müssen. Damit wir auf diese Berechnungen auch bei den späteren Aufrufen zurückgreifen können, erweitern wir einfach das fx-Objekt.

Nun kümmern wir uns letztlich darum die einzelnen CSS-Änderungen zu berechnen und bei den jeweiligen Elementen zu setzen.

Das war´s dann schon. Hier nochmals abschließend die Demo. Mit etwas Kreativität (habe ich nicht) und noch mehr Mathematik (kann ich auch nicht) kann man noch einiges aus der step-Methode rausholen. Abschließend nochmal der gesamte Code:

Written January 25, 2009 by
alexander farkas

jQuery.conConflict

Tags: deutsch, javascript, jquery

Written November 10, 2008 by
alexander farkas

jQuery UI Widgets erweitern: Am Beispiel der UI-Canvasmap (Teil II)

Tags: deutsch, javascript, jquery, tutorial

Die UI-Widgets von jQuery lassen sich relativ einfach durch neue Funktionalitäten erweitern, ohne direkt in den Code eingreifen zu müssen. Der Hauptgrund für diese Erweiterbarkeit ist – neben der Grundstruktur als eigene JS-Klasse - die häufige Verwendung von Events um Zustandsänderungen der Objekte nach außen bekannt zu machen. Daneben stellen einige UI-Widgets eine meiner Meinung nach verbesserungswürdige Plugin-API zur Verfügung.

Durch die Verwendung von Events können Dritte sowie Widget-Autoren einigen Widgets auf einfach Weise – nämlich durch Hinzufügen von Event-Handlern - zusätzliche Funktionalitäten mitgeben, ohne dass eine Vermischung von Widget-Code und Zusatzfunktionalität stattfindet. Das Verwenden von Events ist gegenüber Callback-Funktionen insofern vorteilhaft, da sie häufig wesentlich flexibler und wartungsfreundlicher eingesetzt werden können.

Die Erweiterungsaufgabe

Die Erweiterung basiert auf unserer Canvasmap. Das Tutorial zur Canvasmap sollte also bereits bekannt sein. Wir wollen nun unsere Canvasmap dahingehend erweitern, dass wenn der User einen Teil unserer Imagemap anklickt, nicht nur der Teil hervorgehoben wird, sondern auch eine hübsche Info-Box über der Karte mit weiteren Informationen erscheint. In dieser Box soll sich desweiteren noch ein „schließen-Button“ befinden.

Außerdem stellen wir als Scripter fest, dass sich auf der umzusetzenden Seite ein weiteres Widget befindet, welches ebenfalls eine Info-Box öffnet und wir daher versuchen wollen diese Box-Logik für beide Widgets zu verwenden. (Letztere Anforderung führt dazu, dass wir beim Scripten etwas umständlicher vorgehen, jedoch können wir dann den Code besser wiederverwenden.)

Diese Aufgabe habe ich versucht auf 2 verschiedene Wege zu lösen. Die Demos sind hier und hier.

Events in unseren Canvasmap-Code einbauen

Als erstes müssen wir dafür sorgen, dass unser Canvasmap-Widget besagte Events wirft, mit denen wir arbeiten können. Hierzu fügen wir eine Methode (propagate) hinzu, die diese Events mit weiteren Informationen wirft.

Diese Methode erwartet als 1. Parameter den Namen des zu triggernden Events (diesem Namen wird zusätzlich der Name des Widgets vorangestellt), als 2. optionalen Parameter ein Eventobjekt und als 3. optionalen Parameter weitergehende Informationen, welche unser Canvasmap-Objekt mit zusätzlichen optionalen Informationen erweitert.

Wir können nun diese Methode an verschiedenen Stellen unseres Scripts aufrufen, um Events und Zustandsänderungen nach außen bekannt zu geben. Wir rufen diese Methode unter anderem in unserer Click-Handler und am Ende unserer init-Methode auf:

Click-Handler:

Init-Methode:

Unseren Canvasmap Click Handler könnnten wir dann wie folgt binden:

Der Eventhandler bekommt als 1. Parameter das Eventobjekt und als 2. Parameter unser evtl. erweitertes Widget-Objekt.

Das Functional-Pattern

Das Functional-Pattern ist eine sehr schöne und einfache Art seinen Code zu organisieren. Wie der Name bereits sagt wird eine Funktion verwendet, um seinen Code zu kapseln. Die bekannteste, aber zugleich wohl komplizierteste Variante stellt das sog. Module-Pattern dar, mit welchem Singeltons erstellt werden können. Ich persönlich bevorzuge hier die Syntax des sog. Revealing Module Pattern.

Die simpleste Form des Functional-Patterns könnte so aussehen (Ob man diese bereits als Functional-Pattern bezeichnen darf, weiß ich ehrlich gesagt nicht):

Diese Form eignet sich sehr schön, um kleine Funktionalitäten zu realisieren, die keiner öffentlichen Methoden und Eigenschaften benötigen.

Sollte sich dies Ändern kann der obige Code schnell erweitert werden. Wir möchten beispielsweise, dass die Variable links eine öffentliche Eigenschaft und show eine öffentliche Methode wird, dann könnten wir das wie folgt ausdrücken:

Der Zugriff hierzu erfolgt so:

Anders als beim Module-Pattern kann die Funktion mehrmals aufgerufen werden, welche dann immer einen neuen Ausführungskontext erstellt.

Infobox-Erweiterung mit dem Functional-Pattern

Nun fangen wir mit unserer Info-Box-Erweiterung an. Diese sollte grundsätzlich in der Lage sein alle mit der UI-Widget-Factory erstellten Widgets zu erweitern. Der Grundaufbau sieht wie folgt aus:

Die Methode createDescrBox bekommt als ersten Parameter ein konkretes UI-Widget-Objekt, ein jQuery-Objekt mit den Info-Boxen sowie die Optionen, welche mit den defaults aufgefüllt werden. Aufgabe der createDescrBox-Methode ist es ein vorhandenes Objekt mit weiteren Eigenschaften und Methoden zu erweitern. Der nachfolgende Code wird komplett in die createDescrBox-Methode geschrieben.

Variabeln, die wir in mehreren Funktionen benötigen

Wir erstellen im gesamten Geltungsbereich der createDescrBox-Funktion einen Cross-Browser-tauglichen Namen für das tabindex-Attribut, eine Variable namens reFocusElm und füllen anschließend die Optionen unseres Widget-Objekts mit unseren Defaults und den übergebenen Optionen auf:

Die tabI sowie die reFocusElm Variable dient uns später dazu, den Fokus zwischen dem öffnenden Control-Element und der Infobox hin und her zu setzen, um eine bessere Zugänglichkeit zu erreichen.

Hiernach definieren wir die init-Funktion, welche in allen Boxen nach der ersten Überschrift sucht und diese als fokusierbar kennzeichnt (’-1′ steht für vom Autoren, jedoch nicht vom User fokusierbar).

Abschließend fügen wir unseren Schliessen-Button ein und belegen ihn mit einem Click-Eventhandler. Welcher einerseits ein selfclose-Event tiggert, den Fokus auf das reForcusElm zurücksetzt und abschliessend die close-Funktion aufruft.

Die close-Funktion

Die Schließen-Funktion ist recht simple. Sie triggert ebenfalls ein Close Event mit unserer propagate-Methode, führt eine kleine Fadeout-Animation durch und versteckt schließlich unser Dialog.

Die open-Funktion

Auch die open-Funktion ist sehr einfach. Als erstes schließen wir evtl. vorhandene andere Infoboxen. Danach triggern wir ein ‘beforeOpenBox’, welches wir zum Beispiel zur Positionierung des sich öffnenden Infobox verwenden können, faden unsere Infobox ein, fokusieren die 1. Überschrift in unserer Box und speichern das öffnende Kontroll-Element in die Variable reFocusElm.

Aufruf der init-Funktion und Bestimmung der öffentlichen Methoden und Eigenschaften

Abschließend rufen wir die init-Funktion auf und definieren die öffentlichen Methoden. Da wir bereits ein Objekt haben, nämlich das Widget-Objekt, geben wir kein Objekt zurück (wie oben zum Functional-Pattern gezeigt), sondern erweitern dieses einfach:

Infobox-Logik in ein Canvasmap-Objekt integrieren und aktivieren

Wir haben nun eine Funktion, welche ein jQuery UI-Widget-Objekt um Methoden zum Erstellen und Öffnen bzw. Schließen von Infoboxen erweitert. Allerdings haben wir weder definiert, daß ein canvasmap-Objekt erweitert werden soll noch wann eine Infobox geöffnet werden soll.

Dies beschreiben wir in unserer nächsten Funktion:

Wir nennen diese Methode createCanvasMapInfoBox. Sie bekommt den Selektor für die Canvasmap sowie die Optionen für die Canvasmap einerseits und die Infobox andererseits übergeben. Innerhalb dieser Funktion sowie der weiteren inneren Funktionen soll desweiteren die Variable obj bekannt sein (Diese Variable wird später eine Referenz auf unser Canvasmap-Objekt enthalten):

Der gesamte weitere Code wird in diese Methode platziert.

Die init-Funktion

An der init-Funktion lässt sich sehr schön sehen, wie wir die Events unseres Widgets als Hooks nutzen, um die zusätzliche Funktionalität zu implementieren. Wir binden hier zum einen unsere Event-Handler und starten unser canvasmap-Widget:

Alle weiteren Funktionen sind Eventhandler.

Die onMapInit-Funktion

Die onMapInit-Funktion sammelt im wesentlichen alle Info-Boxen zusammen und ruft hiernach unsere $.createDescrBox – Methode auf.

Die onSelfClose-Funktion

Jedesmal, wenn eine Info-Box geschlossen wird, ohne dass sich eine neue öffnet, wollen wir dass das Highlighting der Canvasmap ebenfalls verschwindet. Hierzu nutzen wir das, durch unsere createDescrBox eingeführte, Event ‘boxSelfClose’.

Die onMapClick-Funktion

Unser bis jetzt modifiziertes canvasmap-Objekt kennt bereits die Methoden zum Öffnen und schließen von Infoboxen, aber das wichtigste, nämlich wann eine Box geöffnet werden soll, ist noch nicht definiert. Dies erreichen wir durch die abschließende onMapClick-Funktion:

Damit ist unsere Erweiterung praktisch fertig und wir müssen nur noch die init-Funktion aufrufen.

Andere Möglichkeiten der Erweiterung

Das Script (hier nochmal die fertige Demo) oben macht sich die dynamische Natur von JavaScript zu nutze, welche es erlaubt ein bereits erstelltes Objekt durch weitere Eigenschaften und Methoden zu erweitern. Dies bedeutet, dass wir die öffentlichen Methoden unserer Erweiterung auch von außen, so aufrufen können - wie wir es von den UI-Widget auch gewöhnt sind. Nachfolgender Code würde beispielsweise beim Click auf den Button die “Spandau-Infobox” öffnen und fokusieren sowie beim Schließen (nur über selfClose) den Fokus wieder auf den öffnenden Button setzen:

Daneben ist selbstverständlich auch ein klassischer Weg der Vererbung möglich, bei dem die JS-Klasse selbst erweitert wird bzw. als Erweiterung einer weiteren JS-Klasse dient und dann automatisch erweiterte Objekte kreiert.

Als 4. Demo könnt ihr Euch ein Script anschauen, welche die obige Erweiterungsaufgabe mit dieser klassischen Technik löst. Außerdem wird hier für eine Funktionalität auf die bereits angesprochene Plugin-Architektur der UI-Widgets zurückgegriffen.

Written October 5, 2008 by
alexander farkas

Tutorial: Barrierearme Checkbox und Radio Button Ersetzung mit jQuery UI und der UI-Reflecting- Technik

Tags: accessibility, deutsch, javascript, jquery, tutorial

Demo und Script-Download

Problem und Lösungsansatz oder was ist UI-State-Reflecting

Eine gerne durchgeführte Aufgabe ist es, die in manchen Browsern recht hässlichen Checkboxen und Radiobuttons durch schönere, dem Design angepasste Eingabeelemente zu ersetzen. (Auf mögliche Verschlimmbesserungen durch schlechtes UI-Design möchte ich hier nicht eingehen. Dies gut zu machen, ist eine Herausforderung für den Designer!)
Möchte man hierbei möglichst barrierearm arbeiten, müssen die Zustände und Rollen der Eingabeelemente nicht nur optisch, sondern auch semantisch deutlich gemacht werden (hierzu eignet sich beispielsweise WAI-Aria).
Zusätzlich muss die Tastaturnutzung der Rolle und seines Zustandes entsprechend implementiert werden (für Radio Buttons für Checkboxen).

Insbesondere für die Implementierung der Tastaturnutzung für Radiobuttons ist ein etwas höherer Scripting-Aufwand nötig und leider sind die meisten JavaScript Entwickler in diesem Punkt entweder faul oder ignorant.

Reflektieren statt Simulieren

Dabei ist dies mit dem UI-Reflecting Ansatz mehr als einfach zu implementieren. Ausgangspunkt von UI-Reflecting ist die Erkenntnis, dass bereits der „Fallback“ entweder die Rollen und Zusände vollständig/teilweise abdeckt oder zumindest einen Teil der Funktionalität. In unserem Fall wird sowohl Funktionalität als auch Rolle und Zustand vollständig durch das Fallback abgedeckt und es findet lediglich eine optische Ersetzung statt. Beim UI-Reflecting lässt man also das Fallback die ganze Arbeit machen und der Entwickler bemüht sich lediglich darum dies auf dem neuen User Interface abzubilden.

UI-Widget-Factory

Die UI-Widget-Factory ist Teil von jQuery UI (in der ui.core.js). Um die Funktionsweise der Factory zu verstehen, empfehle ich folgendes UI Widget Tutorial.
Der Grundaufbau unseres unseres Scripts, wird wie folgt aussehen:

In das noch leere Object-Literal werden wir die im folgenden beschriebenen Methoden integrieren:

Die Init-Methode:

Als 1. sammeln wir alle label-Elemente, welche mit dem jeweiligen input-Element durch das for-Attribut in Beziehung stehen. Diese(s) label-Element(e) werden durch entsprechende Hintergundbilder später die Rolle und den Zustand des input-Elements deutlich machen. Solltet ihr hierfür aufgrund von Designgründen nicht, die bereits vorhanden label-Elemente nutzen, sondern ein eigenes Element für die Optik des input-Elements nutzen wollen, könnt ihr vor dem instanziieren des Checkbox-Objects einfach ein weiteres label-Element einfügen. Beispiel:

In der Regel wird dies jedoch nicht nötig sein.

Sofern es sich um ein Radiobutton handelt, holen wir uns alle Radiobuttons der Gruppe über das name-Attribut. Hintergrund ist, dass Radiobuttons sofern sie ungecheckt werden kein Cross-Browser-taugliches Event werfen und wir dies manuell nachholen müssen.

Im Anschluss verschieben wir das Original-input-Element aus dem Viewport. Dies machen wir allerdings davon abhängig, ob der User sich im Usermode (auch Kontrastmodus genannt) befindet, wofür wir ein weiteres kleines Script einsetzen. Sollte das Script nicht verwendet werden, gehen wird davon aus, dass sich der User nicht im Usermodus befindet.

Hiernach rufen wir unsere – noch zu definierende – reflectUI-Methode auf welche die Zusätnde des input-Elements durch CSS-Klassen auf unser(e) label-Element(e) überträgt.

Im Anschluss fügen wir noch die entsprechenden Event-Handler hinzu, um die Zustandsänderungen dynamisch ändern zu können. Damit das this weiterhin auf unser instanziiertes Object zeigt, setzen wir die bind-Function-Methode ein.

Hiermit ist unsere init-Methode fertig:

Die reflectUI-Methode

Diese Methode ist sozusagen der Kern unserers Scripts. Sie stellt lediglich sicher, dass die Zustände (disabled und vor allem checked) auf unser User-Interface übertragen werden.

Wird eine Veränderung zum vorherigen Zustand erkannt, ruft die Methode die propagate-Methode auf, welche in vielen jQuery-UI-Widgets vorkommt und sehr interessant ist:

Die propagate-Methode

Die propagate-Methode triggert an dem jeweiligen input-Element ein custom-Event, welche durch entsprechende Event-Objekte sowie Übergabe der eigenen Objektreferenz seine Zustände nach aussen trägt. Dies ist eine sehr gute Möglichkeit, um andere Widgets mit dem checkBox-Widget zu verbinden, ohne lästige starke Abhängigkeiten zu schaffen oder in den Original-Code einzugreifen. Es lässt sich aber auch für profaneres Gebrauchen. So kann man beispielsweise die Veränderung des checked-Status dynamisch durch eine Animation kenntlich machen.
Sofern es sich bei dem input-Element, um einen Radiobutton handelt, wird für alle Radiobuttons zusätzlich die reflectUI-Methode aufgerufen.

Die destroy-Methode

Viele jQuery-UI-Widgets enthalten eine eigene destroy-Methode, welche dazu dient, das Widget mehr oder weniger auszuschalten und den Original-Zustand herzustellen. Bei unserer destroy-Methode kann gewählt werden, ob nur das Original-input-Element wieder sichtbar sein soll. Die Events und Zustandsanzeigen am label-Element bleiben erhalten oder ob zusätzlich alle Event-Handler entfernt werden sollen.

Kleine Verfeinerungen

Hiermit ist das Script bereits fertig und einsatzbereit. Als kleine Verfeinerung unseres Scripts fügen wir noch ein paar weitere öffentliche Methoden hinzu, die uns in anderen Situation noch hilfreich sein könnten:

Fazit:

Wie sich gezeigt hat, ist der Scripting-Aufwand im Vergleich zum Ergebnis (komplette Tastaturnutzung wie Original-Elemente, richtige Rollen und Zustände bei Screenreadern etc.) relativ gering gewesen. Der Grund: Wir haben an keiner Stelle eine Tastaturnutzung oder ähnliches Scripten müssen. All das besorgt der Browser für uns.
Demo und Script-Download

Written September 6, 2008 by
alexander farkas

Das ist meine function-bind-Methode…

Tags: javascript, jquery

Es gibt viele davon, aber diese ist meine:

Zwei mögliche Nutzungsszenarien könnten so aussehen:

Nachtrag

Garret Smith hat seine performante bind-Methode veröffentlicht, welche sich an die Funktionweise des kommenden ECMA-Standards (eingebaut in Firefox 3.1) anlehnt.

Daneben hat Stuart Langridge seine Slides vom Vortrag Secrets of JavaScript closures online gestellt. Dass was Stuart in seinem Vortrag wohl gesagt haben mag, war der Grund warum ich diese Methode gepostet habe. Sie spiegelt sehr gut den Charakter von JavaScript wieder. Einerseits sieht man einen typischen Designfehler (arguments ist kein Array), andererseits sieht man gleich mehrere coole JavaScript-Features, die man in anderen Sprachen vergeblich sucht. Wenn man sich die Slides anschaut, sich vorstellt, was er wohl gesagt haben mag und sich danach diese kleine Funktion anschaut, wird man nur eines sagen können “POWER”.

Written August 16, 2008 by
alexander farkas

Ineffizienter jQuery-Code

Tags: javascript

Ich schaue recht gerne in den JS-Code anderer. Hierbei fällt einem neben der Tatsache, dass saubere Organisation von JS selten vorkommt, vor allem die ineffiziente Verwendung von jQuery auf. Hier einige dieser Anti-Patterns:

Ineffiziente CSS-Selektoren:

Beispiel:

Besseres Beispiel:

Ineffizientes Chaining:

Besseres Beispiel:

X-fache Erstellung von jQuery-Objektinstanzen identischen Inhalts (Kein Chaining/Kein “Zwischenspeichern”):

Beispiel:

Besseres Beispiel:

Erstellung von Instanzen, die einen nicht interessieren:

Beispiel:

Besseres Beispiel:

Anfordern von Informationen, die einen nicht Interessieren:

(seltenes) Beispiel:

Besseres Beispiel:

Written July 27, 2008 by
alexander farkas

Wai-Aria Grundlagen

Tags: accessibility, deutsch, javascript

WAI Aria Rollen und Eigenschaften bieten zusätzliche Semantik in Attribut-Form. Ihr erklärtes Ziel ist die Verbesserung der Zugänglichkeit von Webseiten – insbesondere für Blinde, aber auch für Tastaturnutzer sowie andere Behinderungen. Daneben sind theoretisch auch die – für eine Verbesserung der Semantik - üblichen positiven Nebeneffekte für Suchmaschinen, Browser-Features und Gebrauchstauglichkeit denkbar.

ARIA bietet zusätzliche Semantik für

  • Rollen von typischen JS-Widgets (z.B. tree, menubar)
  • jeweilige Zustände/Eigenschaften (z.B.: aria-hidden bzw. aria-haspopup, aria-required)
  • Rollen für strukturelle Elemente und Beziehungen zwischen ihnen (z.B. seealso, navigation etc.)
  • Rollen für logische Bereiche (z.B. allgemein: section, region, bei Ajax: liveregion, konkrete: main)

Aufgrund ihrer bereits breiten Unterstützung, der Lösung der wohl drängendsten Zugänglichkeitsprobleme sowie der Tatsache, dass Aria noch nicht validieren, scheinen derzeit die Rollen und Eigenschaften zur Beschreibung von Widgets am interessantesten.

Das Rollen-Konzept von WAI Aria

Das Rollen Konzept ist für Web Entwickler grundsätzlich nichts neues. Über semantisches HTML wird über die Zugänglichkeitsschnittstelle letztendlich die Rolle des Elements dem Screenreader mitgeteilt, welcher sich dann der Rolle entsprechend verhalten kann (z.B.: in Form von Information sowie Interaktionsmöglichkeiten).

Die Aria-Widget-Rollen sind an die Rollen der jeweiligen Zugänglichkeitsschnittstellen angelehnt, welche diese bereits nutzen, um Rollen von Interaktionselementen innerhalb des Betriebssystems bzw. einzelner Applikationen zugänglich zu machen. Aufgabe der Browser ist es die Aria-Rollen auf die genaue Rolle der jeweiligen Zugänglichkeitsschnittstelle zu „mappen”. (Beispiele des Aria-Rollen-Mappings für Gecko-Browser je nach vorhandener Schnittstelle).

Einhaltung von Interaktionskonventionen

Aus dem obigen Konzept folgt semantische Gleichheit/Ähnlichkeit zwischen einem Aria-Widget und seinem Pendant innerhalb von „richtigen” Applikationen.

Das nachfolgende Beispiel enthält die selben semantischen Informationen wie die Menüleiste des Firefox-Browser (nicht alle).

Beispiel für eine Menüleiste :

Jaws würde dies beim ersten fokussieren ungefähr wie folgt vorlesen: Menüleiste – Menupunkt – Datei – Untermenü – Sie können mit den Pfeiltasten links / rechts navigieren.

Das Tastaturverhalten wird weder von Browser noch vom Screenreader geliefert, sondern ist alleinige Sache des Autors.

Hierbei sollte er sich beim Scripten der Interaktionsmöglichkeiten, am Original-Verhalten des typischen Widgets innerhalb des Betriebssystems orientieren.

Die WAI-ARIA Best Practices sowie das AOL Developer Network enthalten hierzu, zu einigen Widgets entsprechende Empfehlungen für Maus- und Tastatur-Interaktionen.

Die Bedeutung des Focus

Damit der Screenreader die Rolle sowie Eigenschaften des jeweils aktiven Widget-(Unter-)Elements begreifen kann, muss dieses – insbesondere wenn es Interaktionsmöglichkeiten bietet - in der Regel fokusiert werden. Um die Fokusierbarkeit bei allen Elementen zu ermöglichen, wurde das tabindex-Attribut zum Universalattribut deklariert. Bekommt das tabindex-Attribut den Wert „0″, ist es in der normalen Tabreihenfolge durch den User fokussierbar. Hat es dagegen den Wert „-1″, liegt es außerhalb der Tabreihenfolge, kann jedoch vom Autoren mit JavaScript fokussiert werden (Bei Aria-Widgets für „Unterelemente” der Normalfall.). (Näheres zum Setzen/Auslesen des tabindex per JavaScript.)

Fokus setzen

Für das eigentliche Fokussieren durch den Autoren stehen zwei Möglichkeiten bereit:

  1. Die focus-Methode des jeweiligen Element-Objekts
  2. Das Attribut activedescendant

Um den Fokus mit der focus-Methode zu setzen, sollte diese mit einem Timeout aufgerufen werden, um Fehlverhalten in den Browsern zu umgehen. Eine Funktion die dies übernimmt, könnte beispielsweise so aussehen:

Bei meinen Tests mit einem Accordion-Widget hat sich gezeigt (hier gibt es keine spezifischen Aria-Rollen), dass ein Timeout von mehr als 180ms notwendig ist, damit Jaws (9.0) beim Vorlesen den neu angezeigten Bereich nicht überspringt.

Bei der activedesendant-Methode bekommt das tatsächlich fokussierte Element das Attribut activedescendant mit der ID des „virtuell” fokussierten Elements.

Stylen des Fokus

Daneben muss das fokussierte Element entsprechend gestylt werden. Damit dies richtig funktioniert, sollte nicht auf die Pseudo-Selectoren :focus/:active zugegriffen, sondern beim Event „focus” eine entsprechende CSS-Klasse gesetzt und beim Event „blur” wieder entfernt werden.

Alles im Blick

Bei vielen Widgets wird der Autor die Tastaturfunktionalität über die Pfeiltasten realisieren und hierbei das default–Verhalten des Browser unterbinden müssen. Daher sollte darauf geachtet werden, dass insbesondere die wichtigen Bereiche des Widgets immer im Viewport bleiben. Grundsätzlich wird dies bereits durch das Fokusieren des aktiven Elements erledigt. In bestimmten Fällen kann man sich hierauf alleine jedoch nicht verlassen.

Gründe hierfür können sein:

  • der interessante Bereich ist größer z.B. kleiner Menüpunkt öffnet großes Untermenü
  • man fokusiert nicht „physisch” sondern mit Hilfe des activedescendant-Attributs
  • Das fokusierte Element spannt sich nicht vollständig auf

In diesen Fällen kann mit der Methode „scrollIntoView”, CSS-Anpassung oder ganz kompliziert durch Abfragen des Viewports, des Scrollbereichs sowie der Position des DOM-Elements der neue scrollLeft/scrollRight-Wert berechnet und das DOM-Element in den Viewport geschoben werden.

fliegender Wechsel zwischen Maus und Tastaturnutzung

Bei vielen Widgets sollte der Autor darauf achten, dass ein Wechsel zwischen Maus und Tastaturnutzung möglich ist. D. h. Hat der User beispielsweise einen Menüpunkt aktiviert und drückt der User auf die Pfeiltaste rechts, muss das Untermenü – sofern vorhanden – geöffnet und der 1. Menüpunkt aktiviert werden. Ähnlich sieht es beispielsweise bei einem Slider-Widget aus. Hat der User es mit der Maus aktiviert und fängt an zu ziehen, sollte mit den Pfeiltasten link/rechts ebenfalls in Kontakt treten können, ohne vorher mit Tab den Fokus selbst setzen zu müssen.

1.0 Semantik überschreiben

Das Role Attrbibut überschreibt die Sematik herkömmlicher HTML-Elemente. Hierbei sollte darauf geachtet werden, dass keine „falsche” Semantik zurückbleibt. Diese kann mit der Rolle „presentation” überschrieben werden.

Beispiel (Menuleiste):

Grds. sollte es im obigen Beispiel bereits ausreichen das 2. ul Element mit der Presentation-Rolle zu belegen, die dazugehörigen li-Elemente sollten dann ebenfalls nicht als Listenelemente an die Zugänglichkeitsschnittstelle übertragen werden.

Wer ownt, der ownt

Aria schreibt ähnlich wie HTML vor, dass einige Elemente immer Kindelement einer bestimmten Rolle sein müssen. Dies gilt beispielsweise für die Rolle option, welches ein Kind von select oder einer ähnlichen Rolle sein muss. In manchen Fällen ist dies technisch nicht möglich (Ein input-Element kann keine Kinder haben.) oder widerspricht dem Aufbau der UI-Komponente. In diesen Fällen kann das owns-Attribut verwendet werden, welches anzeigt, dass ein Element das Eltern-Element eines anderen ist.

Beispiel (Combobox):

inhaltliche Bedeutung nicht vergessen

Hat man sein Widget mit Aria semantisiert bzw. verwendet eine Bibliothek, welche einem diese Arbeit abnimmt, sollte man ganz besonders darauf achten, ob die inhaltliche Bedeutung des Widgets klar wird. Nutzt man beispielsweise ein Slider-Widget hilft die ganze semantische Auszeichnung nichts, wenn nicht klar wird, was die Eingabe mit Hilfe des Sliders bewirken soll.

Hierfür stehen häufig verschiedene Möglichkeiten zur Verfügung:

  1. der Text-Knoten des jeweiligen Elements
  2. das title-Attribut des jeweiligen Elements
  3. ein mit dem Element verknüpftes label-Element ([for=widget-id])
  4. Ein Textknoten eines anderen Elements auf das das Widget mit labeldby verweist

Umfangreichere Informationen zum Widget können mit dem Attribut describedby zugänglich gemacht werden.

Fallstricke & Bugs

Grundsätzlich ist Wai-Aria sehr leicht zu implementieren, allerdings gab es bei meinen Versuchen folgende Probleme/offene Fragen:

  • Barrierefreiheit 1.0 vs. 2.0

    In einigen Fallen weicht das Aria-Konzept von dem ab, was normalerweise beim konventionellen, barrierearmen JavaScript gemacht wird. Dies betrifft insbesondere 2 Punkte.

    1. Aria implementiert eine verbesserte Tastaturnutzung insbesondere mit Pfeiltasten. Tabben soll in der Regel innerhalb des Widgets nicht verwendet werden können. Vielmehr soll der User hierdruch das Widget verlassen. Screenreader-/Browserkombinationen, die jedoch kein Aria verstehen, könnten einerseits die Nutzung der Pfeiltasten für sich beanspruchen und nicht an den Browser weiterleiten andererseits übernehmen sie häufig Fokusänderungen durch den JavaScript-Autoren nicht.
    2. Viele Aria-Widgets sollen explizit die display Eigenschaft ‘none’ nutzen, welche mit dem Aria Attribut ‘hidden’ korrespondiert. Währendem nach konventionellem barrierearmen Javascript man in einigen dieser Fälle eher den Inhalt lediglich aus dem Viewport schieben würde.
  • Nicht vorhandene, aber evtl. allgemein doch nutzbare Rollen?
    Im Web existieren häufig Widgets, welche in Applikationen nicht vorkommen und auch in WAI-ARIA nicht spezifiziert wurden.
    Ein Beispiel hierfür wäre beispielsweise ein typisches Accordion. Die Zugänglichkeit eines solchen Widgets kann sicherlich ebenfalls mit Aria erhöht werden, es stellt sich allerdings die Frage welche Rollen und sonstigen Eigenschaften dem Accordion-Widget am nächsten kommen, ohne dass gleichzeitig eine „falsche” Bedienmöglichkeit suggeriert wird.
  • Bugs im Screenreader
    Daneben zeigen sich insbesondere in Jaws noch einige Bugs, die anscheinend darauf zurückzuführen sind, dass die - normalerweise von „richtigen” Programmen benutzen - Rollen noch nicht ausreichend an die Erfordernisse im Web angepasst wurden.

Fazit

Trotz einiger Bugs und anderer Schwierigkeiten ist das Potential von Aria nicht zu unterschätzen. Mich hat schwer beeindruckt, wie sich die Gebrauchstauglichkeit in Screenradern von vielen alltäglichen JavaScript-Widgets mit recht einfachen Mitteln auf hohem Niveau verbessern lässt.

Gute Ressourcen

Wer Lust auf mehr bekommen hat, kann auf eine wachsende Anzahl von Tutorials, Artikeln und Beispielen zurückgreifen:

Written June 23, 2008 by
alexander farkas
« older posts