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