protofunc()

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:

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

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 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. 1.der Text-Knoten des jeweiligen Elements
  2. 1.das title-Attribut des jeweiligen Elements
  3. 1.ein mit dem Element verknüpftes label-Element ([for=widget-id])
  4. 1.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

Die jQuery UI-Widget-Factory am Beispiel einer Canvas Map: Teil I

Tags: deutsch, javascript, jquery, tutorial

Wir müssen nicht mehr lange warten und jQuery UI 1.5 wird ist released. Neben einigen Komponenten und hübschen Effekten, bietet der Core eine – für Plugin-Autoren – interessante Widget-Factory, welche ich mal am Beispiel einer Canvas Imagemap näher unter die Lupe nehmen möchte. Als erstes sollte man sich daher die neuesten Versionen von jQuery, UI und explorercanvas (wir wollen ja, dass die Beispiele auch ein bisschen im IE funktionieren.) besorgen. Hier findet ihr eine - zugegebenermaßen - häßliche Demo des einfachen CanvasMap-Plugins (ich bin kein Grafiker und scheitere bereits bei Imagemaps :-).

Die HTML Struktur und das CSS

Bevor wir anfangen solltet ihr einen kleinen Blick in das HTML werfen. Ihr werdet sehen, dass wir erstens ein Bild mit der Karte Berlins haben und zweitens ein transparentes Bild, welches die eigentliche ImageMap darstellt. Der Grund hierfür ist einfach. Canvas erzeugt anders als SVG richtige Pixelgrafixen. Man kann hier leider keine Events, wie beispielsweise mouseover gezielt abfangen. Wir brauchen daher eine darüber liegende Interaktionsebene für den User.

Was macht die Widget-Factory?

Im Prinzip macht die Widget-Factory nicht sehr viel, aber sie macht es geschickt. Sie erstellt aufgrund eines – von uns zu definierenden - „Prototypen-Objekts“ ein ordinäres jQuery-Plugin und eine JS-Klasse. Das jQuery-Plugin dient hierbei als Initialisierungs- und Zugriffs-“Controller“ für die definierte Klasse. Um das folgende Tutorial zu verstehen sollte man also Grundkenntnisse in JavaScript-OOP besitzen.

Der Grundaufbau eines jQuery Widgets könnte so aussehen:

Die Widget-Factory erstellt uns nun die gewünschte Klasse und das jQuery-Plugin:
Mit folgendem Ausdruck können wir unsere Klasse instantiieren:

Innerhalb unserer Instanzmethoden können wir auf das #map-Element mit this.element (Es handelt sich hierbei um ein jQuery-Objekt von #map.) und auf die Optionen mit this.options zugreifen (d.h. also this.options.defaultOptionB enthält den String ‘ mein Freund’ und defaultOptionA ist weiterhin der default-Wert ‘Hello’).
Wenn wir unsererem Widget ganz zu Anfang noch einige Dinge mitgeben möchten wie z.B. CSS-Klassen setzen, Events-Binden, zusätzliche Elemente hinzufügen, können wir eine Instanzmethode namens init definieren. Diese wird automatisch vom Constructor unserer Klasse aus aufgerufen (Den Constructor selbst liefert die Widget-Factory.).

Startcode unserers Canavasmap-Widgets

Wir wollen später das unser Widget wie folgt aufgerufen werden kann:

Das #map-Element ist hierbei nicht die Imagemap selbst, sondern ein Container, welcher die Bilder und die Imagemap umfasst.

Als erstes erstellen wir unseren Namespace (pf für pfrotofunc) und definieren die 1. Methode sowie unsere Defaults für unser Plugin namens canvasmap. In der Initialiserungsmethode erstellen wir ein Canvas-Element fügen es in einen div-Container und platzieren es vor das Imagemapbild. Der Container ist für das später auszuführende explorercanvas notwendig. Als nächstes überprüfen wir, ob das Canvas-Malobjekt (this.canvas[0].getContext) zur Verfügung steht.
Wenn dem nicht so ist, haben wir es wahrscheinlich mit dem IE zu tun und rufen G_vmlCanvasManager.initElement auf dem Element auf, wandeln es in ein jQuery-Objekt und speichern es in unsere canvas Eigenschaft.

Nach einer weiteren „Sicherheitsabfrage“ speichern wir unser Malobjekt in this.ctx und binden unsere Events.

Da die Browser recht unterschiedlich mit Events bei Imagemaps umgehen, belegen wir sowohl das Bild als auch die einzelnen areas der Imagemap mit dem click und mouse-over/out – Event und da wir unobtrusiv und barrierearm arbeiten, stellen wir den Mausevents noch focus- und blur-Events zur Seite.

Der aufmerksame Betrachter wird feststellen, dass die Eventhandler, die gebindet wurden, noch gar nicht existieren. Hierzu kommen wir aber zum Schluss zurück, wenn der übrige Aufbau unserer Klasse feststeht.

Die weiteren Instanzmethoden

Die nachfolgenden Methoden werden – wie die init-Methode - ebenfalls im Canvas-Prototypen-Objekt gespeichert und mit der $.widget-Factory in eine Instanzmethode unserer $.pf.canvasmap-Klasse umgewandelt.

1. Die getArea-methode

Da wir - wie oben gesehen haben - unsere Events auch auf das Bild gelegt haben, aber wir natürlich mit der jeweiligen area-Arbeiten müssen (hier stehen nämlich die wesentlichen Informationen zum Malen drin.), müssen wir aus unserem Event die area extrahieren.

Die getArea-Methode bekommt sowohl das Event-Objekt als auch das DOM-Element von unserem Eventhandler übergeben und muss nun entscheiden, welches von beidem die gewünschte Image-Map area enthält. Sofern sie keine area findet, ist irgendetwas schief gelaufen und sie gibt false zurück.

2. Die getCoords-Methode

Aus unserer Area müssen wir nun die einzelnen Koordinaten, welche im Attribut coords durch Komma separiert gespeichert sind, extrahieren. Daneben müssen wir das ganze noch in Zahlen umwandeln. Wir speichern das ganze dann in ein Array ab und geben es zurück.

3. Die drawArea-Methode

Nun sind wir kurz vor dem eigentlichen Mal-Vorgang. Wir benötigen für unser Beispiel nur den Shape-Typ ‘poly’, wir wollen aber später evtl. noch weitere Typen hinzufügen. Daher enthält unsere drawArea-Methode gar nicht den eigentlichen Mal-Code, sondern lediglich die Entscheidung, ob wir den Shape-Typen unterstützen und welche Mal-Methode aufgerufen werden soll:

Die Methode extrahiert also den Shape-Typ aus der Area, überprüft ob wir eine solche Methode definiert haben und wenn ja übergibt drawArea die Koordinaten sowie die Maloptionen an die eigentliche Mal-Methode.

4. Die poly-Methode

Nun malen wir aber wirklich:

Als erstes sagen wir unserem Malobjekt, was wir malen wollen. Dann bewegen wir unseren Zeichenstift zur ersten Koordinate. Danach gehen wir das Array bei 2 beginnend, immer um zwei Schritte durch und ziehen unsere Linien. Sobald wir hiermit fertig sind, ziehen wir unsere Verbindungslinie zwischen End- und Anfangspunkt. Und zuletzt Füllen wir unsere gezeichneten Linien unseren übergebnenen Optionen (o) entsprechend aus und zeigen eine Border an.

5. Die drawClickArea-Methode

Um den Sinn der letzten Methode zu verstehen, muss man etwas zu canvas wissen. Es handelt sich wie gesagt um Pxielgrafiken. Man kann also nicht einfach sein Malobjekt verändern, sondern muss mit jedem Schritt das gesamte Bild löschen und von neuem anfangen. Gleichzeitig wollen wir aber, dass ein angeklickter Bereich auch dann noch gehighlightet wird, wenn man mit der Maus drüber fährt. Dafür speichern wir unseren geklickten Bereich in unserer Objektinstanz unter der Eigenschaft ‘ clickArea’ ab und rufen diese Methode beim hovern regelmäßig auf.

Nun haben wir alle Methoden zusammen und können uns den noch fehlenden Event-Handlern widmen:

Wie ihr seht löschen wir innerhalb dieser Funktionen das Canvas und rufen letzlich immer nur die bereits oben definierten Methoden auf.

Das Zwischen-Ergbenis unseres Canvasmap-Plugins.

Und was habe ich davon?

Dies werden sich nun einige fragen. Als 1. haben wir objekt-oriientierten Code hinterlassen. Dies hat in verschiedener Hinsicht Vorteile. So haben wir beispielsweise alle Zustände und Eigenschaften aus der DOM-Map sauber in unserer Objektinstanz zwischengespeichert. Die Eigenschaft areas zeigt beispielsweise nur auf die Areas unserer einen Imagemap, selbst wenn wir mehrere Imagemaps auf unserer Seite haben und diese mit unserem Plugin behandeln.

Einige jQuery-Plugin Autoren werden nun aufschreien und konstatieren, dass sie den selben Effekt mit der herkömmlichen Architektur ebenfalls hin bekommen. Dies stimmt tatsächlich. Je nachdem, ob man Funktion oder Variable außerhalb oder innerhalb der this.each-Funktion definiert, erreicht man einen derartigen „Speicherzustand“ entweder für alle DOM-Elemente des aktuellen jQuery-Objekts innerhalb seines Plugins oder für jedes einzelne DOM-Element innerhalb des Plugins.

Das hängt stark mit der Funktionsweise von Closures zusammen. Das Problem hierbei ist, dass eine Menge hilfreicher Funktionen und Variablen privat sind und schwer von außen zugänglich gemacht werden können.

Stellt euch beispielsweise mal vor euer Kunde kommt auf euch zu und will nun eine weitere Funktionalität implementiert haben. Ausserhalb der Imagemap soll eine Legende mit Kontrollelementen erscheinen. Sobald der User auf eines dieser Elemente klickt, soll ein bestimmter Stadtteil gehighlightet werden. Die gesamte Funktionalität habt ihr ja im Prinzip geschrieben.

Der Autor, welcher die herkömmliche Schreibweise von jQuery-Plugins benutzt hat, wird nun erklären, dass er für das Hinzufügen der neuen Funktionalität eine halbe Stunde benötigt. Daneben wird ihm – vorausgesetzt er steht auf sauber organisierten Code – der Kopf rauchen wie er diese Funktionalität sauber und flexibel in sein Plugin und möglichst als Option integriert.

Der Widget-Factory-Autor wird selbstverständlich selbiges behaupten, braucht aber beispielsweise keine Zeit sich zu überlegen wie er seinen Code nun neu organisieren muss und hat noch genügend Zeit für eine oder zwei Kippen :-).

Zugriff von außen

Die UI-Factory Methode fügt unserem jeweiligen DOM-Element unsere Objektinstanz - ähnlich einem DOM-Expando (nur Memory Leak sicher) - hinzu. Wir haben daher auf unsere gesamte Objektinstanz mit folgendem Ausdruck Zugriff.

In der Regel werden wir dies nicht benötigen, da die Factory uns eine sehr schöne Möglichkeit bietet einzelne Methoden von aussen zu erreichen. Hierbei ruft man das normale jQuery-Plugin auf und übergibt als 1. Parameter den Namen der Instanzmethode. Alle weiteren Parameter werden ebenfalls als Parameter an die Methode weitergegeben:

Dieser Code sagt also nichts anderes als: Mal die area mit der id ’spandau’ mit folgenden Optionen aus.

Einfacher geht es nicht oder? Demo für den Zugriff von außen.

Ausblick auf Teil II des Tutorials

Neben der Widget-Factory hat sich das UI-Team auch Gedanken darüber gemacht, wie die „Aussenwelt“ über entscheidende Zustandsänderungen innerhalb des Widgets informiert werden und wie man ein Widget modularisieren kann, um beispielsweise den Widget-Core klein zu halten oder um Dritten die Möglichkeit zu geben die Funktionalität zu erweitern/abzuändern, ohne dass eine Zeile des Original-Codes verändert werden muss. Wie man sein eigenes Widget derartig „pimpen“ kann soll der zweite Teil zeigen.

Written June 8, 2008 by
alexander farkas

Start Using Accessible JavaScript

Tags: accessibility, ajax, deutsch, javascript

Obwohl ich dem Artikel Stop using Ajax! von James Edwards (”Brothercake”) und dem „zur Seite springen” von Chris Heilmann inhaltlich nur zustimmen kann, hat mir der Artikel nicht gefallen. Neben einem kurzen Hinweis auf einen Lösungsweg für Übermorgen bzw. einen möglichen Workaround empfand ich ihn vor allem als de-konstruktiv. In vielerlei Hinsicht lenkt er auch vom eigentlich Hauptproblem für Javascripter und möglichen Lösungen ab. Zusammen mit dem Standard-Test in den vielen Barrierefreiheitschecklisten (Schalten Sie JavaSript im Browser aus und überprüfen Sie, ob die Seite immer noch funktioniert) sorgt er für ein vollkommen falsches Bild von den Barrierefreiheitsproblemen. Jede Zuwiderhandlung gegen diesen Checkpunkt wird in der Regel mit dem größtmöglichen Punktabzug bestraft. Dabei sagt dieser SEO-Test kaum etwas über die Zugänglichkeit von JavaScript aus.

Nicht AJAX, sondern JavaScript ist „unreif” für Screenreader

Seien wir mal ehrlich nicht AJAX selbst oder das „Nicht-Vorhandensein von JavaScript” ist aus Zugänglichkeitsgesichtspunkten „unreif”. JavaScript und vor allem seine Unterstützung in assistiven Technologien sind höchst problematisch und sehr schwer zusammen zu bringen. AJAX ist lediglich eine gewisse Verschärfung des eigentlichen Problems, da zwischen User-Aktion und Content-/Style-Änderung eine größere Verzögerung liegt. Nehmen wir zum Beispiel die DOMTabs. Diese bestehen nicht nur die meisten standardisierten Checkpunkte, sondern gehen sogar etwas darüber hinaus. Dies ist angesichts des Know-Hows des Entwicklers auch kein Wunder, aber wirklich barrierefrei arbeiten diese im Screenreader nicht.
Das, in den meisten aktuellen Screenreadern, wohl am häufigsten auftretende „Fehlverhalten” dürfte sein, dass der Screenreader-User auf einen Tab-Link klickt und erwartet, entweder auf eine neue Seite zu kommen bzw. zum ausgewählten Inhalt zu springen. In den meisten Fällen wird jedoch erstmal nichts passieren bzw. der Screenreader wird stur die nächsten Tab-Links vorlesen (Obwohl der Inhalt des ausgewählten Tabs korrekt in der Sprachausgabe upgedatet wurde.). Noch problematischer wird es, wenn man zum einfachen Wechsel ein bisschen Animation hinzufügt. Hier kann es passieren, dass der neue Inhalte nicht upgedatet wird oder der alte Inahlt ebenfalls noch im Screenreader-Buffer schlummert. Man bedenke, dass es sich bei diesem Tab-Switch um ein relativ einfaches Gebilde handelt. User Interfaces, die heutzutage mit JavaScript realisiert werden, sind nicht selten wesentlich komplizierter und die Berücksichtigung aller möglichen unterschiedlichen Barrieren erfordert viel Arbeit (Lösungsansätze für die eine Barriere können nicht selten eine neue befördern). Mit diesen JavaScript-Barrieren werden Entwickler bei ihrer Arbeit wesentlich häufiger konfrontiert als mit den AJAX-Barrieren.

JavaScript ist nicht tot zu kriegen

JavaScripter, denen Zugänglichkeit am Herzen liegt, müssen sich trotz der Probleme nicht in einer Höhle verkriechen. Dies hat vor allem drei Gründe:

  1. Die Barrierefreiheit von reinen HTML & CSS Webseiten ist alles andere als „gottgegeben” und erfordert ebenfalls gewisses Know-How. Den Entwicklern, die sich etwas mit Zugänglichkeit beschäftigt haben, werden auch ein paar Beispiele einfallen, die zu dem Schluss führen könnten, dass Screenreader für bestimmtes, theoretisch vollkommen korrektes HTML noch nicht reif sind.
  2. JavaScript ist die Sprache des Web. Trotz der oben genannten Probleme gehört JavaScript korrekt eingesetzt zur Lösung von Barrieren im Netz und nicht umgekehrt. (beispielhaft: Denn Sie tun es - CSS und Javascript
  3. Die Zugänglichkeit von JavaScript wird derzeit deutlich vorangetrieben (dazu später mehr)

Ein Beispiel: Entgegen allen Behauptungen Flyout-Menüs wären böse, sollten wir uns darüber im klaren sein, dass in bestimmten Fällen ein Drop-Down-Menü oder ähnlich dynamisches Menü (Accessibility im Schatten der Usability und Neue Studie zur Usability unterschiedlicher Naviagtions-Methoden, die beste und gebrauchtauglichste Möglichkeit ist, eine Navigation zu erstellen. Jedoch gerade die falsche Ansicht, dass solche Menüs auch in veralteten Browsern ohne JavaScript funktionieren müssen, hat Entwickler dazu gebracht Techniken einzusetzen, die weder gebrauchstauglich sind noch ein Mindestmaß an Barrierefreiheit erfüllen. Diese CSS-Only-Lösungen können nicht richtig mit der Tastatur bedient werden, sie passen sich weder „Fehleingaben” des Users noch seiner Bildschirmgröße an und sie sind aufgrund fehlerhaft verschachtelter Links (gut versteckt in konditionellen Kommentaren) ein Showstopper für Screenreader. Für so etwas ist JavaScript schlichtweg die sauberste und barriereärmste Lösung.

WAI - ARIA - die Lösung für zugängliches JavaScript

In Sachen barrierefreies JavaScript tuen sich mit ARIA (Accessible Rich Internet Application) bereits heute und vor allem morgen nutzbare Möglichkeiten auf, seine „Widgets” erheblich barrierefreier zu gestalten. Hierbei bekommen typische “JavaScript-Widget-Elemente” (häufig Navigations- oder andere Eingabeelemente) zusätzliche semantische Attribute, welche ihre Rolle und Eigenschaften in maschinenlesbarer Form über den Browser dem Screenreader zugänglich machen können. Es existieren beispielsweise Rollen und zusätzliche Eigenschaften für Slider, Tabs, Grids, Menüs u.v.m.. Hierbei sind ebenfalls sog. Eigenschaften für live regions enthalten, welche die Zugänglichkeit von AJAX erhöhen, allerdings werden diese noch nicht ausreichend unterstützt. Die Unterstützung der zuvor genannten Widget-Rollen, die der W3C-Entwurf in der Praxis dagegen hat, ist trotz einiger Wandlungen des Entwurfs in jüngster Zeit sowie unterschiedlicher Implementierungen, atemberaubend. Mozilla hat bereits mit Firefox 1.5 eine inzwischen veraltete und mit Firefox 3 überarbeitete Implementierung begonnen (zu dem unterschiedlichen Support in den Browsern: http://learningtheworld.eu/2008/wai-aria-update/, http://blog.namics.com/2008/03/sxsw-2008.html ). Gleichzeitig besteht in folgenden Screenreadern/Vergrösserungssystemen grundlegender Support: Window Eyes 5.5+, Jaws 7.0+, ZoomText, Orca 2.20+. Die meisten deutschen Screenreader-Nutzer (angeblich 70%) nutzen Jaws. Häufig liegen sie ein bis zwei Versionsnummern zur aktuellen Version zurück. Derzeit ist die aktuellste Jaws-Version bei 9.0 angelangt. Gleichzeitig wird der Support in den Browsern erweitert: Opera 9.5 unterstützt ARIA ähnlich wie Firefox 3, Microsoft hat dem IE8 eine etwas abweichende Implementierung spendiert und Webkit (Safari) haben mit der Umsetzung begonnen. Dies ist für JavaScripter eine extrem erfreuliche Nachricht. Die neuesten Browser, von denen hier die Rede ist, können kostenlos genutzt werden bzw. werden zumindest kostenlos mitgeliefert. Bei den verhältnismäßig teuren Screenreader taugen bereits die älteren Modelle. Spätestens mit Erscheinen der nächsten Generation dieser Browser steht die Implementierung (formaler Standard hin oder her) fest und kann genutzt werden. Ich kann nur jedem Entwickler raten sich mit ARIA näher zu beschäftigen und neben den anderen Techniken für barrierefreies JavaScript bald bald einzusetzen :-).

weiterführendeLinks:

Written May 1, 2008 by
alexander farkas

Controlling Embedded Video with Javascript Part I: Quicktime

Tags: english, javascript, tutorial, video

This is the first part in a little series of articles about scripting the controls of video with Javascript. I worked on a project recently where using flash for video was not an option, but instead all other major formats/plugins had to be supported (quicktime, windows media player, real player, vlc player). While researching for this project, I noticed that there was a certain lack of easily accesible information about this topic, especially concerning cross-plugin and cross-browser functionality. That’s why I decided to write these articles, maybe they can help someone who’s trying to do something similar.

Javascript can be used to control nearly every aspect of the mentioned plugins’ behaviour. Usually the reason for scripting video plugins will be to create a custom control interface, while the standard controls are hidden. One should think very carefully about this from a usability perspective, because most users will be more familiar with the standard controls. Also there will always be certain (older) browsers-plugin-combinations that will not behave as expected, possibly preventing certain users from accessing the content. Therefore it’s always a good idea to have some sort of fallback plan (i.e. show standard controls if Javascript is deactivated… think graceful degradation).

This first article will cover the Quicktime plugin, followed shortly by WMP, Real, and VLC. Below is a list of desired features that should ideally work across all plugins and browsers. The approach I want to take is to create a common interface of functions covering the desired features and implement the interface for each plugin (view the interface functions - Note: This is an interface by convention only, it is not enforced through code). That way the underlying plugin can be easily swapped without changing much code. See the Quicktime scripting demo page for a working example.

List of desired features:

  1. cross-browser embedding with standard interface deactivated
  2. basic playback controls: play, pause, stop
  3. display current time, duration, and status information
  4. jump to specified time within movie
  5. volume control
  6. change/load movie
  7. buffering
  8. fullscreen display

Embedding Quicktime

To begin with we must embed the quicktime plugin properly so that it works in all major browsers (Quicktime version 7.4 as of writing). I won’t go into much detail here, since this has been covered sufficiently elsewhere (i.e. here or here). This is the method that I’ve used:

In order to avoid the <embed> tag, which is non-standard (i.e. won’t validate), there are ways to use nested <object> tags instead (see A List Apart Articles: Bye Bye Embed), but I could not get the scripting to work properly in that case (any evidence of the contrary would be greatly appreciated…).

Note: This method of embedding the Quicktime plugin does not address the ActiveX activation issue in Internet Explorer – therefore you will currently need to click on the video once in order to activate it. It seems though that Microsoft is finally going to change the behaviour of embedded controls so that this won’t be an issue any longer after April 2008 (see: IEBlog: IE Automatic Component Activation (Changes to IE ActiveX Update)).

Basic Scripting

So let’s get started with the scripting, this is going to be pretty basic stuff – I might cover more advanced topics in separate articles. Note that the presented code is not fully optimized and I’ve ommited safety checks in order to preserve simplicity.

The first thing we need to do is to get a reference to the video object. The easiest way to do this, assuming the embed technique described above is being used, is to give the object tag an ID and the embed tag a NAME with identical values. That way the video can simply be accessed by its name:

In this case Internet Explorer will use the ID attribute and all other browsers the NAME attribute. Once we have a reference to the actual video, we can start manipulating it – the basic playback controls would look something like this:

Before actually manipulating the movie it is usually a good idea to check the status of the movie to make sure that it is ready for action. Otherwise if you try to access the video too early before it has loaded, Javascript errors can occur. The following method will return a string with the status of the movie:

It’s safe to access the movie, if the status is either “Playable” or “Complete”. The returned string will have one of these values:
“Waiting” - waiting for the movie data stream to begin
“Loading” - data stream has begun, not able to play/display the movie yet
“Playable” - movie is playable, although not all data has been downloaded
“Complete” - all data has been downloaded
“Error: ” - the movie failed with the specified error number

The next two functions show how to access the current playback time and the duration of the movie. The thing to note here is that the time returned by “GetTime()” and “GetDuration()” are in the movie’s time scale units. In order to convert this to seconds one needs to divide it by the time scale (units per second). This will then give you the time in seconds, usually you will then want to convert this into a nicely readable string format (something like: hh:mm:ss).

In order to set the movie to a specific position, one basically has to do the opposite – multiply the movie’s timeScale with the desired position in seconds. Note that the time cannot be set beyond what has currently been buffered - if you attempt to do this it will set the time to the last buffered frame.

The volume within Quicktime has a range of 0 – 255. I will convert this to be within a range of 0 – 100, because I think it is easier to work with in that range.

To swap the movie that’s being played, just set a new URL. For some reason Quicktime will show the default controller again if you change the URL – so in order to keep it hidden, you will need to manually hide it again.

When a new movie is initially loaded it will be buffered into memory. It can be useful to give some feedback about the buffering status of a movie. The following function will return the percentage of the movie that has been buffered (0 – 100%):

As for full screen mode, it is currently (to my knowledge) not possible to invoke Quicktime’s internal full screen mode for embedded movies - even though since version 7.2 it has become a feature of the free standalone player.

That about wraps it up, check out the demo page and feel free to post any questions or feedback.

Links

Written February 1, 2008 by
boris

AJAX managen

Tags: ajax, deutsch, javascript, jquery

Nicht selten wünscht man sich, von seinen AJAX-Responses , dass sie in der gleichen Reihenfolge ankommen, in der die Anfragen abgesendet wurden bzw. zumindest in der selben Reihenfolge in der sie beim Server verarbeitet wurden (z.B.: Warenkorb). In anderen Fällen ist dagegen klar, dass eine Antwort auf eine Anfrage keinen Sinn mehr macht, da der User sich - während der Verarbeitung des XHR-Requests - entschieden hat einen anderen Beitrag auszuwählen.

Aufgrund der Asynchronität von AJAX, schwankender Latency sowie unterschiedlich langer Verarbeitung durch serverseitige Scripte kann dies jedoch nicht vorausgesetzt werden. Um derartige sowie ähnliche AJAX-Anfragen zu behandeln, habe ich ein kleines jQuery-Plugin zum managen von AJAX geschrieben.

Mit diesem Script lassen sich AJAX-Anfragen queuen, abbrechen blocken sowie AJAX-Antworten synchronisieren. Zum $.ajaxManager.

Written November 11, 2007 by
alexander farkas