Fragen zu HTML5 & Co beantwortet 7 – Doctypes, CSS-Transformationen, Codecs und Semantik

Veröffentlicht am 17. Juli 2012

Ich bin zur Zeit viel unterwegs und komme nicht dazu, viel originelles zu schreiben. Also spielen wir einfach das altbekannte Spiel weiter: Leser fragen zu HTML5, CSS3, JavaScript und Co und der Erklärbär antwortet. Denn Fragen kommen jeden Tag bei mir an und weil die meisten dieser Fragen (und Antworten) viel zu schade sind, um sie in meinem E-Mail-Archiv verrotten zu lassen, hole ich sie im Rahmen der Artikelserie „Fragen zu HTML5 und Co“ regelmäßig ans Tageslicht – getreu dem Motto „dumme Fragen gibt es nicht“.

HTML5 in XHTML/HTML 4

Ich habe eine schöne HTML5-Seite gebaut mit schönen HTML5-Formularfeldern und so. Nun soll das Formular aber in eine HTML4/XHTML-Seite eingebunden werden und zwar direkt, nicht per <iframe>. Was kann im schlimmsten Fall passieren?

Passieren kann eigentlich gar nichts, die HTML5-Elemente dürften auch in Seiten mit anderen Doctypes funktionieren. Browsern sind Doctypes nämlich total egal. So lange der Browser in der prinzipiell Lage ist, ein Element zu verarbeiten, wird es tun, egal was in der ersten Zeile der HTML-Seite steht. Als kleine Demo: Ein Canvas-Element in einem Dokument mit dem Doctype HTML 3.2.

Wohlgemerkt: dass so etwas funktioniert, heißt nicht, dass man auch Gebrauch von dieser Möglichkeit machen sollte! Sofern es keine wirklich triftigen Gründe gibt, das HTML5-Formular in die Non-HTML5-Seite direkt einzubetten, sollte man es lassen – Webstandards gibt es nicht ohne Grund.

CSS-Transforms und Z-Index

Ich hantiere gerade mit den neuen CSS3-Transformationen. Meine HTML-Elemente sind über Z-Index in verschiedene Ebenen eingeteilt und vor der Anwendung einer Rotate-Transformation funktioniert diese Einteilung auch noch. Dannach aber verhalten sich die Elemente, als ob sie keine Z-Index-Eigenschaft hätten (Beispiel; wenn durch Hover auf der roten Box die Transformation aktiv wird, wird die grüne Box von der blauen Box trotz Z-Index verdeckt). Woran könnte das liegen?

Das ist eine ganz normale Wechselwirkung zwischen Transformationen und dem Layering-Mechanismus von CSS. Die Spezifikationen lassen uns wissen:

This module defines a set of CSS properties that affect the visual rendering of elements to which those properties are applied [...] Some values of these properties result in the creation of a containing block, and/or the creation of a stacking context.

Zu deutsch: mit CSS3 transformierte Elemente bilden einen eigenen, geschlossenen Kontext (Stacking Context). Wenn man die in einem solchen Element enthaltenen Kindelemente mit einem Z-Index versieht, gilt dieser nur in Relation zu anderen Kindelementen des transformierten Elements. Dieser Effekt tritt auf, weil nicht wirklich das Element plus seine Kindelemente transformiert werden, sondern es wird durch den Browser ein Gesamtbild des Elements und seines Inhalts errechnet, das dann transformiert wird. Und damit wird der Z-Index eines Kindelements in Bezug auf Elemente außerhalb des Elternelements natürlich bedeutungslos.

Audio- und Videocodecs abfragen

Wir sind gerade dabei, das Video-Element in unsere Seite einzubauen und es funktioniert soweit ganz gut. Ich möchte jetzt aber eine Fehlermeldung ausgeben, wenn keins der angebotenen Video-Formate vom Browser akzeptiert wird. Welches JavaScript muss ich da drauf werfen? :-)

Ob ein Browser ein Audio- oder Video-Format unterstützt lässt sich herausfinden, indem man die JavaScript-Funktion canPlayType() auf einem Audio - oder Video-Element ausführt. An die Funktion kann mal als String den MIME-Type bzw. den Codec übergeben, auf dessen Unterstützung man prüfen möchte, also z.B. so:

document.createElement("video").canPlayType("video/mp4");

Die Rückgabewerte dieser Funktion sind entweder ein leerer String (Format wird nicht unterstützt) oder der String "maybe" oder der String "probably". Das mutet etwas kurios an, ist aber eigentlich sowohl sinnvoll als auch praktisch.

Zum einen weiß der Browser unter Umständen wirklich nicht sicher, ob er etwas mit dem übergebenen Format anfangen kann. Wenn man z.B. Chrome 20 nach seine Unterstützung für "video/mp4" fragt, antwortet er "maybe", weil er aus dem MIME-Type allein nicht schließen kann, mit welcher Codec-Version er es zu tun bekommen wird. Zwar kann er prinzipiell etwas mit MP4-Dateien anfangen, aber ganz sicher sein kann sich der Browser ohne weitere Informationen eben nicht. Fügt man eine Codec-Angabe hinzu und fragt z.B. nach "video/mp4; codecs='avc1.42E01E'" (H.256 Baseline) kommt ein zuversichtlicheres "probably" zurück, denn jetzt weiß der Browser schon sehr viel genauer, was da auf ihn zukommt und er kann einschätzen, ob sein Codec-Fundus der Aufgabe gewachsen ist.

Außerdem haben Stings als Rückgabewerte den Vorteil, dass man Sie in JavaScript entweder sehr genau oder nur ungefähr unterscheiden kann, je nachdem was man möchte. Möchte man man sowohl "maybe" als auch "probably" als Antworten akzeptieren, ist die entsprechende If-Abfrage ganz einfach:

var canPlay = myVideo.canPlayType('video/mp4');
if(canPlay){
    macheEtwas();
}

Die Funktion macheEtwas() wird hier sowohl bei "maybe" als auch bei "probably" ausgeführt, da beide Strings als true durchgehen, der leere String (die negative Antwort von canPlayType()) gilt hingegen als false. Möchte man es hingegen genau nehmen und nur "probably" durchlassen, so ist das gar kein Problem:

var canPlay = myVideo.canPlayType('video/mp4');
if(canPlay === "probably"){
    macheEtwas();
}

Was auf den ersten Blick so kurios daher kommt, ist also eigentlich ganz patent.

Welches Element für Kommentare?

Wenn ich nach semantischem Markup für Kommentare suche, wird immer mit <article> pro Kommentar gearbeitet. Mir erschließt sich der Sinn dafür allerdings nicht. Ich finde eine <ol>-Liste wesentlich korrekter dafür. Wie siehst du das?

Ich persönlich halte beides für Varianten, die man einsetzen kann, je nach Situation. Die Spezifikationen scheinen recht eindeutig zu sein, denn sie erwähnen in ihrer Definition des <article>-Elements explizit Nutzerkommentare als möglichen Anwendungsbereich:

The article element represents a self-contained composition in a document, page, application, or site and that is, in principle, independently distributable or reusable, e.g. in syndication. This could be a forum post, a magazine or newspaper article, a blog entry, a user-submitted comment, an interactive widget or gadget, or any other independent item of content.

Aber jetzt bleibt natürlich die Frage, ob ein Nutzerkommentar tatsächlich eine self-contained composition und ein independent item of content ist. Und das, würde ich mal sagen, hängt von der betroffenen Webseite bzw. von dem Inhalt der dort produzierten Nutzerkommentare ab. Wenn wir uns z.B. eins der Werke von Ahoi Polloi ansehen, sind die Kommentare dort eher kurz und ohne den Kontext des besprochenen Haupt-Posts unverständlich. Ich denke nicht, dass ein Statement wie Hoil Polloi! eine self-contained composition und ein independent item of content ist. Wenn wir hingegen schauen, was die Besucher bei Stefan Niggemeier an Kommentaren hinterlassen, sieht die Lage anders aus. Ich zitiere mal Kommentar Nummer 7 aus Kein schöner Lanz:

Lanz führt die Tradition von Kerner fort. Besser noch, er hat dessen Stil perfektioniert. Genau dieses Talent hat das ZDF gesucht. Wir sprechen hier immerhin von einer Kernzielgruppe 50 +. Die wollen nicht denken, die wollen berieselt werden. Am besten von einem Schwiegersohn Marke Lanz oder Pilawa. Deshalb passt Lanz auch sehr gut zu Wetten dass. In diesem Sinne, kein Grund sich aufzuregen. Alles so gewollt. Alles gut. Immer diese Ansprüche …

Dieser Abschnitt könnte (wie die meisten Kommentare auf der Seite) ohne Probleme für sich allein stehen und ergibt auch ohne den Originalartikel so viel Sinn, dass man von self-contained composition und independent item of content sprechen kann.

Es ist wie immer bei der Semantik: es kommt darauf an, was man will. Nutzerkommentare bei Youtube oder auf /b/? Eher <ol>. Kommentare bei Zeit Online oder SPON? Eher <article>. Das ist jedenfalls meine Interpretation der Sache …

Weitere Fragen?

Eure Fragen zu HTML5, JavaScript und anderen Webtechnologien beantworte ich gerne! Einfach eine E-Mail schreiben oder Formspring bemühen und ein bisschen Geduld haben – falls ich gerade unterwegs bin, kann es mit Antwort manchmal etwas dauern, doch früher oder später schreibe ich garantiert zurück.

Fragen zu HTML5 & Co beantwortet 6 – Slashes, Web Storage, Formulare und falsche Arrays

Veröffentlicht am 29. Juni 2012

Als Erklärbär für HTML5 und andere Webtechnologien bekomme ich jede Woche neue Fragen rund um Webentwicklung gestellt. Da diese Fragen (und Antworten) viel zu schade sind, um sie in meinem E-Mail-Archiv verrotten zu lassen, hole ich sie im Rahmen der Artikelserie „Fragen zu HTML5 und Co“ regelmäßig ans Tageslicht – denn dumme Fragen gibt es schließlich nicht!

Slashes in selbstschließenden Elementen?

Mir ist noch nicht zu 100% klar, wie es sich mit HTML5 und selbstschließenden Elementen verhält. Sollte man diese gar nicht mehr verwenden? Also z.B. statt <input /> nur noch <input> und statt <br /> nur noch <br> verwenden? Ist es, wenn man ein sauberes HTML5-Dokument schreiben möchte, sinnvoll, weg von der XHTML-Syntax zu gehen? Oder ist es völlig egal?

HTML5 ist völlig egal, ob selbstschließende Tags mit einem Slash zugemacht werden oder nicht. Der HTML-Parser des Browser ignoriert den Slash komplett, denn dass es so etwas wie „Inhalt zwischen öffnendem und schließendem Input-Tag“ nicht gibt, ist fest in ihn einprogrammiert. Wenn der Slash aber vorhanden ist, ist er für den Browser reines Hintergrundrauschen und stört nicht groß.

Kurz gesagt ist ist <input /> aus Browser-Sicht genau so richtig wie <input>. Die einzig maßgebliche Entscheidungshilfe in dieser Frage sollte der Code-Standard des eigenen Teams oder Projekts sein.

Warum funktioniert Web Storage nicht?

Ich habe mir zur Einführung in HTML5 (speziell das Thema Local Storage) eine Notizbuch-Applikation aus dem Internet rausgesucht. Diese hat im Firefox, Opera und Chrome auch funktioniert, nur im Safari 5.1 und Internet Explorer 9 wurden die eingegebenen Werte nicht übernommen. Laut meiner Recherche sollte Local Storage aber in der jeweiligen Version vom Safari und Internet Explorer funktionieren. Javascript ist auch aktiviert. Wie kann das sein?

So etwas kann passieren, wenn eine Webseite nicht über einen Webserver, sondern über das lokale Dateisystem (file://) aufgerufen wird. Web Storage arbeitet, wie viele andere HTML5-Technologien auch, auf einer Pro-Origin-Basis. Die Daten werden getrennt nach Host, Protokoll und Port einer Webseite gespeichert, damit http://foo.de nicht die Daten von https://www.bar.com auslesen kann. Bei einem Aufruf über das lokale Dateisystem gibt es aber weder Host noch Protokoll noch Port –also keinen Origin – und einige Browser verweigern dann eben den Dienst. Das ist bei vielen HTML5-Technologien so, nicht nur bei Web Storage.

Mehrere Formular-Ziele mit HTML5

Gibt es in HTML5 die Möglichkeit, mehrere Submit-Buttons in einem Formular zu haben und hinterher zu unterscheiden, welcher gedrückt wurde?

Diese Möglichkeit gibt es, allein die Browserunterstützung ist etwas problematisch. Mit Hilfe eines formaction-Attributs auf einem Submit-Button kann ein Formular dazu gebracht werden, seine Daten an eine andere URL als die im action-Attribut des <form> notierte zu schicken:

<form action="http://foo.com">
  <input type="submit">                            <!-- sendet an foo.com -->
  <input type="submit" formaction="http://bar.de"> <!-- sendet an bar.de -->
</form>

Schade nur, dass es mit der Browserunterstützung vergleichsweise finster aussieht – IE < 10 und der Android-Browser wissen mit dem Attribut nichts anzufangen. Da hilft wohl höchstens ein JavaScript-Hack …

forEach für getElementsByTagName

Funktioniert forEach() eigentlich auch mit Sachen wie document.querySelectorAll() und document.getElementsByTagName()? Ich habe es mit Opera, Firefox und IE9 probiert und es wird der Fehler „forEach ist keine Funktion“ ausgeworfen. Mit „normalen“ Arrays klappt es …

Dieses Verhalten hat so seine Richtigkeit, wenn es auch nicht besonders hilfreich ist. Es ist tatsächlich so, dass forEach() eine Methode von Arrays ist, aber das, was man von getElementsByTagName() und Konsorten zurückbekommt, sind gar keine Arrays! Stattdessen handelt es sich um NodeLists, die außer der length-Eigenschaft und der Möglichkeit, auf enthaltene Elemente über ihren Index zuzugreifen nichts mit Arrays gemein haben. Sie stammen nicht von Array.prototype ab und daher gibt es für sie auch kein forEach().

„Reparieren“ lässt sich dies (in modernen Browsern), indem man die NodeList in ein Array verwandelt. Einfach die slice()-Methode von Array.prototype ausleihen und sie mittels call() auf die NodeList anwenden:

var nodes = document.getElementsByTagName('div');
var arr = Array.prototype.slice.call(nodes);
typeof arr.forEach; // "function"

In nicht ganz so neuen Browsern (IE8 und älter) hilft eine Schleife:

var nodes = document.getElementsByTagName('div');
var arr = [];
for(var i = 0, l = nodes.length; i < l; i++){
    arr.push(nodes[i]);
}

Dieses Prinzip funktioniert mit ziemlich allen Array-ähnlichen Objekten, denen man im so Browser über den Weg läuft (wie z.B. FileList und arguments).

Weitere Fragen?

Eure Fragen zu HTML5, JavaScript und anderen Webtechnologien beantworte ich gerne! Einfach eine E-Mail schreiben oder Formspring bemühen und ein bisschen Geduld haben – falls ich gerade unterwegs bin, kann es mit Antwort manchmal etwas dauern, doch früher oder später schreibe ich garantiert zurück.

ECMAScript 6: Maps, Sets und Weak Maps

Veröffentlicht am 13. Juni 2012

Während andere Programmiersprachen Entwicklern eines ganzes Arsenal von Datenstrukturen zur Sammlungen von Objekten anbieten – Listen, Sets, Trees usw. – muss man sich in JavaScript seit jeher mit Arrays und Objekten begnügen. Diese magere Auswahl führt häufiger zu einem leisen Zähneknischen, denn die aus Bordmitteln gebauten Ersätze sind, wenn überhaupt machbar, umständlich und nicht besonders performant. Um an dieser Stelle für Linderung zu sorgen, führt ECMAScript 6 einen ganzen Haufen von neuen Cellections ein, von denen wir uns heute einmal Maps, Sets und Weak Maps genauer ansehen werden.

Maps

Maps sind ähnlich wie normale JavaScript-Objekte Paare aus Namen und Werten. Mit ihrer set()-Methode können neue Name-Wert-Paare in der Map gespeichert werden und mit get() können die Werte wieder ausgelesen werden:

var myMap = new Map();
myMap.set('foo', 42);
myMap.get('foo');      // 42

Der Clou hierbei: als Namen können alle JavaScript-Datentypen herhalten, nicht nur Strings:

var myMap = new Map();
myMap.set(42, 'foo');           // Number als key
myMap.set([1, 2, 3], 'bar');    // Array als key
myMap.set(function(){}, 'baz'); // Funktion als key
myMap.set(NaN, 'buh');          // Selbst NaN geht!

Damit wird es natürlich sehr viel leichter, Metadaten zu Objekten wie z.B. DOM-Nodes oder jQuery-Objekten zu verwalten. Weder ist es nötig, die Metadaten im Objekt selbst zu speichern (z.B. als data-*-Attribute in HTML-Elementen) noch muss man sich mit der Zuordnung eines Objekts zu einem Schlüssel in einem anderen Objekt abmühen – die Objekte selbst sind die Schlüssel!

Wichtig ist: als Schlüssel dienen wirklich die Objekte selbst und sie werden (fast) wie bei einem Vergleich mit === unterschieden.

// get() ergibt undefined - die Funktionen in Get und
// Set sind unterschiedliche Objekte
myMap.set(function(){}, 'foo');
myMap.get(function(){});

// get() ergibt "bar" - die Funktion in Get und Set
// sind jeweils das gleiche Objekt
var fn = function(){};
myMap.set(fn, 'bar');
myMap.get(fn);

Die einzigen Ausnahmen sind an dieser Stelle NaN, das als identisch zu NaN gewertet wird (obwohl eigentlich NaN !== NaN gilt) und +0 bzw. -0, die unterschieden werden (obwohl eigentlich +0 === -0 gilt).

Neben set() und get() haben Maps noch die folgenden drei Methoden:

  • myMap.has(key): Gibt an, ob unter dem Schlüssel key etwas gespeichert ist (Boolean)
  • myMap.delete(key): Löscht die unter dem Schlüssel key gespeicherten Daten
  • myMap.size(): Gibt die Anzahl der gespeicherten Name-Wert-Paare zurück

Eine Möglichkeit über Maps zu iterieren sehen die ECMAScript-Entwürfe zur Zeit nicht vor und auch kein Browser hat diesbezüglich etwas in petto. Dass in der Richtung noch nachgerüstet wird, ist aber nicht besonders unwahrscheinlich.

Sets

In Sets können wie in einem Array beliebige Daten abgelegt werden, wobei allerdings jeder Wert nur einmal im Set vorhanden sein kann. Mit der has()-Methode kann geprüft werden, ob ein Wert in einem Set vorliegt:

var mySet = new Set();
mySet.add(42);
mySet.has(42); // true

Ob ein Wert in einem Set vorhanden ist, ist eine einfache Ja/Nein-Frage. Führt man mehrmals mySet.add(42) aus, ändert sich nichts, denn der Wert 42 ist ja schon nach dem ersten Mal im Set vorhanden. Neben add() und has() kennen Sets noch die Methoden delete() zum Löschen eines Werts und size() zur Angabe der Anzahl der gespeicherten Datensätze.

Wie bei Maps auch werden die Werte in Sets mit === vergleichen mit den gleichen Ausnahmen für NaN sowie +0 und -0. Auch eine Möglichkeit über die gesammelten Daten zu iterieren fehlt Sets noch – Nachrüstung nicht ausgeschlossen.

Weak Maps

Weak Maps funktionieren wie normale Maps, verhindern aber nicht, dass ihre Schlüsselobjekte der automatischen Speicherbereinigung von JavaScript zum Opfer fallen. JS-Engines kümmern sich automatisch darum, Speicher für Objekte vorzuhalten und sie geben auch automatisch Speicher wieder frei, sobald ein Objekt nicht mehr gebraucht wird. Den Status „nicht mehr gebraucht“ erhält ein Objekt, sobald keine Variablen mehr auf es zeigen und es damit vom laufenden Programm nicht mehr erreicht werden kann. Im folgenden Beispielcode würde der vom Objekt belegte Speicher erst dann wieder frei gegeben, wenn die Variablen foo und bar nicht mehr auf das Objekt zeigen, dieses dadurch nicht mehr erreichbar und damit nicht mehr benötigt wird.

var foo = {};
var bar = foo;

Die gleiche Lage gibt es auch beim Einsatz von Maps:

var myMap = new Map();
var keyObj = { foo: 42 };

myMap.set(keyObj, 'foo'); // "foo" unter `keyObj` speichern
keyObj = undefined;       // `keyObj` referenziert nicht mehr das Objekt

Das Problem ist nur: wie werden wir jetzt das Objekt los, das unter keyObj gespeichert war? Da wir keine Referenz mehr in Form einer Variablen auf das Objekt haben, können wir myMap.delete() nicht benutzen. Andererseits wird das Objekt ja weiterhin von der Map referenziert und wird daher bis in alle Ewigkeit von der Speicherbereinigung verschont bleiben. Passiert so etwas häufiger, hat man ein hässliches Speicherproblem.

Weak Maps schaffen hier Abhilfe, denn sie referenzieren Objekte in ihren Keys nur schwach. Wenn die einzige verbliebene Referenz auf ein Objekt der Schlüssel in einer Weak Map ist, darf die Speicherbereinigung das betreffende Objekt entfernen. Nehmen wir also den Problemcode von oben und ersetzen die Map durch eine Weak Map, löst sich das Speicherproblem in Luft auf:

var myMap = new WeakMap();
var keyObj = { foo: 42 };

myMap.set(keyObj, 'foo'); // "foo" unter `keyObj` speichern
keyObj = undefined;       // `keyObj` referenziert nicht mehr das Objekt

Sobald keyObj nicht mehr das Objekt referenziert, ist die einzig verbliebene Referenz die in der Weak Map. Da diese Referenz nur schwach ist, wird das Objekt beim nächsten Speicherbereinigungsdurchlauf gelöscht und der Speicher freigegeben. Naturgemäß kennen Weak Maps anders als Maps keine size()-Methode und auch eine Iterationsmöglichkeit wird es nicht geben.

Unterstützung und Ausblick

Maps, Sets und Weak Maps sind in Chrome Canary (bei aktiviertem Strict Mode) und Firefox ab Version 13 (Weak Maps ab Version 6) vorhanden. In Chrome muss unter about:flags der Punkt „Enable Experimental JavaScript“, Firefox und Firefox Mobile unterstützen die neuen Features ohne weiteres zutun. In Node.js können Weak Maps mit dem Startparameter --harmony_weakmaps aktiviert werden.

Komplett fertig und stabil scheinen die neuen Datenstrukturen noch nicht zu sein, zumindest mit Iterationsmöglichkeiten für Sets und Maps ist noch zu rechnen. Andererseits gibt es nun in Chrome bzw. V8 und Spider Monkey (Firefox) bereits zwei Implementierungen – ganz so groß werden die Verwerfungen also wohl nicht mehr werden und zumindest dass die nächste ECMAScript-Version Maps, Sets und Weak Maps haben wird, scheint sicher. Weiterer Lesestoff:

Die Responsive Images Story

Veröffentlicht am 22. Mai 2012

Responsive Images sind zur Zeit in aller Munde und verursachen viel Streit. Was steckt dahinter und wert streitet mit wem? Zeit für einen Überblick von jemandem, der mitten drin steckt und selbst an der Entwicklung von Entwürfen zum Thema beteiligt war. Ein Gastbeitrag von Anselm Hannemann.

Im Juli letzten Jahres kam die Diskussion um responsive images in Webseiten auf. Wir können zwar mittlerweile die tollsten Layouts für verschiedene Endgeräte optimieren, aber die inhaltlichen Bilder (<img>-Elemente) bleiben dabei immer die gleichen. Und damit hat man natürlich einen großen Nachteil: Große Bilder für Desktopauflösungen werden auch bei Mobilgeräten in voller Dateigröße geladen. Andersherum kann für ein mobile optimiertes Bild kein qualitativ passendes Bild auf ein iPad3 mit HighRes-Display ausgegeben werden. Deshalb waren mehrere Leute der Meinung, dass Bilder ebenfalls responsiv werden müssen.

Historie und Initiativen

Die eine Fraktion der Interessierten wollte direkt ein streamendes Bildformat, was durchaus eine sehr gute und anstrebenswerte Lösung wäre. Allerdings werden damit Randbereiche wie andere Bildausschnitte nicht abgedeckt. Außerdem würde das neue Format wohl erst in frühestens zehn Jahren überall einsetzbar sein. Die andere Fraktion hingegen beschränkte sich auch deshalb auf HTML und CSS als Möglichkeiten. Zu dieser Fraktion gehörte auch ich und deshalb veröffentlichte ich in der Diskussion über das Problem und mögliche Lösungen auf der WHATWG-Mailingliste Ende Juli 2011 folgenden Vorschlag:

<img 
    src="http://cdn.url.com/img/myimage_xs.jpg" 
    media-xs="(min-device-width:320px and max-device-width:640px)" 
    media-xs-src="http://cdn.url.com/img/myimage_xs.jpg"  
    media-m="(min-device-width:640px and max-device-width:1024px)" 
    media-m-src="http://cdn.url.com/img/myimage_m.jpg"  
    media-xl="(min-device-width:1024px)" 
    media-xl-src="http://cdn.url.com/img/myimage_xsl.jpg" 
/>

Dieser wäre prinzipiell mit alten Browsern kompatibel gewesen, da er auf dem bereits existenten img-Element basiert und gleichzeitig eine einfache Syntax für alternative Ressourcen enthält – diese würden über über ganz normale CSS3 @media-queries angesteuert.

Der große Nachteil bei dieser Lösung ist, dass das bisher bekannte src-Attribut nach aktueller Logik der Browser immer geladen werden würde. Im Zweifelsfall lädt der Browser also zunächst das in src referenzierte Bild (das immer das kleinste Format sein sollte) und zusätzlich das passende höher auflösende Bild. Es würden also zusätzliche HTTP-Requests und mehr Datenmengen anfallen, als eigentlich erforderlich. Dies könnte jedoch für moderne Browser aufgehoben werden, wenn die Browserhersteller bei der Implementierung berücksichtigen. Hier war zumindest von Browserseite schnell eine ablehndende Haltung vorhanden, da angeblich größerer Aufwand nötig sei.

Nach ein paar Antworten schlief die Diskussion in der WHATWG Mailing Liste jedoch schnell ein. Das schlimme daran war jedoch, dass viele damals der Meinung waren, dass solch eine Lösung nicht gebraucht werde, da ja offenbar kein Bedarf für responsive images da sei. In diesem Zuge schrieb Ian Hickson selbst im Januar noch

What’s the use case for doing it for images in <img> elements? Typically <img> elements are for content images, where you don’t usually want to adapt anything.

Im Nachhinein ist diese Entwicklung insofern fatal, als dass einige dieser Leute nun im Mai 2012 einen First Draft für die WHATWG veröffentlicht haben. Als ich aber unterwarteterweise im Oktober 2011 wieder auf das Thema angesprochen wurde und auch mehrere Leute wieder anfingen über das Thema in Blogbeiträgen zu diskutieren, wendete sich zunächst das Blatt. Erneut kam das Thema auf die WHATWG-Mailingliste, wiederum jedoch nicht wirklich mit großer Resonanz.

Gründung der W3C Community Group "Responsive images" und das <picture>-Element

Plötzlich wurde im Februar 2012 dann vom W3C eine Community Group "Responsive images" eingerichtet. Eine Community Group ist eine Plattform, auf der jeder, der mitmachen möchte, über spezifische Probleme mit HTML/CSS diskutieren kann. Damit war endlich ein Ort geschaffen, an dem sinnvoll über Vor- und Nachteile der diversen Code-Vorschläge und der zu eruierenden Funktionalitäten und Eigenschaften diskutiert werden konnte. Schnell wuchs die Gruppe auf über 70 Teilnehmer heran, rege Diskussionen wurden geführt, Lösungen vorgeschlagen und verworfen. Letztlich blieb nur noch ein Vorschlag übrig: das <picture>-Element. Dabei handelt es sich um ein komplett neues Element, das kein Standard-Browserverhalten mitbringt, wie es das bisherige img-Tag macht, das aber eben mehrere Ressourcen beinhalten kann. Die dazugehörige Syntax sollte wie folgt aussehen:

<picture alt="Alt tag describing the image represented">
    <source src="photo.jpg" />
    <source src="photo@2x.jpg" media="min-device-pixel-ratio:2" />
    <img src="photo.jpg" />
</picture>

Es handelt sich also um eine Liste an Bildern in <source>-Elementen, angereichtert und abgefragt nach Media Queries, plus ein mögliches Fallback-img-Tag am Ende für ältere Browser. Der Vorteil dieser Technik ist, dass Bilder nicht automatisch geladen werden und nur das angefordert wird, was wirklich nötig ist. Das funktioniert sogar bereits, denn Scott Jehl hat hierzu auf GitHub den sogenannten picturefill entwickelt, einen Polyfill für das <picture>-Element. Sogar eine eine Methode zum bereits heute standardkonformen Einsatz gibt es. Diesen Vorteilen stehen aber auch einige ungelöste Probleme gegenüber.

Da wäre zum Beispiel die Frage der Ladezeit bei Geräten mit hoher Auflösung. Bloß weil ein iPad3 mit hochauflösendem Display ein Bild anfordert, ist ja noch lange nicht gesagt, dass dieses auch schnell geladen wird. Ist man beispielsweise per GPRS unterwegs, was zumindest in Deutschland dank knapp bemessener „Flatrates“ häufig der Fall ist, so möchte man vielleicht auch auf einem Highres-Bildschirm die kleine Variante des Bildes haben. Da stößt man nun mit den bisher zur Verfügung stehenden Media Query-Abfragen auf Probleme, denn eine Bandbreitenabfrage ist für einen Browser extrem schwierig durchzuführen. Auch die Syntax ist nicht unproblematisch; schließlich ist der ganze Spaß mit einigem Mehraufwand verbunden, wenn ich für jedes Bild mehrere Ressourcen angeben muss und für jede Ressource noch Media Queries festlegen muss.

Matthew Willcox beschäftigte sich deshalb intensiv in den letzten Wochen mit Möglichkeiten, die Media Query-Vergabe zu automatisieren. Am 13. Mai 2012 brachte er einen umfassenden Lösungsansatz heraus, der vorsieht, dass in <meta>-Tags eine Art Template festgelegt wird und mit (noch zu spezifizierenden) HTML-Variablen diese Template-Variablen in den Ressourcenlisten eingesetzt werden können. Auch wenn ich persönlich dem konkreten Vorschlag nicht ganz zustimme: ein Templateverhalten in <meta>-Tags festzulegen wäre eine Möglichkeit mit Variablen schnell und einfach HTML zu schreiben. Das wäre nicht nur für das <picture>-Element sehr praktisch sondern auch für viele andere Anwendungen. Willcox‘ Ansatz sieht im HTML folgendermaßen aus:

<head>
    <meta name="case" data="breakpoint1" media="min-width:350px" />
    <meta name="case" data="breakpoint2" media="min-width:1000px" />
</head>
<body>
    <img src="/content/images/{case}/photo.jpg" alt="" />
</body>

Im CSS ist dazu noch folgendes Format vorgesehen: Statt

@media only screen and (min-width: 700px) { ... }

soll nun

@media (case: breakpoint1) { ... }

geschrieben werden können.

WHATWG Vorschlag: responsive images via srclist-Attribut

Zum Streitpunkt wurde das Thema responsive images, als die WHATWG am 15. Mai einen ganz eigenen Entwurf für responsive images online stellte. Das kam sehr überraschend für die meisten – auch für mich, zumal man selbst auf den WHATWG-Mailinglisten vorab nicht allzuviel darüber lesen konnte. Ich war ehrlich gesagt etwas vor den Kopf geschlagen, als ich von diesem Entwurf hörte; hauptsächlich, weil die Syntax, die der Vorschlag verwendet, extrem unübersichtlich und HTML-untypisch ist:

<img src="face-600-200@1.jpeg" alt=""
     set="face-600-200@1.jpeg 600w 200h 1x,
          face-600-200@2.jpeg 600w 200h 2x,
          face-icon.png       200w 200h">
Als Kurzsyntax:
<img src="logo.png" alt="SampleCorp" set="logo-HD.png 2x">

Die Angabe 600w 200h ist nicht, wie man vielleicht vermuten würde, eine Größenangabe des Bildes (so war es ja im <img>-Tag immer gewesen), sondern die Angabe der Viewportgröße. Das heißt, hier wird eine Art Mini-Media-Query angegeben. Für mich birgt allein das schon ein extrem großes Fehlerpotential für den normalen Webentwickler. Zudem gibt es eine auflösungbasierte Angabe @1x oder @2x, mit der die Auflösungen notiert werden. Bietet man @1x also mit 96ppi an, muss man @2x automatisch mit 192ppi bereitstellen. Auch hier entsteht eine weitere Fehlerquelle. Und es gilt am Ende immer noch das Prinzip: der Nutzer muss ausbaden, was der Entwickler falsch gemacht hat. Wird diese Syntax also genauso eingeführt muss man befürchten, dass Entwickler Fehler generieren, die dazu führen, dass ein User auf eine Smartphoneseite mit einem LowRes-Gerät geht und ein HighRes-Bild ausgeliefert bekommt oder ähnliche Szenarios. Das wäre fatal und sollte wenn irgendwie möglich vermieden werden.

Das Resultat dieser Aktion der WHATWG war vorhersehbar: die Web-Community stand innerhalb von wenigen Stunden Kopf, wobei das vielleicht größte Problem an dieser Geschichte die Kommunikationsweise der WHATWG war. Ich versuche nun mein Bestes, die hunderten von E-Mails, Blogposts und dazugehörigen Kommentare zu sortieren und einzuordnen.

Das Kommunikationsproblem, was passierte und wie es dazu kam

Mehrfach hatte ich mit einigen Community-Membern den Versuch unternommen Browser-Hersteller mit in die Community-Group einzubinden. Der Erfolg war mäßig, denn außer einem Opera-Mitarbeiter meldete sich überhaupt niemand zu Wort, weder von Apple (Safari) noch von Google (Chrome), Mozilla (Firefox) oder Microsoft (Internet Explorer). Und nun, nachdem die Community Group mehrere Monate verschiedene Lösungswege durchdacht und sogar praktisch getestet hatte, wird kommentarlos ein komplett eigener Entwurf angenommen! Dieser kam übrigens von Apple – Apple hatte bereits zur iPad3-Einführung eine etwas eigenwillige CSS4 Responsive Images-Lösung eingeführt.

Leider scheint man in der WHATWG nicht einmal von der Existenz der Community Group etwas mitbekommen zu haben. Das jedoch kann ich mir schwerlich vorstellen, hatten wir nicht nur WHATWG-Mitglieder direkt angesprochen, sondern auch diverse Fachblogs und -portale, die darüber berichteten. Nebenher tauchte der Link zur Gruppe auch in den WHATWG-Mailinglisten auf. Dennoch wurde der Spezifikationsentwurf veröffentlicht und zunächst einmal gar nicht auf andere Vorschläge eingegangen. Das führte unweigerlich auch zu verbalen Auseinandersetzungen, die sicher vermeidbar gewesen wären.

Am 16. Mai hat sich zumindest Tab Atkins (Google) zu Wort gemeldet und versuchte die Wogen zu glätten. Seltsamerweise hat jedoch kaum jemand auf die Argumente, die gegen diese Sourcelist-Version der WHATWG sprechen, geantwortet. Ich denke, hier wird das letzte Wort auch nicht gesprochen sein, es wird viele lange Diskussionen geben, wie es sie bereits vor knapp einem Monat zur Vendor-Prefix-Debatte gegeben hat. Nicht zuletzt hat das W3C angekündigt, die Problematik bei ihrem nächsten Treffen (wöchentlich) anszusprechen und sich ihrer anzunehmen.

Mein Zwischenfazit

Ich persönlich hoffe, dass es in Zukunft eine sinnvoll strukturierte, für jeden Entwickler anwendbare Syntax für responsive Bilder geben wird und nicht einmal mehr sich die Browserhersteller mit einer schnell umzusetzenden Lösung auf Kosten der Entwickler durchsetzen. Das bedeutet aber auch, dass die Lösung noch ein wenig mehr Zeit einnehmen wird, bevor sie richtig eingesetzt werden kann. Doch gut Ding will bekanntlich Weile haben.

Über den Autor

Portraitfoto von Anselm HannemannAnselm Hannemann ist selbständiger Webentwickler, Digital Publishing-Experte und Vorkämpfer in Sachen Webstandards für Responsive Images.

anselm-hannemann.com/blog/
@anselmhannemann