protofunc()

Die wahren Probleme von HTML5 Polyfills: HTML5 shim und das Ende des 3 Schichten-Modells (Teil 1)

Tags: HTML 5, deutsch

Das HTML5shim ist erforderliche Grundlage, um coole HTML5 Elemente wie canvas, video, audio oder datalist, aber auch die “langweiligen” Sectioning Elemente wie nav, article, section etc. sowie andere langweilige Elemente wie mark, figure, figcaption etc. in älteren IE-Versionen einsetzen zu können.

Da das HTML5shim erfordert, dass der Nutzer JS eingeschaltet hat, wird dies gerade im Zusammenhang mit den Sectioning Elementen als problematisch gesehen, da diese im wesentlichen zur Strukturierung und letztlich zum Styling genutzt werden und damit im Widerspruch zum Schichtenmodell der Frontendentwicklung stehen.

Skepsis ist erlaubt

Ich stehe dem heutigen Einsatz dieser Elemente bei 90% aller Webseiten, grundsätzlich negativ gegenüber. Der Grund ist einfach, diese Elemente haben einerseits keine funktionalen Features und ihre Semantik ist schlechter unterstützt als WAI ARIA (Landmarks), andererseits machen sie erheblich mehr Probleme.

Der diskutierte Einsatz von Sectioning Elementen

Eine diskutierte Variante ist zwar HTML5 Elemente einzusetzen, hier jedoch zusätzliche div-Wrapper vorzusehen und ausschließlich diese in CSS und JS anzusprechen. In diesem Fall ist es dann in älteren IEs egal, ob sie nutzbar sind oder nicht.

Letztendlich kann ich dem nicht viel abgewinnen, da in diesem Fall nur zusätzlicher Ballast erzeugt und letztlich in keinem Browser wirklich HTML5 angesprochen wird. Zudem unterscheidet sich die HTML Struktur in den unterschiedlichen Browsern, so dass hierdurch andere Bugs entstehen können und beispielsweise oft keine Kind- sowie Nachfolgeselektoren eingesetzt werden können.

Die konträre Extrem-Position setzt auf das HTML5shim und erklärt das Trennungsprinzip mit HTML5 und seinen reichhaltigen DOM-/JS-APIs sowieso für grundsätzlich erledigt. Webseiten müssen hiernach nicht mehr ohne JS funktionieren und der Aufbau einer Webseite soll nach anderen Prinzipien erfolgen (wobei die neuen Prinzipien, dann nicht mehr erklärt werden bzw. offen gesagt wird, dass man noch auf der Suche nach solchen ist).

Ich halte letztere Position zwar für richtig, aber die Argumentation für extrem gefährlich. Extrem gefährlich deshalb, weil “HTML5 Entwickler” sowieso schon zu dumm sind, um zu erkennen, dass ein button-Element, die richtige Struktur für einen Abspielbutton des eigenen HTML5 Videoplayers wäre. Soviel Dummheit kann man nicht mit den Worten, “Die Spielregeln haben sich verändert, irgendwann erklär ich sie dir”, auf die Welt los lassen.

HTML5 ist nichts anderes als die Weiterentwicklung von HTML und HTML4 bot bereits eine sehr reichhaltige DOM-API. Dass man nun mit noch mehr APIs, mehr und featurereichere Webapplikationen umsetzen kann, welche selbstverständlich JS voraussetzen müssen, ist kein Argument schnöde Webseiten mit ein paar Tabs, Accordions und Galerien so umzusetzen, dass sie ohne JS nicht funktionieren. Es ist letztendlich auch nicht so, dass Webentwickler vor der grossen Herausforderung stehen, sich zu fragen wie man JS abhängige Webapplikationen sauber strukturiert. Die steigende Anzahl und das ebenso steigende Interesse an Templating-Scripten sowie JS-Frameworks, die das MVC-Pattern sowie ähnliche Konzepte mit aufnehmen, dürfte dies deutlich machen.

technik- und nutzerzentrierte Vereinbarung des Schichtenmodells mit HTML5shim

Als erstes gilt es festzustellen, dass das Trennungsprinzip ein Entwicklungsmodell ist, welches beim Entwickeln ungemein hilft. Man schaue sich hier das in die Jahre gekommene DOM-Tab Script an, quasi das Paradebeispiel des Trennungsprinzips und unobtrusiven JS. Ist es nicht schön zu sehen, wie die semantische Information des HTML (Verbindung eines Links mit einem Element durch href-Verweis auf die id dieses Elements) als Konfigurationsgrundlage für das JS aufgegriffen wird. Es gibt viele Beispiele für eine gute HTML-Struktur die nicht nur ohne JS alleine funktioniert, sondern beim stückweisen Aufbau der Webseite mit JS hilft. Soll ich auf so etwas geniales verzichten, obwohl mir gerade HTML5 mit den data-* Attributen und vielem mehr, weitere “Semantik” als [custom-]Konfigurationsmöglichkeiten zwischen Backend und Frontend in die Hände drückt, nur weil irgendwelche alten Browsergenerationen einer einzelnen Firma, bestimmtes HTML bei ausgeschaltetem JS nicht richtig parsen? Nein mit Sicherheit nicht! Warum sollten Frontendentwickler gerade heute anfangen, ihre Konzepte am IE8 und nicht am IE9 auszurichten. Wer unobtrusives JS in Kombination mit HTML5 Sectioning Elementen schreibt, bekommt in jedem Fall ein sauberes Entwicklungsprinzip für eine Webseite und im Fall moderner Browser wird diese wie gewohnt auch weiterhin ohne JS funktionieren. Soviel zur Technik.

Letztendlich kommt es nicht allein auf die Technik, sondern auf den Nutzer an. Hat eine Webseite viele Besucher mit IE8 (und ausgeschaltetem JS), sollte man ohne Zweifel auf HTML5 Sectioning Elemente schlichtweg verzichten. Aber auch in Fällen in denen kaum Nutzer des IE8 mit ausgeschaltetem JS zu erwarten sind, muss man den Nutzer nicht im Regen stehen lassen. Durch geschickte Vermischung von Konditionellen Kommentaren und dem so verhassten noscript Element, lässt sich auf einfache Weise erreichen, dass auch der IE8 bei ausgeschaltetem JS ein (IE6-)universelles Stylesheet bekommt und ansonsten das HTML5 Stylesheet. Und man kann sich dabei auch ganz toll HTML5 fühlen. Denn erst ab HTML5 ist es erlaubt das noscript Element innerhalb des head-Elements zu platzieren.

Die wahren Probleme von neuen Elementen im IE8

Das erste Problem dürfte mit Erscheinen von jQuery 1.7 bekannter werden und gleichzeitig zumindest für die jQuery-Welt auch gelöst sein. Das HTML5shim hilft nämlich bei dynamisch erstellten Elementen nicht viel. Will man beispielsweise seinen schönen HTML5 content mit AJAX in eine Lightbox laden, hat man das selbe Problem wie mit ausgeschaltetem JS. Das dynamisch erstellte HTML[5] weist dann i.d.R. die selben Fehler auf, wie ohne HTML5shim.

Das andere Problem ist die fehlende Möglichkeit HTML5 Elemente zu drucken. Dies erscheint auf den ersten Blick nicht so schlimm, aber man kann letztlich bei fast keinem Projekt im voraus ausschließen, dass es zumindest irgendwann eine Druckvariante geben wird. Die einzige Möglichkeit besteht derzeit in der Verwendung von IEPP (wird vom aktuellen HTML5shim inzwischen genutzt), welches jedoch im Vergleich zu dem reinen klassischen HTML5shim kein einfacher Workaround, sondern ein gigantischer Hack ist. In der Natur von IEPP liegt es, dass der IE kurz vor dem Druck entweder sehr kurz oder recht lang bis hin zum Absturz einfriert. Ebenso in der Natur des Hacks liegt es, dass das Resultat mal dem definierten CSS mal eher weniger diesem entspricht, da IEPP an HTML sowie CSS für die Zeit des Drucks grosse Veränderungen durchführt. Ob es zu solchen Performanceproblemen oder solchen Fehlern kommt, hängt entscheidend vom Umfang des CSS und dem Schreibstil des jeweiligen Entwicklers ab. IEPP ist noch ein recht junges Projekt, welches sich noch deutlich weiterentwickeln kann und muss. Leider sind Verbesserungen häufig nur zu Lasten anderer Kenngrößen des Scripts(Performance vs. Genauigkeit vs. Dateigröße) zu erreichen.

Es ist merkwürdig, dass über dieses Problem kaum gesprochen wird bzw. kaum Entwickler, welche HTML5 gerade anfangen einzusetzen, diese wirklich kennen. Das kennen dieser Probleme sollte jedoch für jeden Entwickler, der über den Einsatz von HTML5 “Sectioning Elementen” nachdenkt, Pflicht sein. Zusätzlich kann zur Verbesserung des Problems einiges getan werden: Entwickler könnten darüber diskutieren, wie sie ihr CSS einbinden/schreiben, damit IEPP dieses schneller parsen kann. JavaScript-Entwickler könnten sich an die Verbesserung des Scripts machen etc..

opap casino

Written September 25, 2011 by
protofunc

JS-Ladeperformance: Müßen wir die yslow-Regeln überdenken?

Tags: Performance, deutsch, javascript

Es gibt wohl auf der Welt keinen Frontend Entwickler, der nicht die wichtigsten Performance Regeln des Exceptional Performance teams von yahoo kennt.

Der wichtigste Teil dieser Regeln wurde 2006 erstellt und nicht nur Entwickler haben dazugelernt. Auch die Browser-Hersteller haben ihre Hausaufgaben gemacht und Performanceoptimierungen für die größten Ladebremsen durchgeführt. Einige dieser Browser-Optimierungen müßen zu einer Änderung der Regeln für Entwickler führen. Die bline Befolgung der Performance-Regeln führt zu einer ungerechtfertigten Bevorzugung des IE6 und des IE7 zu Lasten moderner Browser.

Put your JavaScript at the bottom

Die blinde Befolgung dieser Regel war mir schon immer ein Dorn im Auge. Wer sich die Ratio dieser Regel durchliest, könnte denken, daß Scripte, welche am Ende der Seite eingebunden sind, nicht mehr blockend laden. In Wirklichkeit blocken konventionell eingebundene Scripte immer, gleichgültig an welcher Stelle sie eingebunden wurden. Lediglich weil danach keine weiteren Resourcen kommen sollten, fällt dies nicht so gravierend auf. An der Gesamtladezeit einer Seite ändert sich hierdurch jedoch nichts. Lediglich die subjektive Ladezeit wird herabgesetzt, da der white page effekt dramatisch herabgesetzt wird, was wiederum immer zu Problemen von Flash of Unbehaviored Content und zumindest teilweise zu Flash of Unstyled Content führt.

Wer das blockende Laden lösen möchte, kann sein JS getrost im head dynamisch mit JS nachladen. Hierdurch wird das blockende Verhalten des JS ausgeschaltet, der Browser kann seine Downloads parallelisieren und auch die white page Zeit wird herabgesetzt. Da die JS-Dateien aber wesentlich früher geladen werden, treten die verschiedenen Arten von FOUCs seltener bis gar nicht auf.

Minimize HTTP Requests

Minimize HTTP Requests gehört wohl zu der goldenen Regel für die Frontend-Performance. Im Fall von JS wird empfohlen, alle Script-Dateien in eine einzelne Datei zu kombinieren. Diese Optimierung durch den Entwickler nimmt dem Browser jede Möglichkeit selbst Performance Optimierungen durchzuführen und ist in allen moderneren Browsern (inklusive IE8!) eine wahre Bremse.

Um diese Behauptung zu stützen, hier ein paar einfache Performancetests:

Ein erster Test mit cuzillion

ist ein nettes Tool um schnell mal ein paar Performance Tests durchzführen. Da die Mindestladezeit bei einer Sekunde liegt und keine CSS-Bilder simuliert werden können, sind die Ergebnisse jedoch immer mit Vorsicht zu genießen. Unser Cuzillion-Testaufbau, umfaßt immer 3 Stylesheets, 3 Bilder sowie ein iframe, welche je 1 Sekunde Ladezeit benötigen. Daneben wird JS von 2 Domains in unterschiedlicher Anzahl eingebunden, welche insgesamt 9 Sekunden betragen:

Wie schnell Laden nun die verschiedenen Browser die Seite, wenn wir diese JS-Dateien einerseits auf 2 Dateien und andererseits auf 8 Dateien zusammen fassen:

:
IE9: 10 | FF4: 10 | IE6: 15 | Chrome 11: 10

:
IE9: 5 | FF4: 4.6 | IE6: 16 | Chrome 11: 4.8

Wie wir sehen, wird die Seite durch Befolgung der Performanceregel im IE6 (und wohl auch im IE7) ein bißchen schneller, während dem die Seite in allen modernen Browsern deutlich langsamer wird.

Die Gründe für diese krassen Unterschiede sind relativ klar, moderne Browser haben mindestens 2 Performanceoptimierungen durchgeführt, welche durch das Zusammenfaßen von JS-Dateien nicht mehr ihre positiven Wirkungen entfalten können:

  • konventionell eingebundenes JS blockt nicht mehr
  • Die maximale Anzahl gleichzeitiger Requests zur gleichen domain wurden von 2 auf 4-8 angehoben

Ein weiterer nicht uninteressanter Test, ist das Laden von 8 JS Dateien, von denen 7 dynamisch eingebunden wurden:

:
IE9: 5 | FF4: 4.8 | IE6: 14 | Chrome: 5.5

Es zeigt sich, daß moderne Browser vom dynamischen Einbinden gar keinen Nutzen mehr ziehen können. Wirklich überraschend ist, daß der IE6 mit seinen beiden mageren Downloadslots die hohe Anzahl der Dateien durch Parallelisierung wettmachen kann.

Praxisnaher Performancetest

Als Grundlage für einen praxisnäheren Test diente ein Prototyp mit einer HTML Seite mit 60kb, unterschiedlicher Anazhl von JS-Dateien mit insgesamt 300kb, 4 x Stylesheet-Dateien (125kb), 25 x CSS-Bildern (140kb) und 5 x Inhaltsbildern (160kb). Für die weiteren Tests habe ich auf die alten Browser verzichtet und ausschließlich IE9 sowie FF4 verwendet, da beide mehr oder weniger gleich schnell sind, faße ich beide zusammen.

Auch habe ich mit verschiedenen Verbindungen getestet, da die Ergebnisse jedoch nicht relevant sind und nichts an der generellen Beobachtung ändern, bleiben die langsamen Verbindungsdaten unerwähnt.

Alle JS Dateien wurden grundsätzlich konventionell eingebunden.

Ladezeiten bei unterschiedlicher Anzahl von Dateien (Anzahl Dateien / DOM-Ready-Zeit / onload-Zeit):

  • 1 Dateien / 1100ms / 1600ms
  • 4 Dateien / 850ms / 1350ms
  • 6 Dateien / 800ms / 1300ms
  • 8 Dateien / 800ms / 1300ms
  • 12 Dateien / 800ms / 1300ms

Interessant an dem Ergebnis ist, daß sowohl IE9 als auch FF4 mit 6 Dateien ihr Performanceoptimum erreicht haben, aber andererseits durch Verdoppelung auf 12 Dateien keine Verlangsamung eintritt.

Wie aussagekräftig sind die Tests?

Um hier möglicher Kritik aus dem Weg zu gehen. Diese Ergebnisse beruhen auf ein paar kleinen Tests, welche mit Sicherheit nicht den Grundlagen der Statistik und des wissenschaftlichen Arbeitens folgen. Aber sie zeigen einen deutlichen Trend, der viele zum Umdenken/Neudenken befördern sollte.

Ähnliche Tests mit ähnlichen Ergebnissen wurden im Bereich der performanten Stylesheet-Einbindung bereits vor ein paar Jahren von Dirk Jesse gemacht, welcher eine Aufsplittung in 4 CSS-Dateien empfiehlt. (Ich habe aber wirklich echt nicht abgeschrieben).

Was bedeutet das für die Praxis?

Die Bedeutung von “Minimize http-requests” ist mit Sicherheit in der heutigen Browserlandschaft gesunken, hat aber immer noch Berechtigung. Der Entwickler muß sich hier darüber im Klaren sein, daß diese Regel der Parallelisierung von Downloads entgegensteht und einen angemessenen Ausgleich schaffen.

Dieser Ausgleich muß sich an den jeweiligen Größen der eingebundenen Dateien orientieren.

Das Verringern von http-requests einerseits dient vor allem der Reduzierung der Dauer für das Senden des Request-Headers sowie der Wartezeit bis der eigentliche Download beginnt, währenddem das “Auftrennen” von Dateien andererseits zu einer Reduzierung der eigentlichen Downloadzeiten durch Parallelisierung führt. Kurz: Ist eine Datei kleiner als 5kb sollte sie mit anderen zusammengefaßt werden, ist eine Datei dagegen größer 50kb sollte sie gesplittet werden.

Written March 1, 2011 by
protofunc

deutsche Validierungsnachrichten in HTML5 fähigen Browsern

Tags: HTML 5, deutsch

HTML5 Formularfeatures dürften Anfang nächsten Jahres auf größeres Interesse stoßen. Von einem Feature nämlich der validationMessage-Eigenschaft, läßt sich tatsächlich (rein zufällig natürlich) auf Umfang und Qualität der derzeitigen HTML5 From Implementierung in den unterschiedlichen Browsern schließen:

  • Opera: Qualität/Umfang: gut, in Details verbesserbar
  • Safari: Umfang gut, aber letztendlich nicht zu gebrauchen
  • FF: Umfang gering, aber dafür mit hoher Qualität


Tabelle in eigenem Fenster

Written November 17, 2010 by
protofunc

HTML5 placeholder Attribut und Zugänglichkeit

Tags: HTML 5, accessibility, deutsch, javascript

Angestachelt durch Robert´s Post/Script zu “Adding HTML5 placeholder attribute support through progressive enhancement” und vor allen Dingen einigen Kommentaren habe ich mein eigenes HTML5 Placeholder-Script nochmals überarbeitet.

Eine Sache, die ich verändert habe, betrifft die Frage, ob das placeholder-Attribut ein Ersatz des label-Elements darstellen kann oder nicht. Laut HTML5 Spezifikation stellt das placeholder-Attribut keinen Ersatz für das label-Element dar, sondern dient nur als zusätzlicher Hint. Meiner Meinung nach ist dies ein weiterer kleiner Fail der Zugänglichkeit in der HTML5 Spezifikation. HTML5 wollte sich doch eigentlich an der Praxis orientieren und das was man mit dem placeholder-Attribut optisch erreicht, wird heutzuage millionenfach als platzsparender Ersatz eines label-Elements praktiziert (Stichwort: overLabel).

Das nun verbesserte Script kann letztendlich als stark vereinfachter Implementierungsvorschlag für Browser dienen. Wird ein placeholder-Attribut entdeckt, wird eine a11y-Namen Berechnung durchgeführt, wird diese nicht fündig, wird der Text des placeholder-Attributs zum a11y-Namen gemacht. Dies mag nicht ganz der Spezifikation entsprechen, ist jedoch deutlich näher an der Realität. Wird dagegen sowohl eine a11y-namengebende Auszeichnung als auch das placeholder-Attribut verwendet, bleibt es selbstverständlich bei dem ursprünglichen Namen und das placeholder-Attribut wird lediglich als zusätzlicher Hint angesehen.

Written June 18, 2010 by
protofunc

jme – das erste HTML6 audio/video development kit

Tags: HTML 5, accessibility, deutsch, javascript, jquery, video

Es gibt zwei Dinge mit denen mein jMediaelement Projekt allein nicht beschrieben wäre: HTML5 audio / video einerseits und Player-Script andererseits. Die von jme genutzen HTML5 Media-Elemente sind nur eine Abspieltechnologie von insgesamt dreien. Würde man den HTML5 Teil weglassen und damit lediglich Flash und VLC als Abspieltechnik dienen, hätte jme weiterhin imense Vorteile für Frontendentwickler. Das liegt unter anderem daran, daß jme eben kein reines Player-Script ist.

jme besteht aus 2 Hauptkomponeten:

  1. einem leichtgewichtigem, kugelsicheren reinen embed-Teil
  2. der “Vollversion”, welche neben dem embed-Teil, auch die DOM-API und die Markup-API (Controls) enthält

Media-Elemente einbinden schnell und einfach wie ein Bild

In vielen Fällen, in denen ein Entwickler einen Videoplayer einbinden muß, ist kein spezielles Design und kein besonderes Feature verlangt. Es geht schlicht weg darum einen Player zum Abspieln von Videos/Audios zu haben.

Der reine embed-Teil von jme ist daher in einer separaten Version als leichtgewichtiger Helper eingebunden.

Der notwendige HTML-Code ist identisch mit dem HTML5 Code:

<video src="myvideo.webm" poster="mein-poster.jpg" controls="controls">
	<!-- Fallback Content -->
</video>

Damit dies auch ohne Bugs und mit älteren Browsern funktioniert, kopiert man einfach die mm.embed.min.js aus dem packages Ordner vom jMediaelement-Download zusammen mit der player.swf und – falls man youtube-videos einbinden will – der yt.swf in einen gemeinsamen Ordner.

Danach kann man bei DOM-Ready seine HTML5 Media-Elemente mit jmeEmbed registieren:

$(document).ready(function(){
	$('audio, video').jmeEmbed({showFallback: true});
});

Der embed Teil kümmert sich um die Erkennung von Browser- und Plugin-Features, deren Einbindung/Initialisierung, der Normalisierung der HTML5 Mediaelement Attribute sowie der hiermit zusammenhängenden Bugs. Ist die Vollversion von jme eingebunden, wird hierdurch auch die DOM-API initialisiert. Die Vorteile für den Entwickler:

  • er hat einen einfachen, intuitiven und semantischen Einbindecode
  • Sofern ein Video mit richtigem Container/Codec eingebunden wird, erhöht sich die Zugänglichkeit für bestimmte Endgeräte
  • der Entwickler ist nicht gezwungen mehrere Video-Formate anzubieten. Letztendlich können auch nicht HTML5-Formate wie zum Beispiel flv-Dateien oder Youtube-Seiten als Datengrundlage genommen werden.
  • Die jeweiligen Browser-, Plugin-Player rendern die Kontrolelemente automatisch, so daß keine weiteren HTML-/CSS-Anpassungen anfallen.

Kurz: Gegenüber der Art wie man derzeit Videos einbindet, kommt man hier mit jme schneller, semantischer und manchmal auch zugänglicher ans Ziel.

HTML ist die Schnittstelle zwischen JavaScript und CSS oder “divide et impera” – Teile und herrsche

Der zweite Teil ist keine reine JavaScript API, sondern gliedert sich in DOM-API und Markup-API auf. Dieser Aufbau ist der Erkenntnis geschuldet, daß JavaScript nur wenig HTML diktieren sollte und HTML unter anderem eine Schnittstelle zwischen einerseits JavaScript und CSS und andererseits zwischen Frontend und Backend darstellt.

Die Erstellung von funktionsfähigen Kontrollelementen ist Aufgabe der Markup-API. Features welche nicht über die Markup-API realisiert sind, können mit Hilfe der DOM-API erledigt werden.

HTML6 Control-Elemente = jme Markup-API/Kontrollklassen

Anstatt, daß durch einen JavaScript-Aufruf ein Haufen unsemantischer HTML-Code um den Player gepackt wird, besteht jme aus kleinen Bausteinen, die einfach zusammengesetzt werden können und so einen individuellen Player ermöglichen. Hierbei besteht als oberstes Prinzip, daß sich die verwendeten Bausteine flexibel in die HTML-/DOM-Welt integrieren laßen müßen.

Stell Dir vor HTML5 ist Schnee von gestern und das W3C würde mit HTML6 einige neue Elemente spezifizieren, welche als Kontrollelemente für das audio- und video-element dienen. Ein Abspiel-/-Pause Button könnte wie folgt aussehen:

<button type="play-pause">play / pause</button>

Das schöne daran, der Browser kümmert sich um Funktionalität und Zugänglichkeit und der Entwickler besitzt die Flexibilität die Kontrollelemente erstens frei zu platzieren und zweitens mit weiterem HTML anzureichen, um Sie dann beispielsweise per CSS besser zu stylen oder um ein gewünschtes Userinterface-Verhalten zu erreichen.

Damit der Browser weiß, mit welchem Mediaelement das jeweilige Kontrollelement verbunden ist, werden die Kontrollelemente gemeinsam mit dem Mediaelement in einem ebenfalls neuen Element mit dem Namen player gewrappt. Das Ergebnis sieht dann wie folgt aus:

<player>
	<video src="myvideo.webm"></video>
	<button type="play-pause">play / pause</button>
</player>

Die jme Kontrollelement-Bausteine funktionieren hierbei auf ähnliche Weise. Die neuen Kontrollelemente werden durch beliebige valide Elemente mit bestimmten HTML-Klassen symbolisiert und lediglich das Wrapper-Element “player” wird durch einen JavaScript Aufruf ersetzt:

<div class="player-wrapper">
	<video src="myvideo.webm" controls="controls"></video>
	<button class="play-pause">play / pause</button>
</div>

Das jQuery-Plugin ‘jmeControl’, welches auf dem wrapper-Element aufgerufen wird, registriert hierbei sozusagen die einzelnen Kontrolelemente und ruft bei Bedarf, die embed-Methode auf. Genau wie bei der jmeEmbed-Methode muß grundsätzlich keine weitere Konfiguration vorgenommen werden.

$('div.player-wrapper').jmeControl();

Werden die nativen Kontrollelemente nicht angezeigt, wird der Flashplayer – sofern nicht ausdrücklich anders konfiguriert – automatisch in den wmode=transparent Zustand versetzt, um auch schöne Overlay-Kontrollelemente oder Overlay-Logos über dem Video erstellen zu können.

Man kann selbstverständlich auch mehrere Kontrollelemente unterbringen:

<div class="player-wrapper">
	<video src="myvideo.webm" controls="controls"></video>
	<button class="play-pause">play / pause</button>
	<span class="current-time">--:--</span>
</div>

jme kümmert sich hierbei nicht nur um die Funktionalität und rendert die jeweils aktuelle Zeit in das span-Element und erstellt ein Play-Pause-Toggle Verhalten aus dem button-element, sondern auch um die Zugänglichkeit. Insbesondere dann, wenn zum einfacheren Stylen statt dem button-Element ein span-Element genommen wird, fügt jme die gewollte Semantik per WAI-ARIA ein. Folgendes geschriebenes HTML ….

<div class="player-wrapper">
	<video src="myvideo.webm" controls="controls"></video>
	<span class="play-pause">play / pause</span>
	<span class="current-time">--:--</span>
</div>

…wird durch jme in folgendes HTML umgewandelt:

<div class="player-wrapper">
	<video src="myvideo.webm"></video>
	<span role="button" tabindex="0" class="play-pause">play / pause</span>
	<span role="timer" class="current-time">--:--</span>
</div>

Neben allgemein üblichen Video/Audio Kontroll-Elementen bietet jme über Plugins auch Verhaltensklassen für eine Playliste und Captions an.

Die Tatsache, daß wir eine Markup-API verwenden, die nicht von JavaScript diktiert wird, heißt aber nicht, daß wir nun alle möglichen Kontroll-/Verhaltensklassen auswendig lernen und wieder und wieder ins HTML schreiben müßen. jme bietet im utils-Ordner das jmeEmbedControls-Plugin an, welches nichts anderes macht, als einen Haufen Kontrollelemente in das Element, auf dem es aufgerufen wurde, einzufügen, um dann hierauf die jmeControl-Methode auszuführen. Diese Methode ist sozusagen das Schnellstarter-Kit für jme. Er ist bewußt ausgelagert, um jedem Webentwickler eine projektspezifische Anpassung zu ermöglichen.

einheitliche DOM-API statt vereinheitlichte JavaScript-API

Die vereinheitlichte JavaScript-API ist bei jme bewußt eine DOM-API, da nur so sich Mediaelemente, sei es nun HTML5, Flash oder VLC, in den normalen Workflow eines Frontendentwicklers integrieren können.

Ein Beispiel: JW-Player for HTML5 ist das erste HTML5 Player-Script, welches eine dokumentierte API vorweisen kann. Und obwohl jQuery, also ein klassischer DOM-Wrapper, als JavaScript Grundlage gewählt wurde, wurde keine DOM-API implementiert. Um einen Player zu scripten, sieht der Code wie folgt aus:

$.jwplayer().play();

Laut Dokumentation wird nun immer auf dem ersten eingebundenen Mediaelement die Methode play aufgerufen, möchte man nun jedoch auf einem anderen Mediaelement Methoden ausführen, muß man bereits zum Zeitpunkt der Initialisierung die Referenz auf dieses Objekt speichern und sicherstellen, daß alle Module die hiermit interagieren müßen an diese Referenz kommen:

//holen der Referenz auf ein bestimmtes Video bei der Initialisierung
var player = $('#stage video').jwplayer({
	flashplayer:'/static/files/player.swf',
	skin:'/static/files/five.xml'
});

//initialisieren aller anderen videos
$('video').not('#stage video').jwplayer({
	flashplayer:'/static/files/player.swf',
	skin:'/static/files/five.xml'
});

//arbeiten mit der gespeicherten Referenz
//irgendwo ganz anders im script
player.play();

Mit einer DOM-API sieht die Sache dagegen anders aus, denn die API ist Teil des DOMs und kann wie jede DOM-API mit der jQuery-Selektorengine selektiert werden.:

$('#stage video').play();

Ebenso wie man bei anderen DOM-Elementen auf Events horcht, Methoden chained etc., funktioniert dies auch bei jme. Das video bzw. das audio Element bleiben hierbei immer das zentrale API Element, gleichgültig ob tatsächlich HTML5 oder eben ein Plugin zur Anzeige der Mediainahlte genutzt wird.

$('video').bind('ended', function(){
	$(this).attr('autoplay', true).loadSrc('my-video2.webm');
});

Kurz: nur so kann Scripten Spaß machen.

Fazit

Merkwürdig aber wahr: jme stellt in vielen Punkten einige Weltrekorde auf:

  1. jme ist das erste audio/video Script mit Fallback, welches eine dokumentierte DOM-API besitzt (das werden garantiert einige nachmachen, da es sooo nahe liegt)
  2. jme ist das erste audio/video Script, welches eine Markup-API für Kontroll- und Zustandselemente verwendet
  3. jme ist – und das ist wirklich traurig – das erste Script seiner Art, welches semantischen Code erlaubt/nutzt (notfalls mit WAI-ARIA forciert)
https://cheapessay.net/

Oder anders gesagt: jme versucht nicht aus HTML5 Flash zu machen, sondern aus Flash HTML5.

Written June 6, 2010 by
protofunc

HTML 5 Mediaelemente für alle – Warum und Wie video-/audio- Elemente schon heute genutzt werden sollten

Tags: HTML 5, deutsch, javascript, jquery, video

HTML 5 Mediaelemente für alle – Warum und Wie video-/audio- Elemente schon heute genutzt werden sollten

Ich gehöre mit Sicherheit nicht zu denjenigen, welche die Nutzung von HTML5 in seiner reinen Form bereits heute propagieren. Die Probleme der Rückwärtskompatibilität mit HTML5-Elementen sind zu groß und können eben nicht mit dem document.createElement – Trick behoben werden. Ausnahmen bestätigen jedoch die Regel und eine hiervon sind mit Sicherheit die HTML5 Mediaelemente, audio und video. Warum sollten diese Elemente also zum Einbinden von Multimediainhalten genutzt werden, wenn immernoch Flash oder andere Plugins – zumindest als Fallback genutzt werden müßen?

audio/video Elemente sind einfacher zu Handhaben als object-/embed-Elemente

Im Laufe der Zeit haben sich eine Menge Einbindetechniken für Flash-Player entwicklet. Diese Menge an Einbindetechniken sind jedoch kein Zeichen von Flexibilität, sondern Ausdruck für die Unzulänglichkeiten jeder jeweils anderen Einbindetechnik. Ein paar Beispiele:

  • verschachtelte object-Elemente bzw. verschachtelte object-/embed – Elemente

    fehleranfällig, da unterschiedlicher Code für unterschiedliche Browser; kaum ein Entwickler kann sich den genauen Einbindecode merken; unelegantes HTML

  • Einbindung mit swfobject oder ähnlichem

    kann nicht ohne Javascript funktionieren; kaum ein Entwickler kann sich den Einbindecode merken; Herausforderungen dies elegant mit einem CMS zu lösen

  • Javascript transformiert ein HTML-Element automatisch zum Videoplayer-Code (z. B.: a.video -> object)

    kann nicht ohne Javascript funktionieren; angesichts der Vorhandenen HTML5-Semantik ist diese Einbindung semantisch überholt

Auf der anderen Seite stehen die HTML5-Elemente audio/video, welche die Einbindung von Mediainhalten so einfach machen, wie die Einbindung von Bildern.

	<video src="mein-video.mp4" poster="mein-poster-bild.jpg" controls="controls"></video>

Unterstützt der Browser nicht das video-Element bzw. das Video-Format kann dies mit Javascript erkannt und in ein unterstütztes object-Element umgewandelt werden. Was bleibt ist jedoch ein klarer Einbindecode, der semantisch und elegant ist und in modernen Browsern grundsätzlich auch ohne Javascript und vor allem ohne weitere Plugins funktioniert.

Es funktioniert auf mehr Endgeräten

Spätestens seit der unsäglichen iPhone/iPad – Flash Diskussion wissen alle, daß Flash nicht auf allen Geräten installiert ist. Bindet man Mediainhalte per HTML5 ein und fällt – bei nicht Unterstützung – auf ein oder besser mehrere mögliche Plugin(s) (Flash, Quicktime, VLC etc.) zurück, erreicht man schlichtweg mehr User.

jMediaelement – Video/Audio für alle

Das jMediaelement-Script auf Basis von jQuery stellt eine Lösung dar, welche erkennt, ob die HTML5-Mediaelemente im allgemeinen und die konkreten Mediadaten im besonderen unterstützt werden. Bei Bedarf bindet es unterstützte Plugins ein (derzeit Flash auf JW Player Basis sowie VLC), normalisiert die unterschiedlichen APIs und hilft bei der Erstellung von stylbaren Kontrollelementen für die Player.

Mediainhalte mit jMediaelement einbinden

Die Einbindung von Video- und Audio – Dateien mit jMediaelement ist dementsprechend sehr einfach. Als Grundlage muß man lediglich den HTML Code schreiben, wie er durch HTML 5 vorgesehen ist. Die Attribute poster, loop, autoplay, controls werden hierbei unterstützt. Man kann statt einer Source-Datei auch mehrere Dateien verwenden. Als mögliche Source-Dateien kommen neben MP3, MP4 und OGG auch FLV, MOV und weitere in Frage.

Nachfolgend ein paar Beispiele:

<video src="mein-video.mp4" poster="mein-poster-bild.jpg" controls="controls"></video>

<video loop="loop">
	<a class="source" href="mein-video.flv" controls="controls">Video als flv</a>
	<source src="mein-video.flv" />
</video>

<video poster="mein-bild.png" autoplay="autoplay" controls="controls">
	<a class="source" href="mein-video.ogg">Video als ogg</a>
	<source src="mein-video.ogg" />
	<a class="source" href="mein-video.mp4">Video alsmp4</a>
	<source src="mein-video.mp4" />
</video>

Anschließend ruft man einfach das jQuery Plugin auf den Audio/Video Elementen auf. In der Regel muß man hier nur den Pfad zur Flashplayerdatei konfigurieren:

$('audio, video').jmeEmbed({
    jwPlayer: {
        path: '../mediaplayers/player.swf'
    }
});

Zur “kugelsicheren” Einbindung mit schönem Fallback, falls alle Stricke reißen, kann noch etwas mehr getan werden, aber dies soll hier erstmal reichen.

Multimedia scripten war noch nie so einfach

Eine Sache die mit jMediaelement wirklich Spaß macht, ist das Scripten der Player. Zum einen ist die API für die unterschiedlichen Player normalisiert, zum anderen erweitern diese jQuery selbst. Gleichgültig welche API und welcher Browser verwendet wird, ist und bleibt das HTML5-Element hierbei das anzusprechende Element, so daß mit den Mediaplayern gearbeitet werden kann, wie man dies von jQuery gewohnt ist. Eine Auflistung der meisten Methoden findet sich im jMediaelement-Wiki.

Hier ein paar Beispiele:

Sind mehrere Video- und/oder Audio-Player auf einer Seite und sollen, sobald der User einen Player startet, alle anderen pausiert werden, könnte dies folgender einfacher Code bewerkstelligen:

$(document).bind('play', function(e){
	$('audio, video').not(e.target).pause();
});

Wir wollen Sprungmarken in das Video erlauben. Klickt der User auf eine Sprungmarke springt der Player an diese Stelle und fängt an zu spielen. Wir haben hierfür folgendes HTML vorbereitet:

<video class="skip-video" src="mein-video.mp4" poster="mein-poster-bild.jpg" controls="controls"></video>
<ul class="skip-links">
	<li><a href="#" data-time="10">jump to 10</a></li>
	<li><a href="#" data-time="20">jump to 20</a></li>
	<li><a href="#" data-time="30">jump to 30</a></li>
</ul>

Dann würde unser Javascript wie folgt aussehen:

$('ul.skip-links').delegate('a', 'click', function(e){
	var time 	= parseInt($(this).attr('data-time'), 10);
	if(isNaN(time)){return false;}
	$('video.skip-video')
		.currentTime(time)
		.play()
	;
	return false;
});

Wir wollen, daß ein Video automatisch, aber im gemuteten Zustand, abspielt. Zwar kennt der HTML5 Standard das Attribut autoplay, aber für den mute-Zustand gibt es keine Markup-API. Unser HTML könnte dann so aussehen:

<video class="automute" autoplay="autoplay" src="mein-video.mp4" poster="mein-poster-bild.jpg" controls="controls"></video>

… und unser Javascript wie folgt:

$('video.automute').muted(true);

Bisher haben wir die Player nur in verschiedene Zustände versetzt (currentTime, play, pause, muted). Es ist aber genauso möglich, Zustände auszulesen. Hierzu müßen wir allerdings abwarten, bis die API bereit ist, ausgelesen zu werden. Dies sagt uns die Methode jmeReady. Hier ein kleines Beispiel:

$('video.myvideo').jmeReady(function(){
	var muted = $(this).muted(); //true|false
	var time = $(this).currentTime(); //am Anfang wohl in der Regel 0
	//etc.
});

Manche Daten wie beispielsweise die Länge des Videos/Audios sind erst noch später verfügbar, nämlich dann, wenn die Metadaten geladen wurden, wofür das Event loadedmeta zur Verfügung steht. Hier ein Beispiel:

$('video.myvideo').bind('loadedmeta', function(){
	var duration = $(this).getDuration(); //das video dauert xxx Sekunden
});

Unter meinen jMediaelement-Demos befindet sich eine Audioplayer-Demo, welche zeigt, wie einfach mit der Methode loadSrc eine Playliste erstellt werden kann.

Stylebare Kontrollelemente erstellen

Die Erstellung von stylbaren HTML-Kontrollelementen wird durch jMediaelement ebenfalls unterstützt. Hierbei ging es mir um die größtmögliche Flexibilität für den Webautoren. Anstatt Kontrollelemente in einer mehr oder – in der Regel – weniger konfigurierbaren, lokalisierbaren und barrierearmen Form mit Javascript in das HTML zu Rendern, darf(/muß) der Entwickler sein HTML selber schreiben. Das einzige was vorgegeben wird, ist eine HTML-Klasse, welche dem Script sagt, wie sich das Kontrollelement verhalten sowie ein Mechanismus, welcher Player hierdurch gesteuert werden soll.

Wir wollen beispielsweise einen Play-/Pause-Togglebutton machen. Die vorgegebene Klasse nennt sich hierfür ‘play-pause’.

Unser HTML könnte dann wie folgt aussehen:

<a href="#" class="play-pause">play / pause</a>
<!-- oder -->
<button class="play-pause">play / pause</button>
<!-- oder, aber nicht empfohlen, da nicht tastaturbenutzbar -->
<span class="play-pause">play / pause</span>

Folgende Schreibweisen wären jQuery UI Themeroller kompatibel:

<a href="#" class="play-pause"><span class="ui-icon"></span> play / pause</a>
<!-- oder -->
<button class="play-pause"><span class="ui-icon"></span> play / pause</button>
<!-- oder, empfohlen: -->
<button class="play-pause"><span class="ui-icon"></span> <span class="button-text">play / pause</span></button>

Für die Assoziierung eines Kontrollelements mit einem bestimmten video-/audio-Element, biete ich im Prinzip 3 Möglichkeiten an:

  • gemeinsames wrapper-Element
  • data-controls-Attribut am Kontrollelement (HTML5-valide, data-controls=”IDREF”)
  • data-controls-Attribut an einem wrapper-Element

gemeinsames wrapper-Element

Wird die jQuery-Methode ‘jmeControl’ auf einem Wrapper-Element ohne data-controls-Attribut aufgerufen, werden alle darin befindlichen Kontrollelemente mit dem ersten gefundenen video/audio-Element assoziiert:

Unser HTML:

<div class="video-wrapper">
	<video src="mein-video.mp4" poster="mein-poster-bild.jpg"></video>
	<a href="#" class="play-pause">abspielen / pausieren</a>
	<a href="#" class="mute-unmute">ton aus / ton an</a>
</div>

Unser Javascript:

$('div.video-wrapper').jmeControl();

data-controls-Attribut am Kontrollelement

Soll das Kontrollelement eigentlich ganz woanders auf die Webseite, gibt es unter Umständen keinen gemeinsamen wrapper, welcher andere video/audio-Elemente ausschließt bzw. wäre obige Schreibweise ineffizient.

Unser HTML:

<div class="video-wrapper">
	<video id="my-video" src="mein-video.mp4" poster="mein-poster-bild.jpg"></video>
</div>
<!-- ganz viel HTML dazwischen -->
<a data-controls="my-video" href="#" class="play-pause">abspielen / pausieren</a>

… und unser Javascript:

$('a.play-pause').jmeControl();

data-controls-Attribut an einem wrapper-Element

Exsistieren mehrere Kontrollelemente weit vom zu kontrollierenden video-/audio-Tag entfernt, wäre es müßig jedem einzelnen Element das data-controls-Attribut zu geben.

Unser HTML:

<div class="video-wrapper">
	<video id="my-video" src="mein-video.mp4" poster="mein-poster-bild.jpg"></video>
</div>
<!-- ganz viel HTML dazwischen -->
<div class="control-wrapper" data-controls="my-video">
	<a href="#" class="play-pause">abspielen / pausieren</a>
	<a href="#" class="mute-unmute">ton aus / ton an</a>
</div>

… und unser Javascript:

$('div.control-wrapper').jmeControl();

Konfiguration der Kontrollelemente

Die Kontrollelemente sind daneben zusätzlich konfigurierbar. Beispielsweise sind alle slider sowie die progressbar Teil von jQuery UI und alle Optionen jQuery UI Optionen können verwendet werden. Wir wollen beispielsweise unsere Klassen Namespacen, da wir in einem ganz anderen Modul bereits die Klasse play-pause verwenden, außerdem wollen wir, daß der bereits abgespielte Teil auf der Timeline anders eingefärbt wird und sich der Sliderhandle animiert auf der Timeline bewegt.

Unser HTML:

<div class="video-wrapper">
	<video id="my-video" src="mein-video.mp4" poster="mein-poster-bild.jpg"></video>
</div>
<div class="control-wrapper" data-controls="my-video">
	<a href="#" class="jme-play-pause">abspielen / pausieren</a>
	<div class="jme-time-slider"></div>
</div>

… und unser Javascript:

$('div.control-wrapper').jmeControl({
	classPrefix: 'jme-',
	timeSlider: {
		range: 'min',
		animate: true
	}
});

Fazit: Eine API sie zu knechten

Als ich mit dem Projekt angefangen habe, stand die Auseinandersetzung mit den HTML5 Mediaelementen im Vordergrund. Als ich dem ganzen einen Sinn verleihen wollte, war für mich der erste Gedanke, so viel HTML 5 wie möglich so wenig Flash/Plugins wie nötig, möglich zu machen. Diese Sichtweise hat sich inwzischen enorm geändert. jMediaelement ist für mich ein Tool, welches es erlaubt Mediainhalte einerseits einfach und elegant einzubinden und gegebenefalls ebenso einfach zu scripten und zu stylen sowie andererseits Mediainhalte auch dort zugänglich zu machen, wo entsprechende Plugins (noch) nicht verfügbar sind. Aber wem mein erster Gedanke eher gefällt, kann jMediaelement auch für seine Zwecke nutzen :-) .

Written April 19, 2010 by
protofunc

jMediaelement – HTML 5 Audio/Video für alle

Tags: javascript

Warum das gut, einfach anzuwenden und auch mächtig ist, schreibe ich bestimmt bald, aber jetzt gibt es einfach nur ein paar kurze jMediaelement-Demos.

Written March 17, 2010 by
protofunc

Das Problem mit dem HTML 5 Video Element

Tags: HTML 5, deutsch, javascript, video

Ich arbeite in letzter Zeit immer wieder mit dem HTML 5 Video Element. Bis auf einige kleinen Problemchen mit der API sowie einigen grausamen Implementierungsbug im Safari (betrifft nicht Chrome), bin ich eigentlich ganz zufrieden.

Es gibt aber letztendlich eine Sache die mich extrem rasend macht. Schaut man sich die Youtube HTML5 Demo an und vergleicht diese mit der Spezifikation wird einem klar, daß Youtube mit HTML5 nie so aussehen kann, wie diese Demo einem glauben machen will.

Die Demo zeigt einen Fullscreen-Button. Leider ist die HTML5 Spezifikation hier sehr einduetig. Einerseits ist keine API für einen möglichen Fullscreen-Modus definiert andererseits werden Browserhersteller mit fetter, roter Schrift ausdrücklich davor gewarnt, eine solche zu implementieren. Als Begründung wird auf mögliche “Nervigkeiten” sowie Angst vor Sicherheitsproblemen, genauer Phishing Attacken, abgestellt.

Letztendlich ist die Begründung vollkommen überholt und daneben. Das Security Argument stimmt nicht, da

  • ein Videoelement beim Starten des Vollbildmous grundsätzlich einen entsprechenden Hinweis zeigen soll
  • ein Videoelement im Vollbildmodus, unabhängig vom Vorhandensein des controls-Attribut, die Kontrollelemente anzeigen muß
  • es im Vollbildmodus gar nicht möglich ist – und das wäre das eigentliche Sicherheitsrisiko – Tastaturevents abzufangen

Auch das Risiko einer Belästigung des Users ist gering, wenn man es mit ähnlichen Einschränkungen implementieren würde wie dies bei Adobe Flash getan wurde. Gerade Flash, welches häufig für nervige Dinge wie Werbebanner eingesetzt wird, hat eine API für den Fullscreenmodus, anstatt daß dies mich nervt, empfinde ich es als extrem nützlich.

Hier lag wohl auch augenscheinlich das Problem. Eine API für den Fullscreenmodus wurde mit Methoden wie window.open oder window.resize verglichen, ohne zu erkennen, daß das Anschauen von Videos im Vollbildmodus ein wesentlich nützlicheres Feature ist als das ungefragte Öffnen von Popups und Verschieben von Browserfenstern.

Meine große Hoffnung liegt nun darin, daß entweder irgendein Browser (zum Beispiel: Chrome 4 oder 5 oder Internet Explorer 9) aus der Reihe tanzt und zeigt wie man ein cooles, sicheres Feature implementiert oder daß bei der Zugänglichkeits- (oder vielleicht auch Usability-) überarbeitung des Elements auffällt, daß ein Fullscreenmodus nicht allein mit der rechten Maustaste als Kontextmenü realisiert werden darf.

Ich kotze.

Written January 19, 2010 by
protofunc

widgetExtend: jQuery UI Widgets erweitern

Tags: deutsch, javascript, jquery

Letztendlich gibt es mehrere Möglichkeiten vorhandene jQuery UI Widgets zu erweitern. Die hierzu am häufigsten verwendete Methode ist die $.extend. Was in etwa so aussieht:

//neues a11yTabs erweitert tabs
$.widget('ui.a11yTabs', $.extend({}, $.ui.tabs.prototype, {
	select: function(){
		$.ui.tabs.prototype.select.apply(this, arguments);
	}
});
// tabs selbst erweitern
//altes select sichern
var oldSelect = $.ui.tabs.prototype.select;
$.extend($.ui.tabs.prototype, {
	select: function(){
		oldSelect.apply(this, arguments);
	}
});

Überschreibt man hierdurch eine bereits vorhandene Funktion, welche man noch nutzen möchte, muß man diese, wie im zweiten Beispiel geschehen, vor dem überschreiben zwischenspeichern, so daß man weiterhin auf die Originalmethode Zugriff hat.

Eine relativ elegante Schreibweise hierzu findet sich bei Felix Nagel, welcher folgenden einfachen Code zeigt:

// extends original ui.tabs widget
$.extend($.ui.tabs.prototype,{
	// copy original method
	_original_init: $.ui.tabs.prototype._init,
	// when widget is initiated
	_init: function() {
		var self = this, options = this.options;
		// fire original method
		self._original_init();

		// now we can do some accessibility stuff
	}
});

Wenn man derartiges drei- bis viermal schreiben muß, kommt man sich aber doch recht schnell etwas blöd vor. Als sich mir eben dieses Problem stellte, hatte ich daher folgende kleine extend-Methode geschrieben (noch nicht völlig durchgetestet):

var slice = Array.prototype.slice;
$.widgetExtend = function(widget, exts){
	var args = arguments;

	$.each(exts, function(name, fn){
		if( name in widget ){
			if( fn && $.isFunction(fn) ){
				fn._super = widget[name];
			} else {
				widget['_super'+ name] = widget[name];
			}
		}
		widget[name] = fn;
	});

	if( args.length > 2 ){
		args = slice.call(arguments, 2);
		args.unshift(widget);
		widget = $.widgetExtend.apply(this, args);
	}

	return widget;
};

Mit dieser kleinen Methode kann man, dannn ohne lästiges zwischenspeichern über arguments.callee._super auf die Hauptmethode zugreifen:

//neues a11yTabs erweitert tabs
$.widget('ui.a11yTabs', $.widgetExtend({}, $.ui.tabs.prototype, {
	select: function(){
                //$.ui.tabs.prototype.select kann eigentlich auch noch verwendet werden
		arguments.callee._super.apply(this, arguments);
	}
});
// tabs selbst erweitern

$.widgetExtend($.ui.tabs.prototype, {
	select: function(){
		arguments.callee._super.apply(this, arguments);
	}
});
Written January 9, 2010 by
protofunc

WAI-ARIA – Epic Fail: Reste fressen

Tags: javascript

Nicht wenige Aria Beispiele beschränken sich auf das wesentliche und statten unsemantisches HTML, insbesondere div und span-Elemente, mit den jeweiligen Aria-Attributen aus. In der Praxis wird regelmäßig semantisches HTML als Grundlage genommen. Gleichzeitig passieren hierbei jedoch zwei vermeidbare Fehler.

1. Die Verschachtelung der Aria-Attribute folgt der semantischen HTML-Struktur und nicht der Aria-Spezifikation

Ein typisches Beispiel ist eine Menüleiste, welche mit verschachtelten Listen aufgebaut wurde:

<ul role="menubar">
	<li role="menuitem" aria-haspopup="true">
		<a href="#" tabindex="0">Menubaritem</a>
		<ul role="menu" aria-hidden="true">
			<!-- weitere menuitems -->
		</ul>
	</li>
</ul>

Schaut man sich diese Struktur an und vergleicht sie mit der Aria-Spezifikation sollte auffallen, daß

  • der Menüeintrag das interaktive Objekt ist und nicht der Link
  • der Link innerhalb eines Menüs eigentlich ein artfremdes Objekt ist
  • ein Untermnü/Poupup-Menü kein Kind des dazugehörigen Menüitems

Ein entsprechend korrigiertes HTML könnte demnach wie folgt aussehen:

<ul role="menubar">
	<li role="presentation">
		<a role="menuitem" aria-haspopup="true" href="#" tabindex="0">Menubaritem</a>
		<ul role="menu" aria-hidden="true">
			<!-- weitere menuitems -->
		</ul>
	</li>
</ul>

Doch auch diese HTML-Struktur ist letztendlich fehlerhaft und wird – insbesondere von Jaws, dem marktführendne Screenreader – recht unangenehm gelesen.

2. Das Reste fressen

Überall im Netz findet man leider Scripte, die die HTML-Struktur so verändern, daß der Screenreader semantische Überreste der alten HTML-Struktur vorgeworfen bekommt. Nachfolgend ein paar Beispiele mit dem typischen a[href]-Problem:

<!-- Tabs -->
<a href="#" role="tab" tabindex="-1" aria-selected="false" aria-controls="tab-2">Ein Tab</a>

<!-- Menü -->
<a href="#" role="menuitem" tabindex="-1" aria-haspopup="true">Ein Menüitem</a>

<!-- Menübutton (hier gibt es eine kleine Ausnahme) -->
<a href="#" role="button" aria-haspopup="true">Menübutton</a>

Hierbei wird gerne übersehen, daß das href-Attribut eines Anchor-Elements, gleichzeitig immer als Accessibility-Wert des Links an die Zugänglichkeitsschnittstelle übergeben wird. Hat der Link keinen Namen, lesen einige Screenreader als Hilfe eben diesen Wert vor. Nun wurde jedoch in allen Beispielen die Rolle des Anchor-Elements auf eine andere Rolle gemappt und viele Screenreader lesen dann ebenfalls den Wert vor, auch wenn der Name vorhanden ist. Hierbei ist erschwerend zu beachten, daß nicht der Inhalt des HTML-Attributs vorgelesen wird, sondern die href-DOM-Eigenschaft, welche die – vom Browser berechnete – absolute URL darstellt.

Auf dieser Seite würde der Screenreader Jaws beim Fokusieren des oben dargestellten Menüeintrags folgendes vorlesen:

Menüeintrag Ein Menüitem H T T P Doppelpunkt Schrägstrich Schrägstrich w w w Punkt protofunc Punkt com Schrägstrich 2010 Schrägstrich 01 Schrägstrich 03 Schrägstrich wai Bindestrich aria Bindestrich epic Bindestrich fail Bindestrich reste Bindestrich fressen Schrägstrich Raute Untermenü

Im Ergebnis läßt sich folgendes sagen, wenn ein Script solch ein HTML produziert, sollte man dieses Script auf keinen Fall einsetzen. Es ist offensichtlich, daß dieser Code nicht einmal mit dem marktführenden Screenreader getestet wurde und es könnten daher noch weitere Bugs vorhanden.

Ein bereinigtes HTML könnte wie folgt aussehen:

<!-- Tabs -->
<a role="tab" tabindex="-1" aria-selected="false" aria-controls="tab-2">Ein Tab</a>

<!-- Menü -->
<a role="menuitem" tabindex="-1" aria-haspopup="true">Ein Menüitem</a>

<!-- Menübutton (hier gibt es eine kleine Ausnahme) -->
<a role="button" aria-haspopup="true" tabindex="0">Menübutton</a>

Diese Struktur – insbesondere beim Menübutton – führt zu einigen kleineren Problemen, die man beim Coden von CSS/JS berücksichtigen muß. Die Lösung(en) hierzu würde(n) allerdings den Rahmen sprengen.

Written January 3, 2010 by
protofunc
« older posts
Mail.ru