HTML5
Stand: 29.11.2022
Die fünfte Ausführung der Hypertext Markup Language ist HTML5, eine Computersprache zur Auszeichnung und Vernetzung von Texten und anderen Inhalten.
Canvas
Animationen
Anders als SVG- oder SMIL-Animationen sind Canvas-Animationen reine Handarbeit. Ingredienzen dafür sind eine Funktion zum Zeichnen plus ein Timer, der diese in regelmäßigen Abständen aufruft. Für den Zeitgeber stellt JavaScript window.setInterval()
zur Verfügung, der Rest ist dem Einfallsreichtum des Canvas-Programmierers überlassen.
Animation farbiger Kugeln
Unsere Animations-Premiere besteht aus Kugeln in verschiedenen Farben, die an zufälligen Positionen der Canvas-Fläche erscheinen, langsam verblassen und währenddessen durch neue Kugeln überdeckt werden. Das Tempo der Animation soll mit circa 60 Schlägen pro Minute dem Ruhepuls eines Erwachsenen entsprechen, und als Zusatz-Feature soll bei jedem Klick auf den Canvas die Animation gestoppt beziehungsweise neu gestartet werden.
Knapp 50 Zeilen JavaScript-Code reichen dafür aus, doch bevor wir uns näher mit der Analyse des Cods beschäftigen, sehen wir uns gleich einen statischen Screenshot des Ergebnisses an.
Nachdem canvas, context und ein paar weitere Variablen vordefiniert sind, beginnt in der Funktion drawCircles()
die eigentliche Arbeit. Ein semitransparentes, weißes Rechteck verblasst bestehenden Inhalt aus früheren drawCircles()
-Aufrufen, bevor die for
-Schleife für das Zeichnen neuer Kugeln sorgt. Die Position jeder Kugel sowie deren Radius wird mithilfe von Math.random()
wieder zufällig berechnet, wodurch das Zentrum in jedem Fall innerhalb der Canvas-Fläche zu liegen kommt und der Radius ein Zehntel der Canvas-Breite nicht übersteigt.
Den Kugeleffekt erzielen wir durch eine radiale Gradiente, deren Geometrie aus einem Lichtpunkt im oberen rechten Bereich und dem Gesamtkreis besteht. Die Wahl des Inkrements der for
-Schleife spiegelt den Wunsch nach Farben im HSL-Farbraum für die colorStops
der Gradiente wider. Bei jedem Durchlauf erhöht sich der Farbwinkel um 15° und bedingt damit den Farbwechsel von Rot über Grün und Blau zurück nach Rot.
Über die Helligkeit lassen sich dann jeweils zwei zusammenpassende Farben ableiten:
Die erste repräsentiert den Lichtpunkt, die zweite die dunklere Farbe am Kugelrand. Der dritte Aufruf von addColorStop()
bewirkt, dass direkt am Kugelrand zu transparentem Schwarz ausgeblendet wird. Insgesamt werden auf diese Weise 24 Kreise erzeugt, deren Farbpaare zum besseren Verständnis in Abbildung 2 dargestellt sind.
Daran anschließend wird die Kugel schließlich als Kreis mit dem definierten Farbverlauf gezeichnet. Das Einbetten in context.save()
und context.restore()
stellt sicher, dass sich die temporäre Verschiebung mit translate()
nicht auf nachfolgende Kreise überträgt. Damit ist die Funktion drawCircles()
komplett, und wir können ein erstes Set an Kugeln zeichnen und uns dann dem Timer widmen.
Knapp fünfzehn Zeilen reichen aus, um das Starten und Stoppen der Animation über einen onclick
-EventListener zu implementieren. Beim ersten Klick auf den Canvas starten wir die Animation mit window.setInterval()
und speichern die eindeutige Intervall-ID in der Variablen running. Da Zeitangaben bei window.setInterval()
in Millisekunden erfolgen, müssen wir natürlich die Schläge pro Minute in der Variablen pulse entsprechend umwandeln.
Läuft die Animation, ist running beim nächsten Klick mit der eindeutigen Intervall-ID belegt, und wir können sie über window.clearInterval(running)
unterbrechen. Setzen wir running dann wieder auf null, signalisiert der nächste Klick auf den Canvas: Keine Animation läuft. In diesem Fall starten wir wieder, und das Spiel beginnt von Neuem.
Video abspielen mit drawImage()
Anstelle des statischen Ausschnitts von El Capitan platzieren wir nun in der rechten oberen Ecke ein dynamisches Video mit einem 360°-Panoramaschwenk, der vom Taft Point aus gefilmt wurde. Zusätzlich kopieren wir während des Abspielens 10 verkleinerte Schnappschüsse des laufenden Videos als Leiste in den unteren Bereich des Canvas. Nach Beendigung des Videos ergibt sich das Erscheinungsbild aus Abbildung 4.
Im Gegensatz zu Bildern, die bisher immer über die JavaScript-Methode new Image()
ihren Weg in den Canvas gefunden haben, bauen wir den Schwenk als video
-Element direkt in der HTML-Seite ein. Als Zusatzattribute benötigen wir preload
zum Vorladen, oncanplay
als Event-Listener, der uns den Zeitpunkt liefert, zu dem wir die Postkarte layouten und das Starten und Stoppen vorbereiten können, sowie eine style
-Anweisung zum Verstecken des eingebetteten Originalvideos. Dieses benutzen wir nur dazu, um während des Abspielens in kurzen Intervallen den aktuellen Video-Frame auf den Canvas zu kopieren. Der alternative Text für Browser ohne Videounterstützung weist noch kurz auf den Inhalt des Videos hin.
Um sicherzustellen, dass die Funktion init(event)
beim Verweis im oncanplay
-Attribut auch tatsächlich existiert, setzen wir das script
-Element vor unser video
-Element. Der schematische Aufbau dieser zentralen Funktion, die sowohl das Layout als auch die Funktionalität der Videopostkarte implementiert, ergibt sich damit folgendermaßen:
Die Referenz zum Video-Objekt des video
-Elements finden wir in evt.target
und speichern sie in der Variablen video. Ein neues Hintergrundbild erzeugen wir, wie schon so oft, über new Image()
, und sobald das Bild vollständig geladen ist, geht es weiter ans Zeichnen von Hintergrund und Titel. Die Schritte bis hierher müssen wir wohl nicht mehr näher erklären, das Zeichnen des ersten Frames vielleicht schon.
Zuerst positionieren wir durch setTransform()
das Koordinatensystem in der rechten oberen Ecke und zeichnen dann über drawImage()
den ersten Frame mit Randlinie. Diesen Vorgang werden wir später beim Abspielen ständig wiederholen, wobei entscheidend ist, dass das HTMLVideoElement video
der drawImage()
-Methode immer das Bild des aktuellen Frames bereitstellt.
Das Stoppen, Starten und danach das Kopieren der aktuellen Frames des im Hintergrund laufenden Originalvideos auf den Canvas sowie das Erzeugen von verkleinerten Bildausschnitten implementieren wir beim Klicken auf den Canvas in der Funktion canvas.onclick()
:
Wie im ersten Animationsbeispiel enthält die Variable running die eindeutige Intervall-ID von window.setInterval()
und erlaubt die Steuerung der Animation. Ist running belegt, stoppen wir das versteckte Video mit video.pause()
, beenden durch Entfernen des Intervalls das Kopieren von Frames und setzen running wieder auf null.
Im Gegenzug starten wir beim ersten oder nächsten Klick das Video mit video.play()
und kopieren in der Callback-Funktion des Intervalls alle 35 Millisekunden den aktuellen Video-Frame auf den Canvas. Das Ganze setzen wir so lange fort, bis das Video zu Ende ist oder ein weiterer Klick auf den Canvas erfolgt. Bei der Überprüfung, ob die aktuelle Abspielposition noch kleiner als die Gesamtdauer des Videos ist, helfen uns die beiden Attribute video.currentTime
und video.duration
des Video-Objekts in der Variablen video.
Das Zeichnen des kopierten Videos rechts oben geschieht analog zum Zeichnen des ersten Frames. Für die Leiste mit verkleinerten Schnappschüssen ermitteln wir hingegen aus der Gesamtlänge des Videos und der gewünschten Anzahl an Ausschnitten jenes Zeitintervall gap
, nach dem wir den Anfasspunkt x1 mit einer kleinen Lücke tx weiter nach rechts verschieben müssen. Solange x1 denselben Wert besitzt, läuft die Animation auch beim verkleinerten Ausschnitt mit. Wird x1 nach rechts verschoben, bleibt der letzte Frame statisch zurück und die Animation läuft an der neuen Stelle weiter. Nach zirka 40 Sekunden Laufzeit ist das Video beendet, zehn verkleinerte Ausschnitte sind gezeichnet, und wir können die Sequenz durch einen Klick auf den Canvas neu starten.
Bilder einbetten
Zum Einbetten von Bildern stellt Canvas die Methode drawImage()
zur Verfügung, die mit drei unterschiedlichen Parametersätzen aufgerufen werden kann.
In allen drei Fällen benötigen wir im ersten Parameter ein image
-, canvas
– oder video
-Element, das dynamisch über JavaScript oder statisch im HTML-Code eingebunden sein kann. Animierte Bilder oder Videos werden dabei allerdings nicht bewegt dargestellt, sondern statisch durch ihren ersten Frame beziehungsweise einen Poster-Frame, sofern dieser vorhanden ist.
Alle weiteren Argumente der drawImage()
Methode beeinflussen Position, Größe oder Ausschnitt des Quellrasters im Ziel-Canvas. Abbildung 1 liefert die grafische Interpretation der möglichen Positionsparameter, wobei die Präfixe s für source
(Quelle) und d für destination (Ziel) stehen.
Vergleichen wir nun die einzelnen drawImage()
-Methoden anhand von drei einfachen Beispielen. Das gemeinsame Setup besteht aus einem 1200 x 800 Pixel großen Bild, das dynamisch als JavaScript-Objekt erzeugt wird.
Neben Pixelangaben, die uns in den Beispielen begegnen werden, zeigt Abbildung 2 die imposante, 1000 Meter hohe Felswand von El Capitan im Yosemite Nationalpark, vom Taft Point aus fotografiert. Dieses Bild wird nun onload
in einer der drei Arten auf den 600 x 400 Pixel großen Ziel-Canvas gezeichnet. Die erste und einfachste Möglichkeit bestimmt über dx/dy die linke obere Ecke des Bildes im Ziel-Canvas. In unserem Fall ist dies die Position 0/0.
Breite sowie Höhe werden dabei direkt aus dem Originalbild übernommen, und da unser Bild größer als der Ziel-Canvas ist, erscheint wenig überraschend auch nur das linke obere Viertel mit dem Taft Point auf der Canvas-Fläche.
Wollen wir den gesamten Bildausschnitt im Canvas darstellen, müssen wir zusätzlich die gewünschte Breite und Höhe in den Argumenten dw/dh angeben. Die Skalierung des Bildes auf 600 x 400 Pixel übernimmt dann der Browser für uns.
Die dritte Variante von drawImage()
bietet – im Gegensatz zu den beiden bisherigen, die auch mit CSS realisierbar gewesen wären – ganz neue Möglichkeiten, mit Bildern zu arbeiten. Beliebige Ausschnitte des Quellbildes (sx, sy, sw, sh) können jetzt in definierte Bereiche des Ziel-Canvas (dx, dy, dw, dh) kopiert werden. Der Montage von Bildern steht damit nichts mehr im Wege.
Der erste drawImage()
-Aufruf liefert wiederum das linke obere Viertel mit dem Taft Point, der zweite extrahiert den Bereich von El Capitan und zeichnet ihn als Icon in die rechte obere Ecke. Beschriftungen mit Schatten vervollständigen das rudimentäre Layout unserer Postkarte.
Wer lieber El Capitan im Vordergrund und den Taft Point als Briefmarke rechts oben sehen will, der muss nur die drawImage()
-Aufrufe leicht modifizieren – in unserem Beispiel passiert dies, wenn man auf den Canvas klickt.
Neben Vektorgrafiken (Pfaden, Linien usw.) unterstützt die Canvas-API auch Bitmap-Bilder. Die Methode drawImage()
kopiert die Pixel einer Bildquelle (oder eines rechteckigen Ausschnitts der Bildquelle) auf das Canvas und skaliert und rotiert dabei die Pixel des Bilds nach Bedarf.
drawImage()
kann mit drei, fünf oder neun Argumenten aufgerufen werden. Bei allen Aufrufformen ist das erste Argument die Bildquelle, aus der die Pixel kopiert werden sollen. Dieses Bildargument ist häufig ein <img>
-Element oder ein Bild, das im Hintergrund mit dem Image()
-Konstruktor erstellt wurde, kann aber auch ein anderes <canvas>
-Element oder sogar ein <video>
-Element sein. Geben Sie ein <img>
– oder <video>
-Tag an, dessen Daten noch geladen werden, tut der drawImage()
-Aufruf nichts.
Beim drawImage()
-Aufruf mit drei Argumenten geben das zweite und das dritte Argument die x– und y-Koordinaten an, an denen die linke obere Ecke des Bilds gezeichnet werden soll. Bei dieser Aufrufform wird die gesamte Bildquelle auf das Canvas kopiert. Die x– und y-Koordinaten beziehen sich auf das aktuelle Koordinatensyste und das Bild wird entsprechend skaliert und gedreht.
Der drawImage()
-Aufruf mit fünf Argumenten fügt den gerade beschriebenen x– und y-Argumenten die Argumente width
und height
hinzu. Diese vier Argumente definieren das Zielrechteck im Canvas. Die linke obere Ecke befindet sich bei (x,y) und die rechte untere bei (x+width
, y+height
). Auch bei dieser Form wird die gesamte Bildquelle kopiert. Das Zielrechteck wird im aktuellen Koordinatensystem gemessen. Bei dieser Form des Methodenaufrufs wird die Bildquelle so skaliert, dass sie in das Zielrechteck passt, auch wenn keine Skalierungstransformation definiert wurde.
Der drawImage()
-Aufruf mit neun Argumenten gibt sowohl ein Quellrechteck als auch ein Zielrechteck an und kopiert nur die Pixel im Quellrechteck. Die Argumente zwei bis fünf spezifizieren das Quellrechteck und werden in CSS-Pixeln angegeben. Ist die Bildquelle ein anderes Canvas, nutzt das Quellrechteck das Standardkoordinatensystem für jenes Canvas und ignoriert sämtliche angegebenen Transformationen. Die Argumente sechs bis neun geben das Zielrechteck an, in das das Bild gezeichnet wird, und beziehen sich auf das aktuelle Koordinatensystem des Canvas, nicht auf das Standardkoordinatensystem.
Beispiel 1 ist eine einfache Demonstration von drawImage()
. Es nutzt den Aufruf mit neun Argumenten, um Pixel aus einem Teil des Canvas zu kopieren und sie dann, vergrößert und rotiert, wieder in das Canvas zu zeichnen. Wie Sie in Abbildung 1 sehen, wurde das Bild so stark vergrößert, dass es pixelig ist. Sie können sogar die durchscheinenden Pixel sehen, mit denen die Ecken der Linie geglättet wurde.
Beispiel 1: drawImage()
verwenden
Wir können nicht nur Bilder in ein Canvas zeichnen, wir können auch den Inhalt eines Canvas als Bild herausziehen. Dazu dient die Methode toDataURL()
. Im Unterschied zu allen anderen hier beschriebenen Methoden ist toDataURL()
eine Methode des Canvas-Objekts selbst, nicht des CanvasRenderingContext2D-Objekts. Üblicherweise rufen Sie toDataURL()
ohne Argumente auf und erhalten dann das Canvas als PNG-Bild, das über eine data:-URL
als String kodiert ist. Die gelieferte URL kann mit einem <img>
-Tag verwendet werden und ermöglicht Ihnen, folgendermaßen einen statischen Schnappschuss des Canvas zu erstellen:
Von Browsern wird verlangt, dass sie das PNG-Format unterstützen. Einige Implementierungen unterstützen eventuell auch noch anderer Formate. Den gewünschten Mime-Typ können Sie über das optionale erste Argument von toDataURL()
angeben.
Es gibt allerdings eine wichtige Sicherheitseinschränkung, der Sie sich bewusst sein sollten, wenn Sie toDataURL()
nutzen. Damit es nicht zu Cross-Origin-Informationslöchern kommt, funktioniert toDataURL()
bei <canvas>
-Tags nicht, deren Herkunft nicht rein ist. Das ist der Fall, wenn in ein Canvas ein Bild gezeichnet wurde (entweder direkt mit drawImage()
oder indirekt über ein CanvasPattern), das von einer anderen Quelle stammt als das Dokument, das das Canvas enthält.
Canvas – ein erstes Beispiel
Eines der interessantesten und zugleich der ältesten neuen HTML5-Elemente ist Canvas. Bereits im Juli 2004, also nur einen Monat nach Gründung der WHATWG, präsentierte David Hyatt von Apple eine proprietäre Erweiterung für HTML namens Canvas und sorgte damit für Aufregung in der noch jungen HTML5-Bewegung. The real solution is to bring these proposals to the table
war Ian Hicksons erste Reaktion, und nach kurzer Diskussion reichte Apple seinen Vorschlag bei der WHATWG zur Begutachtung ein. Der Weg für die Aufnahme von Canvas in die HTML5-Spezifikation war damit geebnet, ein erster Draft erschien im August 2004.
Bei Canvas (übersetzt Leinwand) handelt es sich vereinfacht gesagt um ein programmierbares Bild,
auf das mithilfe einer Javascript-API gezeichnet werden kann. Dazu benötigen wir neben einem canvas
-Element als Leinwand auch ein script
-Element, in dem die Zeichenbefehle Platz finden. Beginnen wir mit dem canvas
-Element:
Die Attribute width
und height
bestimmen die Dimension des canvas
-Elements in Pixel und reservieren entsprechend viel Platz auf der HTML-Seite. Fehlt eines oder fehlen gar beide Attribute, kommen Vorgabewerte zum Tragen: 300 Pixel für die Breite und 150 Pixel für die Höhe. Der Bereich zwischen Start- und End-Tag ist für alternativen Content reserviert, der zur Anzeige gelangt, wenn ein Browser Canvas nicht unterstützt. Ähnlich wie das alt
-Tag bei Bildern sollte dieser alternative Content den Inhalt der Canvas-Applikation beschreiben oder einen entsprechenden Screenshot zeigen. Formulierungen wie Ihr Browser unterstützt Canvas nicht ohne Zusatzinformationen sind wenig hilfreich und deshalb zu vermeiden.
Damit ist unsere Leinwand fertig, und wir können im nächsten Schritt die Zeichenbefehle in einem script
-Element hinzufügen. Wenige Zeilen Code reichen aus, um unser erstes, zugegebenermaßen recht triviales Canvas-Beispiel zu realisieren.
Auch wenn wir noch nichts über die Syntax der Canvas-Zeichenbefehle wissen, ist bei näherer Betrachtung des Codes das Resultat in Abbildung 1 wenig überraschend. Wir sehen ein rotes und ein hellgelbes Rechteck mit 50 % Opazität, wodurch im Überlappungsbereich ein oranger Farbton entsteht.
Bevor wir auf die Leinwand zeichnen können, benötigen wir eine Referenz zu derselben.
Die erste Zeile im Skript tut genau dies und speichert in der Variablen canvas mithilfe der W3C CSS Selectors API-Methode document.querySelector()
eine Referenz auf das erste gefundene canvas
-Element im Dokument:
Neben den Attributen canvas.width
und canvas.height
besitzt dieses, auch HTMLCanvasElement genannte Objekt die Methode getContext()
. Sie erlaubt uns, auf das Herzstück von Canvas zuzugreifen, den CanvasRenderingContext2D, indem wir 2d als Kontext-Parameter übergeben:
Damit ist der Zeichenkontext definiert, und wir können mit dem Zeichnen der beiden Rechtecke beginnen. Ohne auf Details des Attributs fillStyle
oder der Methode fillRect()
einzugehen, bietet sich zweimal derselbe Ablauf: Füllung definieren und dann das Rechteck hinzufügen:
Wenden wir uns aber wieder dem 2D-Kontext zu, denn die Möglichkeiten, die sich hinter dem CanvasRenderingContext2D-Interface verstecken, sind mannigfaltig und durchaus geeignet, um anspruchsvolle Applikationen zu realisieren. Abbildung 2 zeigt ein einfaches Säulendiagramm, das uns bei der Erklärung der ersten drei Features des Zeichenkontextes begleiten wird: Rechtecke, Farben und Schatten.
Rechtecke
Canvas verfügt über vier Methoden, um Rechtecke zu erstellen. Mit dreien davon wollen wir uns jetzt beschäftigen, die vierte wird uns später beim Thema Pfade begegnen.
Die Namen dieser Methoden sind selbsterklärend. So erzeugt fillRect()
ein gefülltes Rechteck, strokeRect()
ein Rechteck mit Randlinie ohne Füllung und clearRect()
ein Rechteck, das bestehenden Inhalt wie ein Radiergummi löscht. Die Dimension des Rechtecks bestimmen vier numerische Parameter: Startpunkt x/y, Breite w und Höhe h.
In Canvas liegt der Koordinatenursprungspunkt übrigens links oben,
wodurch x-Werte nach rechts und y-Werte nach unten größer werden.
Analog zum ersten Beispiel definieren wir beim Balkendiagramm zuerst eine Referenz auf das canvas
-Element und dann den Zeichenkontext. Für die Hauptarbeit, das Erstellen der horizontalen Balken, ist die Funktion drawBars()
verantwortlich, der wir zugleich die gewünschte Anzahl zu zeichnender Balken übergeben.
Ein Aufruf dieser Funktion durch drawBars(10)
löscht somit über clearRect()
allenfalls bestehenden Inhalt und zeichnet danach in der for
-Schleife über fillRect()
und strokeRect()
die zehn gefüllten Balken mit Randlinie. Die Breite w der Balken variiert von 0 Pixel bis zur Gesamtbreite des canvas
-Elements und wird mithilfe der JavaScript-Funktion Math.random()
zufällig gesetzt. Math.random()
liefert eine Zahl von 0.0 bis 1.0 und ist daher bestens geeignet, um Zufallswerte für Breite, Höhe, aber auch für die Position in Abhängigkeit von der Canvas-Dimension zu ermitteln. Die Multiplikation mit dem entsprechenden Attribut-Wert genügt.
Die gleichabständige, horizontale Aufteilung der Balken folgt der Canvashöhe.
Die Abstände zwischen den Balken resultieren daraus, dass die errechnete, maximale Balkenhöhe h mit dem Faktor 0.8 multipliziert wird.
Die Breite und Höhe des Canvas lassen sich, wie schon im ersten Beispiel erwähnt, bequem über die Attribute canvas.width
und canvas.height
ablesen. Ebenso einfach können wir auch vom Zeichenkontext über dessen Attribut context.canvas
auf das HTMLCanvasElement zugreifen, das wir auch gleich dazu verwenden, um bei jedem Klick auf den Canvas neue Balken zu generieren. Drei Zeilen Code im Anschluss an den drawBars(10)
-Aufruf reichen dazu aus.
Nachdem nun geklärt ist, wie die zehn Balken gezeichnet werden, stellt sich die nächste Frage: Wie kommt die hellgraue Farbe der Balken mit schwarzer Randlinie zustande? Ein Blick auf die Möglichkeiten, in Canvas Farben zu vergeben, liefert die Antwort.
Farben und Schatten
Die Attribute fillStyle
und strokeStyle
dienen dazu, Farben für Füllungen und Linien festzulegen. Farbangaben folgen dabei den Regeln für CSS-Farbwerte und dürfen aus diesem Grund in einer Reihe verschiedener Formatierungen angegeben werden. Tabelle zeigt die Möglichkeiten am Beispiel der Farbe Rot.
Methode | Farbwert |
---|---|
Hexadezimal | #FF0000 |
Hexadezimal (kurz) | #F00 |
RGB | rgb(255,0,0) |
RGB (Prozent) | rgb(100%,0%,0%) |
RGBA | rgba(255,0,0,1.0) |
RGBA (Prozent) | rgba(100%,0%,0%,1.0) |
HSL | hsl(0,100%,50%) |
HSLA | hsla(0,100%,50%,1.0) |
SVG-Farbnamen | red |
Um die aktuell gültige Füll- und Strichfarbe in Canvas festzulegen, genügt es, entsprechende Farbwerte als Zeichenketten für fillStyle
und strokeStyle
anzugeben. Im Balkendiagramm-Beispiel sind dies die benannte SVG-Farbe silver
als Füllung sowie eine halbtransparente schwarze Randlinie in RGBA-Notation. Da alle Balken gleich aussehen sollen, definieren wir die dazugehörigen Stile vor der drawBars()
-Funktion.
Gültige Werte für die Opazität reichen von 0.0 (transparent) bis 1.0 (opak) und können als vierte Komponente sowohl im RGB-Farbraum als auch im HSL-Farbraum verwendet werden. Letzterer definiert Farben nicht über ihre Rot-, Grün- und Blauanteile, sondern durch eine Kombination aus Farbton, Sättigung und Helligkeit.
Info
Bei genauerem Hinsehen erkennt man Schatten hinter den Balken, die durch vier weitere Attribute des Zeichen-Kontextes entstehen:
Die ersten beiden Zeilen legen über shadowOffsetX
und shadowOffsetY
den Schattenoffset fest, shadowColor
bestimmt dessen Farbe und Transparenz, und shadowBlur
bewirkt schließlich das Weichzeichnen des Schattens, wobei als Faustregel Folgendes gilt: Je höher der Wert von shadowBlur
ist, desto stärker ist der Weichzeichnungseffekt.
Bevor wir uns nun im nächsten Abschnitt mit dem Thema Farbverläufe beschäftigen, bleibt noch zu klären, wie der gestrichelte Rahmen im Balkendiagramm-Beispiel und allen folgenden Grafiken zustande kommt. Die Antwort ist einfach: durch CSS. Jedes canvas
-Element darf natürlich auch mit CSS formatiert werden. Abstände, Position oder z-index
lassen sich ebenso festlegen wie Hintergrundfarbe oder Rahmen. Im unserem Beispiel sorgt folgendes Stilattribut für den gestrichelten Rahmen:
Farbverläufe
Als Ergänzung zu Vollfarben für Füllungen und Linien hält Canvas zwei Arten von Farbverläufen bereit: lineare
und radiale
Gradienten. Am Beispiel eines einfachen Farbverlaufes von Rot über Gelb und Orange nach Violett lässt sich das Grundprinzip beim Erstellen von Gradienten in Canvas leicht demonstrieren.
Zuerst erzeugt context.createLinearGradient
(x0, y0, x1, y1) ein CanvasGradient-Objekt und legt dabei über die Parameter x0, y0, x1, y1 die Richtung des Farbverlaufs fest. Da wir in einem weiteren Schritt noch Farboffsets bestimmen müssen, speichern wir dieses Objekt in der Variablen linGrad.
Die Methode addColorStop(offset, color)
des CanvasGradient-Objekts dient in einem zweiten Schritt zum Wählen der gewünschten Farben und deren Offsets auf unserer imaginären Farbverlaufslinie. Offset 0.0 steht für die Farbe am Punkt x0/y0 und Offset 1.0 für die Farbe am Endpunkt x1/y1. Alle dazwischen liegenden Farben werden ihrem Offset entsprechend aufgeteilt, und Übergänge zwischen den einzelnen Stopps werden vom Browser im RGBA-Farbraum interpoliert.
Farbangaben folgen wieder den Regeln für CSS-Farbwerte und sind im Beispiel zur besseren Lesbarkeit als SVG-Farbnamen ausgewiesen. Damit ist unsere lineare Gradiente fertig und kann über fillStyle
oder strokeStyle
zugewiesen werden.
Im Gegensatz zu linearen Farbverläufen liegen Start und Endpunkt bei radialen Gradienten nicht als Punkte, sondern Kreise vor, weshalb wir nun zur Definition der Gradiente die Methode context.createRadialGradient
(x0, y0, r0, x1, y1, r1) verwenden müssen.
Im linken Teil der Grafik erkennen wir Start- und Endkreis, in der Mitte die drei Farbstopps mit Offset-Werten und rechts das Endresultat: eine Kugel mit Lichteffekt. So ansprechend das Ergebnis ist, so einfach und übersichtlich ist auch der Quellcode:
Der Schatteneffekt am Kugelrand entsteht übrigens durch die beiden letzten Farbstopps, bei denen auf kürzestem Weg von Orange zu transparentem Schwarz interpoliert wird, wodurch der sichtbare Teil der Gradiente direkt am Außenkreis endet.
Canvas-Tutorial
Dieser Artikel erläutert, wie man mit JavaScript und dem HTML-<canvas>
-Tag Grafiken in Webseiten zeichnet. Die Möglichkeit, komplexe Grafiken dynamisch im Webbrowser zu generieren, statt sie von einem Server herunterzuladen, stellt eine Revolution dar:
- Der Code, der auf Clientseite zur Erstellung der Grafiken genutzt wird, ist normalerweise erheblich kleiner als die Bilder selbst und spart damit eine Menge Bandbreite.
- Dass Aufgaben vom Server auf den Client verlagert werden, reduziert die Last auf dem Server und kann die Ausgaben für Hardware um einiges mindern.
- Die clientseitige Grafikgenerierung fügt sich gut in die Architektur von Ajax-Anwendungen ein, in denen der Server die Daten stellt und sich der Client um die Darstellung dieser Daten kümmert.
- Der Client kann Grafiken schnell und dynamisch neu zeichnen. Das ermöglicht grafikintensive Anwendungen (wie Spiele und Simulationen), die schlicht nicht machbar sind, wenn jeder Frame einzeln von einem Server heruntergeladen werden muss.
- Das Schreiben von Grafikprogrammen ist ein Vergnügen, und das
<canvas>
-Tag lindert für den Webentwickler die Pein, die die Arbeit mit dem DOM mit sich bringen kann.
Das <canvas>
-Tag tritt nicht an sich in Erscheinung, sondern es erstellt eine Zeichenfläche im Dokument und bietet clientseitigem JavaScript eine mächtige API zum Zeichnen. Das <canvas>
-Tag wird von HTML5 standardisiert, treibt sich aber schon deutlich länger herum. Es wurde von Apple in Safari 1.3 eingeführt und wird von Firefox seit Version 1.5 und Opera seit Version 9 unterstützt. Außerdem wird es von allen Versionen von Chrome unterstützt. Vom Internet Explorer wird es erst ab Version 9 unterstützt, kann in IE 6, 7 und 8 aber hinreichend gut emuliert werden.
Canvas im IE
Stehen diese Zeilen am Anfang Ihrer Webseiten, funktionieren <canvas>
-Tag und elementare Canvas-Zeichenbefehle im IE. Radiale Verläufe und Clipping werden nicht unterstützt. Die Linienbreite wird nicht korrekt skaliert, wenn die x- und y-Dimensionen um unterschiedliche Beträge skaliert werden. Zusätzlich müssen Sie damit rechnen, beim IE weitere Rendering-Unterschiede zu sehen.
Große Teile der Canvas-Zeichen-API werden nicht im <canvas>
-Element selbst definiert, sondern auf einem „Zeichenkontext-Objekt“, das Sie sich mit der getContext()
-Methode des Canvas beschaffen. Rufen Sie getContext()
mit dem Argument 2d
auf, erhalten Sie ein CanvasRenderingContext2D-Objekt, über das Sie zweidimensionale Grafiken in das Canvas zeichnen können. Es ist wichtig, sich darüber bewusst zu sein, dass das Canvas-Element und sein Kontext-Objekt zwei sehr verschiedene Objekte sind. Da der Klassenname so lang ist, verweise ich auf das CanvasRenderingContext2D-Objekt nur selten mit diesem Namen, sondern nenne es einfach das „Kontext-Objekt“. Und wenn ich von der „Canvas-API“ spreche, meine ich damit in der Regel „die Methoden des CanvasRenderingContext2D-Objekts„. Da der lange Klassenname CanvasRenderingContext2D nicht gut auf diese schmalen Seiten passt, wird er außerdem im auf diese Einführung folgenden Referenzabschnitt mit CRC abgekürzt.
3-D-Grafiken in einem Canvas
Als dies geschrieben wurde, begannen Browserhersteller gerade damit, eine 3-D-Grafik-API für das <canvas>
-Tag zu implementieren. Die entsprechende API wird als WebGL bezeichnet und ist eine JavaScript-Schnittstelle zur OpenGL-Standard-API. Ein Kontext-Objekt für 3-D-Grafiken erhalten Sie, wenn Sie der getContext()
-Methode des Canvas den String webgl
übergeben. WebGL ist eine umfangreiche und komplizierte Low-Level-API, die in diesem Artikel nicht dokumentiert wird: Webentwickler werden wahrscheinlich eher auf WebGL aufgesetzte Hilfsbibliotheken nutzen als die WebGL-API selbst.
Ein einfaches Beispiel für die Canvas-API sehen Sie im folgenden Code, der ein rotes Rechteck und einen blauen Kreis in <canvas>
-Tags schreibt und eine Ausgabe wie die generiert, die Sie in Abbildung 1 sehen.
Die Canvas-API beschreibt komplexe Figuren als „Pfade“ von Linien und Kurven, die gezeichnet oder gefüllt werden können. Ein Pfad wird durch eine Folge von Methodenaufrufen definiert, wie beispielsweise die beginPath()
– und arc()
-Aufrufe aus dem vorangegangenen Code. Nachdem ein Pfad definiert ist, operieren andere Methoden wie fill()
auf diesem Pfad. Verschiedene Eigenschaften des Kontext-Objekts wie fillStyle
legen fest, wie diese Operationen ausgeführt werden. Die nächsten Unterabschnitte erläutern die folgenden Dinge:
- Wie man Pfade definiert und die Linie des Pfads zeichnet oder das Innere eines Pfads füllt.
- Wie man die Grafikattribute des Canvas-Kontext-Objekts setzt und abfragt und wie man den aktuellen Status dieser Attribute speichert und wiederherstellt.
- Canvas-Maße, das Standard-Canvas-Koordinatensystem und wie man dieses Koordinatensystem transformiert.
- Die verschiedenen Methoden zum Zeichnen von Kurven, die von der Canvas-API definiert werden.
- Einige spezielle Hilfsmethoden zum Zeichnen von Rechtecken.
- Wie man Farben angibt, mit Transparenz arbeitet, Farbverläufe zeichnet und Bildmuster wiederholt.
- Die Attribute, die die Strichbreite und das Erscheinungsbild der Linienendpunkte und -eckpunkte steuert.
- Wie man Text in ein
<canvas>
schreibt. - Wie man Grafiken so beschneidet, dass nichts außerhalb der von Ihnen angegebenen Region gezeichnet wird.
- Wie man Grafiken Schlagschatten hinzufügt.
- Wie man Bilder in ein Canvas zeichnet (und optional skaliert) und wie man den Inhalt eines Canvas herauszieht und als Bild speichert.
- Wie man den Compositing-Prozess steuert, über den neu gezeichnete (durchscheinende) Pixel mit den im Canvas vorhandenen Pixeln kombiniert werden.
- Wie man die rohen Rot-, Grün-, Blau- und Alphawerte (Transparenz) von Pixeln im Canvas abfragt und setzt.
- Wie man ermittelt, ob über etwas, das Sie auf das Canvas gezeichnet haben, ein Mausereignis eingetreten ist.
Dieses Kapitel endet mit einem praktischen Beispiel, das das <canvas>
-Tag nutzt, um kleine Inline-Diagramme zu zeichnen, die als Sparklines bezeichnet werden. Auf dieses Einführungskapitel folgt eine Referenz, die die Canvas-API in allen Details dokumentiert.
Viele der folgenden <canvas>
-Codebeispiele operieren auf einer Variablen mit dem Namen c. Diese Variable hält das CanvasRenderingContext2D-Objekt des Canvas fest. Der Code, der diese Variable initialisiert, wird üblicherweise jedoch nicht gezeigt. Diese Beispiele funktionieren nur, wenn Sie das HTML-Markup mit einem Canvas mit den entsprechenden width
– und height
-Attributen haben und dann Code wie folgenden ergänzen, um die Variable c zu initialisieren:
Alle nachfolgenden Figuren werden mit JavaScript-Code generiert, der in ein <canvas>
-Tag zeichnet, üblicherweise in ein großes Canvas, das nicht auf dem Bildschirm angezeigt wird, um druckfähige Grafiken mit hoher Auflösung zu erzeugen.
Clipping
Wenn Sie einen Pfad definiert haben, rufen Sie normalerweise stroke()
oder fill()
(oder beides) auf. Sie können aber auch die Methode clip()
aufrufen, um einen Clipping-Bereich oder Ausschnitt zu definieren. Haben Sie einen Ausschnitt definiert, wird nichts außerhalb des entsprechenden Bereichs gezeichnet. Abbildung 1 zeigt eine komplizierte Zeichnung, die mit Ausschnitten erzeugt wurde. Die Umrisse des vertikalen Streifens in der Mitte und des Texts unten an der Figur wurden ohne Ausschnitt gezeichnet. Ihr Inhalt wurde gefüllt, nachdem der dreieckige Ausschnitt definiert wurde.
Beispiel 1: Einen Clipping-Bereich definieren
Es ist wichtig, sich zu merken, dass beim Aufruf von clip()
der aktuelle Pfad selbst auf den aktuellen Ausschnitt beschnitten wird und dieser Pfad zum neuen Ausschnitt wird. Das bedeutet, dass die clip()
-Methode den Ausschnitt verkleinern, aber nie vergrößern kann. Es gibt keine Methode, mit der man den Ausschnitt zurücksetzen kann. Bevor Sie clip()
aufrufen, sollten Sie deswegen stets save()
aufrufen, damit Sie später mit restore()
den unbeschnittenen Bereich wiederherstellen können.
Dimensionen, Koordinaten
Die width
– und height
-Attribute des <canvas>
-Tags und die entsprechenden width
– und height
-Eigenschaften des Canvas-Objekts geben die Ausmaße des Canvas an. Das Standard-Canvas-Koordinatensystem hat den Ursprung (0,0) in der linken oberen Ecke des Canvas. Die x-Koordinate wächst, wenn Sie auf dem Bildschirm nach rechts gehen, die y-Koordinate, wenn Sie auf dem Bildschirm nach unten gehen. Punkte auf dem Canvas können mit Fließkommawerten angegeben werden, und diese werden nicht automatisch züIntegern aufgerundet – das Canvas nutzt Anti-Aliasing-Techniken, um partiell gefüllte Pixel züsimulieren.
Die Maße eines Canvas sind so grundlegend, dass sie nicht geändert werden können, ohne das Canvas vollständig zurückzusetzen. Das Setzen der width
– oder height
-Eigenschaft des Canvas (sogar ein Setzen auf den aktuellen Wert) leert das Canvas, löscht den aktuellen Pfad und setzt alle Grafikattribute (einschließlich der aktuellen Transformation und des Clippings) auf den ursprünglichen Zustand zurück.
Trotz der elementaren Bedeutung müssen die Maße des Canvas
nicht der tatsächlichen Größe des Canvas auf dem Bildschirm oder der Anzahl von Pixeln auf der Canvas-Zeichenfläche entsprechen. Die Canvas-Maße (und das Standardkoordinatensystem) werden in CSS-Pixeln gemessen. CSS-Pixel entsprechen üblicherweise gewöhnlichen Pixeln. Auf Bildschirmen mit hoher Auflösung ist es Implementierungen gestattet, mehrere Gerätepixel auf ein CSS-Pixel abzubilden. Das bedeutet, dass das Pixelrechteck, das das Canvas zeichnet, größer sein kann als die nominellen Maße des Canvas. Dessen müssen Sie sich bewusst sein, wenn Sie mit den Pixelmanipulationsmethoden des Canvas arbeiten, aber die Unterschiede zwischen virtuellen CSS-Pixeln und tatsächlichen Hardware-Pixeln wirkt sich ansonsten in keiner Weise auf den Canvas-Code aus, den Sie schreiben.
Standardmäßig wird ein <canvas>
-Tag auf dem Bildschirm in der Größe (in CSS-Pixeln) gezeichnet, die von den HTML-Attributen width
und height
vorgegeben wird. Wie jedes HTML-Element kann die Bildschirmgröße eines <canvas>
-Tags auch durch die CSS-Style-Attribute width
und height
angegeben werden. Wenn Sie eine Größe angeben, die sich von den tatsächlichen Maßen des Canvas unterscheidet, werden die Pixel auf dem Canvas automatisch so skaliert, dass sie den Bildschirmmaßen entsprechen, die von den CSS-Attributen vorgegeben werden. Die Bildschirmgröße auf dem Canvas wirkt sich nicht auf die Anzahl der CSS-Pixel oder Hardware-Pixel aus, die in der Canvas-Bitmap reserviert sind, und die Skalierung, die angewandt wird, erfolgt gemäß einer Skalierungsoperation für Bilder. Wenn sich die Bildschirmmaße erheblich von den tatsächlichen Ausmaßen des Canvas unterscheiden, führt das züverpixelten Grafiken. Aber das ist ein Problem, mit dem sich Grafiker befassen müssen, es wirkt sich nicht auf die Canvas-Programmierung aus.
Koordinaten-Systemtransformationen
Wie oben gesagt, befindet sich der Ursprung des Standardkoordinatensystems eines Canvas in der oberen linken Ecke, die x-Koordinaten verlaufen nach rechts, die y-Koordinaten nach unten. In diesem Standardsystem entsprechen die Koordinaten eines Punkts direkt einem CSS-Pixel (das dann unmittelbar auf ein oder mehrere Gerätepixel abgebildet wird). Bestimmte Canvas-Operationen und -Attribute (wie das Ermitteln der rohen Pixelwerte und das Setzen von Schattenverschiebungen) nutzen immer dieses Standardkoordinatensystem. Zusätzlich züdiesem Standardkoordinatensystem besitzt jedes Canvas eine „aktuelle Transformationsmatrix“ als Teil des Grafikzustands. Diese Matrix definiert das aktuelle Koordinatensystem des Canvas. Bei den meisten Canvas-Operationen werden die Punktkoordinaten, die Sie angeben, als Punkte im aktuellen Koordinatensystem betrachtet, nicht im Standardkoordinatensystem. Die aktuelle Transformationsmatrix wird genutzt, um die angegebenen Punkte in die äquivalenten Koordinaten im Standardkoordinatensystem umzuwandeln.
Über die Methode setTransform()
können Sie die Transformations-Matrix eines Canvas direkt setzen, aber Koordinaten-Systemtransformationen lassen sich in der Regel leichter als Folge von Translations-, Rotations- und Skalierungs-Operationen angeben. Abbildung 1 illustriert diese Operationen und ihre Auswirkungen auf das Canvas-Koordinatensystem. Das Programm, das diese Abbildung erzeugte, zeichnete sieben Mal die gleiche Gruppierung von Achsen. Das Einzige, was dabei geändert wurde, war die aktuelle Transformations-Matrix. Beachten Sie, dass sich die Transformation nicht nur auf die Linien, sondern auch auf den Text auswirkt.
Die translate()
-Methode verschiebt den Ursprung des Koordinatensystems nach links, rechts, oben oder unten. Die rotate()
-Methode rotiert die Achsen im Uhrzeigersinn um den angegebenen Winkel. (Die Canvas-API gibt Winkel immer in Radiant an. Grad wandeln Sie in Radiant um, indem Sie durch 180 teilen und mit Math.PI multiplizieren.) Die scale()
-Methode streckt oder kontrahiert Abstände auf der x– oder y-Achse.
Wird scale()
ein negativer Skalierungsfaktor angegeben, wird die entsprechende Achse über den Ursprung umgekehrt, als wäre sie in einem Spiegel reflektiert worden. Das wurde in der linken unteren Ecke von Abbildung 1 gemacht: Zunächst wurde der Ursprung mit translate()
in die linke untere Ecke des Canvas verschoben; dann wurde die y-Achse gespiegelt mit scale()
, sodass die Koordinaten nach oben hin ansteigen. Ein umgekehrtes Koordinatensystem wie dieses sollte Ihnen aus dem Geometrieunterricht bekannt sein und kann geeignet sein, wenn Sie Datenpunkte für Diagramme zeichnen müssen. Beachten Sie, dass das die Lesbarkeit des Texts erheblich verschlechtert!
Transformationen mathematisch verstehen
Mir fällt es am leichtesten, Transformationen geometrisch züverstehen und translate()
, rotate()
und scale()
als Transformation der Achsen des Koordinatensystems zübetrachten, wie es in Abbildung 1 illustriert wird. Man kann Transformationen auch algebraisch als Gleichungen betrachten, die die Koordinaten des Punkts (x,y) im transformierten Koordinatensystem wieder in die Koordinaten des gleichen Punkts (x‘,y‘) im vorangegangenen Koordinatensystem überführen.
Der Methodenaufruf c.translate(dx,dy)
kann mit folgenden Gleichungen beschrieben werden:
Die Gleichungen für Skalierungsoperationen sind ähnlich einfach. Der Aufruf c.scale(sx,sy)
kann folgendermaßen beschrieben werden:
Rotationen sind etwas komplizierter. Der Aufruf c.rotate(a)
wird durch folgende trigonometrische Gleichungen beschrieben:
Beachten Sie, dass die Reihenfolge der Transformationen wichtig ist. Angenommen, wir beginnen mit dem Standardkoordinatensystem eines Canvas und verschieben und skalieren es dann. Wollen wir den Punkt (x,y) im aktuellen Koordinatensystem wieder in den Punkt (x‘‚,y‘) im Standardkoordinatensystem überführen, müssen wir erst die Skalierungsgleichungen anwenden, um den Punkt auf einen Zwischenpunkt (x‘, y‘) im verschobenen, aber nicht skalierten Koordinatensystem züüberführen. Dann wenden wir die Translationsgleichungen an, um diesen Zwischenpunkt in (x‘,y‘) züüberführen. Das Ergebnis sieht so aus:
Hätten wir hingegen erst scale()
und dann translate()
aufgerufen, hätten die resultierenden Gleichungen anders ausgesehen:
Der entscheidende Punkt, den Sie sich bei der algebraischen Betrachtung von Transformationen merken müssen, ist, dass Sie sich rückwärts von der letzten Transformation zur ersten hocharbeiten müssen. Wenn Sie transformierte Achsen geometrisch betrachten, arbeiten Sie sich von der ersten zur letzten Transformation hoch.
Die vom Canvas unterstützten Transformationen werden als affine Transformationen bezeichnet. Affine Transformationen können den Abstand zwischen zwei Punkten und die Winkel zwischen Linien ändern, aber parallele Linien bleiben nach einer affinen Transformation parallel – mit einer affinen Transformation ist es beispielsweise nicht möglich, eine Fischaugenverzerrung anzugeben. Jede affine Transformation kann mit den sechs Parametern a bis f in den folgenden Gleichungen beschrieben werden:
Sie können beliebige Transformationen auf das aktuelle Koordinatensystem anwenden, indem Sie diese sechs Parameter an die Methode transform()
übergeben. Abbildung 1 illustriert zwei Typen von Transformationen – Scherungen und Drehungen um einen angegebenen Punkt – die Sie mit der transform()
-Methode folgendermaßen erreichen können:
Die Methode setTransform()
erwartet die gleichen Argumente wie transform()
, aber anstelle des aktuellen Koordinatensystems, das ignoriert wird, wird das Standardkoordinatensystem transformiert und das Ergebnis zum neuen aktuellen Koordinatensystem gemacht. Mit setTransform()
kann man das Canvas vorübergehend auf das Standardkoordinatensystem zurücksetzen:
Farben, Transparenz, Verläufe und Muster
Die Attribute strokeStyle
und fillStyle
legen fest, wie Striche gezogen und Flächen gefüllt werden. Am häufigsten werden diese Attribute genutzt, um blickdichte oder durchscheinende Farben anzugeben, aber Sie können sie auch auf CanvasPattern– oder CanvasGradient-Objekte setzen, um Strich oder Füllung mit einem sich wiederholenden Hintergrundbild oder mit einem linearen oder radialen Farbverlauf zu versehen. Zusätzlich können Sie die Eigenschaft globalAlpha
nutzen, um alles Gezeichnete durchscheinend zu machen.
Wollen Sie eine blickdichte Farbe angeben, können Sie einen der Farbnamen nutzen, die vom HTML4-Standard definiert werden, oder einen CSS-Farbstring:
Der Standardwert für strokeStyle
und fillStyle
ist #000000
: blickdichtes Schwarz.
Aktuelle Browser unterstützen CSS3-Farben und ermöglichen neben den gewöhnlichen hexadezimalen RGB-Farbangaben die Verwendung von RGB-, RGBA-, HSL- und HSLA-Farbräumen. Hier sind einige mögliche Farbstrings:
Der HSL-Farbraum definiert eine Farbe mit drei Zahlen, der Farbton (Hue
), Sättigung (Saturation
) und Helligkeit (Lightness
) angeben. Der Farbton ist ein Winkel in Grad auf einem Farbkreis. Der Farbton 0 ist Rot, 60 ist Gelb, 120 ist Grün, 180 ist Cyan, 240 ist Blau, 300 ist Magenta, und 360 ist wieder Rot. Die Sättigung beschreibt die Intensität der Farbe über einen Prozentwert. Farben mit einer Sättigung von 0% sind Grautöne. Die Helligkeit beschreibt, wie hell oder dunkel eine Farbe ist, und wird ebenfalls über einen Prozentwert angegeben. Eine HSL-Farbe mit 100% Helligkeit ist reines Weiß, und jede Farbe mit der Helligkeit 0% ist reines Schwarz. Der HSLA-Farbraum entspricht HSL, ergänzt aber einen Alphawert zwischen 0.0 (durchsichtig) und 1.0 (undurchsichtig).
Wenn Sie mit durchscheinenden Farben arbeiten, aber nicht explizit einen Alphakanal für jede Farbe angeben oder blickdichten Bildern oder Mustern Transparenz hinzufügen wollen, können Sie die Eigenschaft globalAlpha
nutzen. Der Alphawert jedes Pixels, das Sie zeichnen, wird mit globalAlpha
multipliziert. Der Standardwert ist 1 und fügt keine Transparenz hinzu. Setzen Sie globalAlpha
auf 0, ist alles, was Sie zeichnen, vollständig transparent. Es erscheint alsönichts auf dem Canvas. Setzen Sie diese Eigenschaft auf 0.5, werden Pixel, die sonst blickdicht wären, zu 50% blickdicht. Pixel, die zu 50% blickdicht wären, sind nun zu 25% blickdicht. Setzen Sie globalAlpha
auf einen Wert kleiner 1, sind alle Pixel durchscheinend. Gegebenenfalls werden Sie überlegen müssen, wie die Pixel mit den Pixeln kombiniert werden, die darübergezeichnet werden.
Aber statt einfarbig mit einer (gegebenenfalls durchscheinenden) Farbe zu zeichnen,
können Sie auch Farbverläufe und sich wiederholende Bilder nutzen, wenn Sie Pfade füllen und ziehen. Abbildung 1 zeigt ein Rechteck, das mit breiten Linien und einem Strichmuster über einer Füllung mit einem linearen Verlauf und unter einer Füllung mit einem durchscheinenden radialen Verlauf gezeichnet wurde. Die Codefragmente unten zeigen, wie die Muster und Verläufe erstellt wurden.
Setzen Sie fillStyle
oder strokeStyle
auf das CanvasPattern-Objekt, das von der createPattern()
-Methode des Kontext-Objekts geliefert wird, wenn Sie zum Zeichnen oder Füllen ein Hintergrundbildmuster statt einer Farbe nutzen wollen:
Das erste Argument für createPattern()
gibt das Bild an, das als Muster verwendet wird. Es muss ein <img>
-, <canvas>
– oder <video>
-Element aus dem Dokument sein (oder ein Bildobjekt, das mit dem Image()
-Konstruktor erzeugt wurde). Das zweite Argument ist üblicherweise repeat
, damit ein Bild unabhängig von der Größe des Bilds wiederholt eingefügt wird, aber Sie können auch repeat-x
, repeat-y
oder no-repeat
angeben.
Beachten Sie, dass Sie ein <canvas>
-Tag (selbst eins, das nie dem Dokument hinzugefügt wurde und nicht sichtbar ist) als Musterquelle für ein anderes <canvas>
nutzen können:
Wollen Sie mit einem Farbverlauf füllen (oder zeichnen), setzen Sie fillStyle
(oder strokeStyle
) auf ein CanvasGradient
-Objekt, das von den createLinearGradient()
– oder createRadialGradient()
-Methoden des Kontexts geliefert wird. Die Erstellung von Verläufen erfordert mehrere Schritte, und ihr Einsatz ist komplizierter als der von Mustern.
Der erste Schritt ist, dass Sie ein CanvasGradient
-Objekt erstellen. Die Argumente für createLinearGradient()
sind die Koordinaten zweier Punkte, die eine Linie definieren (die nicht horizontal oder vertikal sein muss), an der entlang sich die Farbe ändert. Die Argumente für createRadialGradient()
geben den Mittelpunkt und die Radien von zwei Kreisen an. (Sie müssen nicht konzentrisch sein, aber der erste Kreis liegt üblicherweise vollständig im zweiten.) Bereiche im kleineren Kreis oder außerhalb des größeren werden einfarbig gefüllt, Bereiche zwischen den beiden Kreisen werden mit einem Farbverlauf gefüllt.
Nachdem wir das CanvasGradient
-Objekt erstellt und die Bereiche des Canvas definiert haben, die gefüllt werden, definieren wir die Verlaufsfarben, indem wir die addColorStop()
-Methode des CanvasGradient
-Objekts nutzen. Das erste Argument für diese Methode ist eine Zahl zwischen 0.0 und 1.0, das zweite eine CSS-Farbangabe. Sie müssen diese Methode mindestens zwei Mal aufrufen; um einen Farbverlauf zu definieren, können Sie sie allerdings auch öfter aufrufen. Die Farbe bei 0.0 erscheint am Anfang des Verlaufs und die Farbe bei 1.0 am Ende. Geben Sie zusätzliche Farben an, erscheinen diese an den entsprechenden Positionen. An den anderen Punkten werden die Farben stetig interpoliert. Hier sind einige Beispiele:
Ein wichtiger Punkt bei Verläufen ist, dass sie nicht positionsunabhängig sind. Wenn Sie einen Verlauf erstellen, geben Sie Grenzen für den Verlauf vor. Versuchen Sie später, einen Bereich außerhalb dieser Grenzen zu füllen, erhalten Sie eine einfarbige Fläche in der Farbe, die am entsprechenden Ende des Verlaufs definiert ist. Haben Sie einen Verlauf über eine Gerade zwischen (0,0) und (100,100) definiert, sollten Sie diesen Verlauf beispielsweise nur nutzen, um Objekte zu füllen, die sich im Rechteck (0,0,100,100) befinden.
Die Grafik in Abbildung 1 wurde mit dem nachfolgenden Code erstellt (unter Verwendung des oben definierten Musters pattern
und der Verläufe bgfade
und peekhole
):
Grafikattribute
Wenn Sie eine Methode definieren, die eine Figur zeichnet und diese Eigenschaften nicht selbst setzt, kann der Aufrufer Ihrer Methode die Farbe der Figur setzen, indem er vor dem Aufruf der Methode die Eigenschaften strokeStyle
und fillStyle
setzt. Diese Trennung von Grafikzustand und Zeichenbefehlen ist grundlegend für die Canvas-API und mit der Trennung von Darstellung und Inhalt verwandt, die man durch die Anwendung von Cascading Style Sheets (CSS) auf HTML-Dokumente erreicht.
Die Canvas-API definiert auf dem CanvasRenderingContext2D-Objekt 15 Grafikattribut-Eigenschaften. Diese Eigenschaften werden in Tabelle 1 aufgeführt und in den nachfolgenden Abschnitten ausführlich erläutert.
Eigenschaft | Bedeutung |
---|---|
fillStyle |
Die Farbe, der Verlauf oder das Muster zum Füllen. |
font |
Die CSS-Schriftart für Befehle zum Zeichnen von Text. |
globalAlpha |
Die Transparenz, die allen gezeichneten Pixeln hinzugefügt wird. |
globalCompositeOperation |
Wie die Pixelfarben kombiniert werden. |
lineCap |
Wie die Linienenden dargestellt werden. |
lineJoin |
Wie Eckpunkte dargestellt werden. |
lineWidth |
Die Breite der gezogenen Striche. |
miterLimit |
Die maximale Länge spitzwinkliger Eckpunkte. |
textAlign |
Horizontale Textausrichtung. |
textBaseline |
Vertikale Textausrichtung. |
shadowBlur |
Wie scharf oder unscharf Schatten sind. |
shadowColor |
Die Farbe des Schlagschattens. |
shadowOffsetX |
Die horizontale Verschiebung von Schatten. |
shadowOffsetY |
Die vertikale Verschiebung von Schatten. |
strokeStyle |
Die Farbe, der Verlauf oder das Muster für Striche. |
Da die Canvas-API die Grafikattribute auf dem Kontext-Objekt definiert, könnten Sie versucht sein, mehrfach getContext()
aufzurufen, um mehrere Kontext-Objekte zu erhalten. Wäre das möglich, könnten Sie auf diesen Kontexten unterschiedliche Attribute definieren, und jeder Kontext wäre dann wie ein anderer Pinsel, der mit anderer Farbe oder anderer Breite zeichnet. Unglücklicherweise können Sie das Canvas auf diese Weise nicht nutzen. Jedes <canvas>
-Tag hat nur ein einziges Kontext-Objekt, und jeder Aufruf von getContext()
liefert das gleiche CanvasRenderingContext2D-Objekt.
Obwohl die Canvas-API Ihnen nicht ermöglicht, eigene Sätze von Grafikattributen zu definieren, ermöglicht sie Ihnen doch, den aktuellen Grafikzustand zu speichern, damit Sie ihn bearbeiten und später leicht wiederherstellen können. Die Methode save()
schiebt den aktuellen Grafikzustand auf einen Stapel gespeicherter Zustände. Die Methode restore()
nimmt das oberste Element dieses Stapels und stellt den entsprechenden Zustand wieder her. Alle in Tabelle 1 aufgeführten Eigenschaften sind Teil des gespeicherten Zustands, außerdem auch die aktuelle Transformation und der Clipping-Bereich (die beide später erläutert werden). Wichtig ist, dass der aktuell definierte Pfad und der aktuelle Punkt nicht Teil des Grafikzustands sind und nicht gespeichert und wiederhergestellt werden können.
Wenn Sie mehr Flexibilität benötigen, als Ihnen ein einfacher Stapel bietet, kann eine Hilfsmethode wie die in Beispiel vorgestellte nützlich sein.
Kurven zeichnen und füllen
Ein Pfad ist eine Folge von Teilpfaden, und ein Teilpfad ist eine Folge verbundener Punkte. In den Pfaden, die wir in Linien zeichnen und Polygone füllen und Transformations-Beispiel definiert haben, wurden diese Punkte durch gerade Liniensegmente verbunden, aber das muss nicht immer der Fall sein. Das CanvasRenderingContext2D-Objekt definiert eine Reihe von Methoden, die einem Teilpfad einen neuen Punkt hinzufügen und diesen über eine Kurve mit dem aktuellen Punkt verbinden:
arc()
Die Methode fügt dem aktuellen Teilpfad einen Kreisbogen hinzu. Sie verbindet den aktuellen Punkt über eine gerade Linie mit dem Anfang des Bogens, dann den Anfang des Bogens über einen Kreisbogen mit dem Ende des Bogens und macht das Ende des Bogens zum neuen aktuellen Punkt. Der zu zeichnende Bogen wird durch sechs Parameter bestimmt: die x– und y-Koordinaten des Kreismittelpunkts, den Radius des Kreises, den Start- und den Endwinkel des Bogens und die Richtung (im Uhrzeigersinn oder entgegen dem Uhrzeigersinn) des Bogens zwischen diesen beiden Winkeln.
arcTo()
Die Methode zeichnet wie arc()
eine gerade Linie und einen Kreisbogen, gibt den zu zeichnenden Bogen aber mit anderen Parametern an. Die Argumente für arcTo()
geben die Punkte P1 und P2 und einen Radius an. Der Bogen, der dem Pfad hinzugefügt wird, hat den angegebenen Radius und tangentiert die Linie zwischen dem aktuellen Punkt und P1 sowie der Linie zwischen P1 und P2. Diese ungewöhnlich scheinende Methode zur Angabe von Bogen ist eigentlich ziemlich nützlich, wenn Sie Figuren mit abgerundeten Ecken zeichnen wollen. Geben Sie für den Radius 0 an, zeichnet diese Methode eine gerade Linie vom aktuellen Punkt zum Punkt P1. Ist der Radius größer als null, wird eine gerade Linie vom aktuellen Punkt in Richtung von P1 gezeichnet, die sich dann in einem Kreis beugt, bis sie in Richtung P2 läuft.
bezierCurveTo()
Die Methode fügt dem Teilpfad einen neuen Punkt P hinzu und verbindet diesen über eine kubische Bezierkurve mit dem aktuellen Punkt. Die Gestalt der Kurve wird durch zwei „Kontrollpunkte“ C1 und C2 bestimmt. Am Anfang der Kurve (am aktuellen Punkt) läuft die Kurve in Richtung von C1. Das Ende der Kurve (der Punkt P) wird aus Richtung von C2 erreicht. Zwischen diesen Punkten ändert sich die Richtung der Kurve stetig. Der Punkt P wird der neue aktuelle Punkt für den Teilpfad.
quadraticCurveTo()
Diese Methode ähnelt bezierCurveTo()
, nutzt aber eine quadratische Bezierkurve statt einer kubischen Bezierkurve und hat nur einen einzigen Kontrollpunkt.
Mit diesen Methoden können Sie Pfade wie die in Abbildung 1 zeichnen.
Beispiel zeigt den Code, mit dem Abbildung 1 erstellt wurde. Die in diesem Code illustrierten Methoden zählen zu den kompliziertesten Methoden in der Canvas-API.
Einem Pfad Kurven hinzufügen
Rechtecke
CanvasRenderingContext2D definiert vier Methoden zum Zeichnen von Rechtecken. Beispiel nutzt eine davon, fillRect()
, um die Kontrollpunkte der Bezierkurven zu markieren. Alle vier Rechteckmethoden erwarten zwei Argumente, die eine Ecke des Rechtecks angeben, sowie zwei, die die Breite und die Höhe des Rechtecks angeben. Üblicherweise geben Sie die linke obere Ecke an und dann eine positive Breite und Höhe, aber Sie können auch andere Ecken und negative Maße bestimmen.
fillRect()
füllt das angegebene Rechteck mit dem aktuellen fillStyle
. strokeRect()
zieht den Umriss des angegebenen Rechtecks mit dem aktuellen strokeStyle
und anderen Strichattributen. clearRect()
ist wie fillRect()
, ignoriert aber den aktuellen Füllstil und füllt das Rechteck mit transparenten schwarzen Pixeln (der Standardfarbe eines vollständig leeren Canvas). Das Wichtige an diesen drei Methoden ist, dass sie sich nicht auf den aktuellen Pfad oder den aktuellen Punkt auf diesem Pfad auswirken.
Die letzte Rechteckmethode heißt rect()
und wirkt sich auf den aktuellen Pfad aus: Sie fügt das definierte Rechteck in einem eigenen Teilpfad dem Pfad hinzu. Wie andere Methoden zur Pfaddefinition zeichnet oder füllt auch diese von allein nichts.
Linien zeichnen und Polygone füllen
Wenn Sie Linien in ein Canvas zeichnen und die von ihnen eingeschlossenen Flächen füllen wollen, definieren Sie zunächst einen Pfad. Ein Pfad ist eine Folge von einem oder mehreren Teilpfaden. Ein Teilpfad ist eine Folge von zwei oder mehr Punkten, die durch Liniensegmente (oder, wie wir später sehen werden, Kurvensegmente) verbunden sind. Einen neuen Pfad beginnen Sie mit der Methode beginPath()
. Einen neuen Teilpfad beginnen Sie mit der Methode moveTo()
. Wenn Sie den Startpunkt eines Teilpfads mit moveTo()
eingerichtet haben, können Sie diesen Punkt über eine Gerade mit einem neuen Punkt verbinden, indem Sie die Methode lineTo()
aufrufen. Der folgende Code definiert einen Pfad mit zwei Liniensegmenten:
Der vorangehende Code definiert nur einen Pfad. Er zeichnet noch nichts auf das Canvas. Rufen Sie die Methode stroke()
auf, um die beiden Liniensegmente im Pfad zu zeichnen (oder „zu ziehen“). Rufen Sie die Methode fill()
auf, um die von diesen Liniensegmenten definierte Fläche zu füllen:
Der vorangehende Code (sowie etwas zusätzlicher Code, der die Strichbreite und die Füllfarbe setzt) erzeugt die in Abbildung 1 gezeigte Zeichnung.
Beachten Sie, dass der oben definierte Teilpfad „offen“ ist. Er besteht aus zwei Liniensegmenten, deren Endpunkt nicht wieder mit dem Startpunkt verbunden ist. Das bedeutet, dass der Pfad keine Fläche einschließt. Die Methode fill()
füllt offene Teilpfade, indem sie so tut, als wäre der Endpunkt über eine gerade Linie mit dem Startpunkt verbunden. Deswegen füllt der vorangehende Code ein Dreieck, zeichnet aber nur zwei Seiten dieses Dreiecks.
Wenn alle drei Seiten des zuvor gezeigten Dreiecks gezeichnet werden sollen, müssen Sie die Methode closePath()
aufrufen, um den Endpunkt des Teilpfads mit dem Startpunkt zu verbinden. (Sie könnten auch lineTo(20,20)
aufrufen, hätten damit aber drei Liniensegmente, die dann einen Start- und Endpunkt teilen, aber nicht wirklich geschlossen sind. Zeichnen Sie breite Linien, ist das sichtbare Ergebnis besser, wenn Sie closePath()
nutzen.)
Es gibt zwei weitere wichtige Punkte, die Sie sich in Bezug auf stroke()
und fill()
merken sollten. Zunächst operieren beide Methoden auf allen Teilpfaden des aktuellen Pfads. Angenommen, wir hätten einen weiteren Teilpfad im Code:
Würden wir jetzt stroke()
aufrufen, würden wir zwei verbundene Schenkel eines Dreiecks zeichnen und eine nicht verbundene vertikale Linie.
Der zweite Punkt ist, dass weder stroke()
noch fill()
den aktuellen Pfad ändern: Sie können fill()
aufrufen, und der Pfad ist immer noch da, wenn Sie stroke()
aufrufen. Wenn Sie die Arbeit mit dem Pfad abgeschlossen haben und einen anderen Pfad eröffnen wollen, dürfen Sie nicht vergessen, beginPath()
aufzurufen. Tun Sie das nicht, fügen Sie dem bestehenden Pfad neue Teilpfade hinzu und zeichnen womöglich immer wieder diese Teilpfade.
Beispiel 1 definiert eine Funktion zum Zeichnen gleichseitiger Polygone und illustriert die Verwendung von moveTo()
, lineTo()
und closePath()
zur Definition von Teilpfaden sowie fill()
und stroke()
zum Zeichnen dieser Pfade. Es erzeugt die in Abbildung 1 gezeigte Zeichnung.
Beispiel 1: Gleichseitige Polygone mit moveTo()
, lineTo()
und closePath()
Beachten Sie, dass dieses Beispiel ein Sechseck zeichnet, das ein Quadrat einschließt. Das Quadrat und das Sechseck werden durch separate Teilpfade gebildet, die sich überschneiden. Wenn das passiert (oder wenn es innerhalb eines Teilpfads Überschneidungen gibt), muss das Canvas ermitteln können, welche Bereiche im Pfad sind und welche außerhalb des Pfads.
Das Canvas nutzt einen Test, der als Nonzero Winding-Regel bezeichnet wird. In diesem Fall wird das Innere des Quadrats nicht gefüllt, da Quadrat und Sechseck in umgekehrter Richtung gezeichnet wurden: Die Ecken des Sechsecks wurden im Uhrzeigersinn durch Liniensegmente verbunden, die Ecken des Quadrats entgegen dem Uhrzeigersinn. Wären die Ecken des Quadrats ebenfalls im Uhrzeigersinn verbunden worden, hätte der fill()
-Aufruf auch das Innere des Quadrats gefüllt.
Die Nonzero Winding-Regel
Ob sich ein Punkt P innerhalb eines Pfads befindet, testen Sie mit der Nonzero Winding-Regel, indem Sie sich einen Strahl vorstellen, der von P aus in beliebiger Richtung bis ins Unendliche geht (oder, was praktikabler ist, bis zu einem Punkt außerhalb des Rahmenrechtecks des Pfads). Initialisieren Sie jetzt einen Zähler auf null und zählen Sie alle Punkte, an denen der Pfad den Strahl schneidet. Erhöhen Sie den Zähler um eins, wenn der Pfad den Strahl im Uhrzeigersinn schneidet. Verringern Sie die Zähler um eins, wenn der Pfad den Strahl entgegen dem Uhrzeigersinn schneidet. Ist der Zähler nach Auszählung aller Schnittpunkte ungleich null, befindet sich der Punkt innerhalb des Pfads. Ist der Zähler null, befindet sich der Punkt außerhalb des Pfads.
Logos zeichnen
Das canvas
-Element ist genauso wie das script
-Element ein Container, sozusagen eine leere Tafel, auf der wir zeichnen können. So definieren Sie ein canvas
-Element mit einer bestimmten Breite und Höhe:
Leider können Sie die Breite und Höhe eines canvas
-Elements nicht mit CSS anpassen, ohne die Inhalte zu verzerren. Insofern müssen Sie sich also schon bei der Deklaration für die Maße Ihres canvas
-Elements entscheiden.
Wir verwenden JavaScript, um Formen darauf zu zeichnen.
Selbst wenn Sie für Browser ohne canvas
-Element alternativen Inhalt bereitstellen, müssen Sie dennoch verhindern, dass dieser durch den JavaScript-Code verändert wird. Suchen Sie das canvas
-Element anhand seiner ID, und prüfen Sie, ob der Browser die getContext
-Methode des canvas
-Elements unterstützt.
Wenn die getContext
-Methode etwas zurückliefert, rufen wir den 2D-Kontext des canvas
-Elements auf, damit wir Objekte hinzufügen können. Wenn wir keinen Kontext erhalten, müssen wir eine Möglichkeit finden, den Alternativinhalt anzuzeigen. Da wir wissen, dass das canvas
-Element nur mit JavaScript funktioniert, bauen wir von Anfang an ein Framework für die Ausweichlösung auf.
Sobald Sie über den Kontext des canvas
-Elements verfügen, können Sie Elemente dazu hinzufügen. Ein rotes Rechteck fügen Sie zum Beispiel ein, indem Sie die Füllfarbe festlegen und dann ein Rechteck erstellen:
Der 2D-Kontext des canvas
-Elements ist ein Raster, dessen standardmäßiger Ursprung die linke obere Ecke ist. Zum Zeichnen einer Form geben Sie die x– und y-Koordinaten sowie Breite und Höhe an.
Jede Form wird in einer eigenen Ebene gezeichnet,
sodass Sie beispielsweise auch drei Rechtecke mit einem Versatz von je 10 Pixel erstellen können:
Die Dreiecke werden dann auch entsprechend übereinander geschichtet:
Da Sie nun die Grundlagen bereits verstehen, stellen wir gleich das Logo zusammen. Wie Sie in Abbildung sehen, ist es relativ simpel.
Das Logo zeichnen
Das Logo besteht aus einem Schriftzug, einem gewinkelten Pfad, einem Quadrat und einem Dreieck. Erstellen wir ein neues HTML5-Dokument mit einem canvas
-Element und einer JavaScript-Funktion zum Zeichnen des Logos, die ermittelt, ob wir das 2D-Canvas verwenden können.
Diese Methode rufen wir aber erst auf, nachdem wir geprüft haben, ob das canvas
-Element existiert:
Beachten Sie, dass wir auch hier wieder die jQuery-Funktion verwenden, um sicherzustellen, dass das Event ausgelöst wird, wenn das Dokument bereit ist. Wir suchen ein Element mit der ID logo
auf der Seite, also sollten wir dafür sorgen, dass unser Dokument auch ein canvas
-Element enthält. Denn nur so können wir es finden und unsere Prüfung durchführen.
Als Nächstes fügen wir unser Text zum canvas
-Element hinzu.
Text einfügen
Um Text zum canvas
-Element hinzuzufügen, müssen wir eine Schrift, eine Schriftgröße sowie die Ausrichtung wählen und anschließend den Text auf die entsprechenden Koordinaten im Raster anwenden. So fügen wir den Text in unser canvas
-Element ein:
Wir definieren den Schrifttyp und legen die Grundlinie bzw. die vertikale Ausrichtung fest, bevor wir den Text in das canvas
-Element einfügen. Wir verwenden die fillText
-Methode und erhalten Text, der mit der Füllfarbe gefüllt ist und den wir 60 Pixel nach rechts platzieren, damit wir Platz für den großen dreieckigen Pfad haben, den wir als Nächstes zeichnen.
Linien zeichnen
Linien können wir auf dem canvas
-Element ganz einfach mit einer Runde „Malen nach Zahlen“ zeichnen: Wir geben einen Anfangspunkt im Raster an und legen dann zusätzliche Punkte im Raster fest, die mit Punkten verbunden werden:
Wir verwenden die Methode beginPath()
, um mit dem Zeichnen einer Linie zu beginnen, und erstellen dann unseren Pfad:
Wenn wir damit fertig sind, uns über das canvas
-Element zu bewegen, rufen wir die Methode stroke
auf, um die Linie zu zeichnen. Anschließend rufen wir die Methode closePath
auf, um den Zeichenvorgang zu beenden.
Bleibt also nur noch die Kombination aus Rechteck und Dreieck innerhalb des größeren Dreiecks.
Den Ursprung verschieben
Innerhalb des größeren Dreiecks müssen wir ein kleines Quadrat und ein kleines Dreieck zeichnen. Wenn wir Formen und Pfade zeichnen, geben wir die x– und y-Koordinaten relativ zum Ursprung in der linken oberen Ecke des canvas
-Elements an. Wir können den Ursprung aber auch an eine neue Position verschieben.
Zum Zeichnen des inneren kleinen Quadrats verschieben wir den Ursprung:
Beachten Sie, dass wir vor dem Verschieben des Ursprungs die Methode save()
aufrufen. Hierdurch wird der bisherige Zustand des canvas
-Elements gespeichert, sodass wir jederzeit Änderungen rückgängig machen können. Das funktioniert wie ein Wiederherstellungspunkt – Sie können sich das wie einen Stapel vorstellen. Jedes Mal, wenn Sie save()
aufrufen, erhalten Sie einen neuen Eintrag. Wenn wir fertig sind, rufen wir restore()
auf, wodurch die jeweils letzte Speicherung auf dem Stapel wiederhergestellt wird.
Nun zeichnen wir mit Pfaden das innere Dreieck. Aber statt Linien verwenden wir eine Füllung, um den Eindruck zu erwecken, dass das Dreieck aus dem Quadrat „herausgeschnitten“ wird.
In diesem Fall legen wir für die Striche und die Füllung die Farbe Weiß (#fff
) fest, bevor wir mit dem Zeichnen beginnen. Anschließend zeichnen wir unsere Linien. Nachdem wir zuvor den Ursprung verschoben haben, bewegen wir uns nun relativ zur oberen linken Ecke des zuvor gezeichneten Quadrats.
Wir sind beinahe fertig, aber es fehlt noch ein bisschen Farbe.
Farben
Im Abschnitt Den Ursprung verschieben haben Sie kurz gesehen, wie Sie die Striche und die Füllfarbe für die Zeichenwerkzeuge festlegen. Wir könnten beispielsweise für alles die Farbe Rot wählen, indem wir vor dem Zeichnen den folgenden Code einfügen:
Aber das wäre ein bisschen langweilig. Stattdessen erzeugen wir Verläufe und weisen diese den Strichen und Füllungen zu:
Wir erstellen einfach ein Gradient-Objekt und legen die Farbwerte fest. In diesem Beispiel bewegen wir uns nur zwischen zwei Rotschattierungen. Wir könnten aber auch den gesamten Regenbogen zeichnen, wenn wir das wollten.
Beachten Sie, dass wir die Farbe festlegen müssen, bevor wir etwas zeichnen.
Damit ist unser Logo fertig und wir haben ein besseres Verständnis davon, wie wir einfache Formen im canvas
-Element zeichnen können. Allerdings bietet der Internet Explorer vor Version 9 keine Unterstützung für das canvas
-Element. Darum müssen wir uns kümmern.
Ausweichlösung
Also müssen wir auf andere Lösungen zurückgreifen, wie etwa ein PNG des Logos innerhalb des canvas
-Elements. Oder wir verwenden das canvas
-Element überhaupt nicht. Dieser Teil sollte nur eine Übung sein, um Ihnen zu zeigen, wie Sie zeichnen können. Es ist also kein Weltuntergang, wenn wir dieses bestimmte Beispiel noch nicht in einem plattformübergreifenden Produktionssystem einsetzen können.
Muster
Zur Festlegung eigener Muster für Füllungen und Linien stellt die Spezifikation die Methode createPattern()
zur Verfügung, die ähnlich wie drawImage()
sowohl image
– als auch canvas
– oder video
-Elemente als Input akzeptiert und im Parameter repetition
die Art der Musterwiederholung definiert.
Gültige Werte für das repetition
-Argument sind, wie schon vom background-color
-Attribut der CSS-Spezifikation her bekannt, repeat
, repeat-x
, repeat-y
, und no-repeat
. Verwenden wir wieder die 16 benannten Grundfarben, können wir über ein paar Zeilen Code Schachbrettmuster mit jeweils zwei zusammenpassenden Farbpaaren erzeugen.
Das Pattern selbst erzeugen wir als in-memory
-Canvas mit 20 x 20 Pixeln Breite und vier 10×10 Pixel großen Quadraten. Am Beispiel des grünen Musters sieht dieser Schritt folgendermaßen aus:
Mit createPattern()
definieren wir dann den Canvas cvs als sich wiederholendes Muster, weisen ihn dem Attribut fillStyle
zu und füllen damit das Quadrat.
Patterns sind am Koordinatenursprung verankert und werden ab dort aufgetragen. Würden wir im obigen Beispiel fillRect()
nicht bei 0/0, sondern um zehn Pixel nach rechts versetzt bei 10/0 beginnen, wäre dementsprechend Dunkelgrün (green
) und nicht Hellgrün (lime
) die erste Farbe links oben.
Sehen wir uns zuerst an, wie der Hintergrund zustande kommt:
Die ersten beiden Zeilen erzeugen wieder ein neues Image-Objekt und setzen dessen src
-Attribut auf das Bild pattern_125.png im Verzeichnis icons. Genau wie bei drawImage()
müssen wir vor dem Definieren des Patterns sicherstellen, dass das gewünschte Bild auch wirklich geladen ist. Die Funktion bg.onload()
enthält dabei den eigentlichen Code zum Generieren des sich wiederholenden Musters, das wir mit 50 % Opazität auf der gesamten Canvas-Fläche auftragen. Über das gleiche Prozedere füllen wir den Titeltext Yosemite! mit pattern_107.png.
Für die überlappenden Bildausschnitte setzen wir kurzerhand das ganze Yosemite-Bild yosemite.jpg als Muster ein und arbeiten dann in einer for
-Schleife das Input-Array extents
ab, in dem sich x-, y-, width
– und height
-Werte der gewünschten Ausschnitte befinden. Über den Aufruf von fillRect()
wird der entsprechende Bildbereich als Füllmuster gezeigt und mittels strokeText()
mit einem zusätzlichen Rahmen versehen.
Da in Abbildung 1 drei verschiedene Bilder zum Zuge kommen und alle drei vor ihrer Verwendung vollständig geladen sein müssen, bleibt uns nichts anderes übrig, als die drei onload
-Funktionen ineinander zu verschachteln. Nur so behalten wir die Kontrolle über die Reihenfolge beim Zeichnen. Der Pseudo-Code für eine mögliche Schachtelung sieht so aus:
Die einzige Möglichkeit, dieses Verschachteln zu vermeiden, wäre, alle beteiligten Bilder im HTML-Code der Seite als über visibility:hidden
versteckte img
-Elemente zu verlinken und mit getElementById()
oder getElementsByTag-Name()
nach dem Laden der Seite in window.onload()
zu referenzieren.
Bleibt abschließend noch festzuhalten, dass bei der Verwendung eines video
-Elements als Quelle für createPattern()
analog zur drawImage()
-Methode der erste Frame des Videos beziehungsweise der Poster-Frame, sofern vorhanden, als Muster zum Einsatz kommt.
Pfade
Die Abläufe beim Erstellen von Pfaden in Canvas sind vergleichbar mit dem Zeichnen auf einem Blatt Papier: Stift an einer Position am Blatt absetzen, Zeichnen, Stift wieder anheben und nach Belieben an anderer Stelle weiterzeichnen. Zeicheninhalte können dabei von einfachen Linien über komplexe Kurven bis hin zu daraus gebildeten Polygonen reichen. Ein erstes Beispiel verdeutlicht dieses Konzept und übersetzt die Schritte beim Schreiben des Buchstabens A in Canvas-Pfadbefehle:
Sehen wir uns den Quellcode für dieses Beispiel näher an, so erkennen wir drei Phasen der Pfaderstellung:
- 1. Initialisieren eines neuen Pfades mit
beginPath()
- 2. Definieren der Pfadgeometrie durch
moveTo()
undlineTo()
-Aufrufe - 3. Zeichnen der Linien mit
stroke()
Jeder Pfad muss mit beginPath()
initialisiert werden und kann dann beliebig viele Segmente enthalten. In unserem Beispiel sind es zwei, die die Bewegungen der Hand beim Schreiben über Kombinationen von moveTo()
und lineTo()
nachbilden. Somit entsteht zuerst die Dachform und dann die horizontale Linie des Buchstabens A. Mit stroke()
wird schließlich der zuvor definierte Pfad auf die Canvas-Fläche gezeichnet.
Die Entscheidung, ob und wann Segmente eines Pfades in mehrere einzelne Pfade aufgetrennt werden,
hängt einzig und allein vom Layout ab, denn jeder Pfad kann nur in seiner Gesamtheit formatiert werden. Sollte die horizontale Linie des Buchstabens also eine eigene Farbe bekommen, müssten auch zwei Pfade definiert werden.
Wenden wir uns nun den wichtigsten Pfad-Zeichenmethoden im Detail zu.
Linien
Zur Konstruktion von Linienzügen wie im Buchstaben-Beispiel stellt Canvas die Methode lineTo()
zur Verfügung:
Übersetzt bedeutet das so viel wie „Linie zum Punkt x/y„, womit klar wird, dass der Ausgangspunkt schon vorher über moveTo()
oder als Endpunkt der letzten Zeichenoperation existieren muss. Nach dem Zeichnen wird die Koordinate x/y zum neuen aktuellen Punkt.
Info
Bei allen Grafiken zur Erklärung der Pfad-Zeichenmethoden sind der Ausgangspunkt x0/y0 in Hellgrau und der neue aktuelle Punkt in fetter Schrift ausgewiesen.
Bezierkurven
Canvas kennt zwei Arten von Bezierkurven: quadratische und kubische, die fälschlicherweise nur als bezierCurveTo()
bezeichnet werden.
Zur Konstruktion der Bezierkurven benötigen wir neben dem aktuellen Punkt als Ausgangskoordinate die Zielkoordinate und je nach Kurvenart einen oder zwei Kontrollpunkte. Neuer aktueller Punkt nach dem Zeichnen ist in beiden Fällen die Koordinate x/y.
Kreisbögen
Etwas schwieriger zu verstehen sind mit Sicherheit die Methoden zum Konstruieren von Kreisbögen, den sogenannten arcs
. Die erste davon definiert sich über zwei Koordinaten und einen Radius:
Wie aus Abbildung 1 erkennbar ist, konstruiert arcTo()
den neuen Pfad auf folgende Weise: An den Linienzug von x0/y0 über x1/y1 nach x2/y2 wird ein Kreis mit gegebenem Radius so angelegt, dass er die Linien in genau zwei Punkten, der Starttangente t1 und Endtangente t2, schneidet. Der Bogen zwischen diesen beiden Punkten wird Teil des Pfades, und die Endtangente t2 wird zum neuen aktuellen Punkt.
In der Praxis gut einsetzbar ist diese Methode für Rechtecke mit abgerundeten Ecken – eine wiederverwendbare Funktion, die das erledigt, ist da nicht fehl am Platz:
Die Funktion roundedRect()
erwartet neben den Basiswerten für das Rechteck selbst den Radius zum Abrunden und zeichnet dann mit einer moveTo()
-, vier arcTo()
– und einer closePath()
-Methode das gewünschte Rechteck. Die Methode closePath()
kennen Sie noch nicht: Sie verbindet den letzten Punkt wieder mit dem Anfangspunkt und bewirkt damit das Schließen des Rechtecks.
Auf den ersten Blick noch komplizierter erscheint die zweite Variante, Kreisbögen zu erstellen: die Methode arc()
. Neben Zentrum und Radius müssen jetzt auch noch zwei Winkel und die Drehrichtung übergeben werden.
Der Ausgangspunkt für den arc
in Abbildung 2 ist das Zentrum eines Kreises mit gegebenem Radius. Von dort aus werden über die Winkel startAngle
und endAngle
zwei Zeiger konstruiert, die den Kreis in zwei Punkten schneiden. Die Richtung des Kreisbogens zwischen diesen beiden Koordinaten bestimmt der Parameter anticklockwise, wobei 0 für im Uhrzeigersinn und 1 für gegen den Uhrzeigersinn steht.
Der resultierende
arc
beginnt somit im Zentrum des Kreises bei x0/y0,
verbindet dieses in einer gerade Linie mit dem ersten Schnittpunkt spx/spy und zeichnet von dort aus den Kreisbogen zum Endpunkt epx/epy, der dann zum neuen aktuellen Punkt wird.
Der größte Wermutstropfen bei der Konstruktion von arcs
ist, dass alle Winkel in Radiant statt Grad angegeben werden müssen. Ein kurzes Helferlein als Gedächtnisstütze bei der Umrechnung kann also nicht schaden:
Wenn wir schon bei Hilfsfunktionen sind, können wir gleich noch zwei weitere hinzufügen, die uns das Zeichnen von Kreisen und Kreissektoren erleichtern. Für Kreise zum Beispiel benötigen wir ja eigentlich nur Zentrum und Radius, den Rest soll unsere Funktion circle()
erledigen:
Bei Kreisdiagrammen, auch Kuchen- oder Tortendiagramme genannt, sind vor allem die Winkelangaben in Radiant wenig intuitiv. Unsere Funktion sector()
erledigt die lästige Arbeit der Umrechnung für uns und erlaubt, Start- und Endwinkel in Grad zu übergeben:
Wenige Zeilen Code genügen nun schon, um Kreise und Kreisdiagramme zu zeichnen und dabei nicht den Überblick zu verlieren. Abbildung 3 zeigt das Ergebnis.
Rechtecke
In der Handhabung unseren beiden Helferlein schon ähnlicher ist die Methode rect()
, die damit auch etwas aus der Reihe tanzt.
Im Gegensatz zu allen bisherigen Pfadzeichenmethoden wird nämlich bei rect()
der aktuelle Punkt x0/y0 beim Zeichnen gänzlich ignoriert und das Rechteck nur über die Parameter x, y, Breite w und Höhe h definiert. Neuer aktueller Punkt nach dem Zeichnen wird der Ursprungspunkt x/y.
Umrisse, Füllungen und Clipmasken
Wenn wir uns an die drei Phasen der Pfaderstellung mit Initialisierung, Festlegen der Pfadgeometrie und Zeichnen erinnern, dann sind wir jetzt bei der dritten und letzten Phase angekommen: dem Zeichnen. Hier fällt die Entscheidung, wie der fertige Pfad aussehen soll. Alle bisherigen Beispiele entschieden sich an dieser Stelle für eine einfache Umrisslinie, die durch folgende Methode erzeugt wird:
Die Farbe der Linie bestimmt dabei das Attribut strokeStyle
. Daneben lassen sich die Linienstärke (lineWidth
), das Aussehen der Linienenden (lineCap
) sowie das Erscheinungsbild der Verbindung zwischen Linien (lineJoin
) über drei weitere Canvas-Attribute festlegen (der Stern kennzeichnet Defaultwerte und wird uns ab jetzt noch öfter begegnen).
Die Linienbreite lineWidth
wird in Pixel angegeben, ihr Defaultwert liegt bei 1.0. Sie gilt, wie die beiden anderen Linienattribute, nicht nur für Linien und Polygone, sondern auch für Rechtecke, die mit strokeRect()
erstellt wurden.
Das Linienende lineCap
kann entweder gekappt (butt
), rund (round
) oder quadratisch (square
) mit butt
als Standardwert sein. Wird round
verwendet, erfolgt die Abrundung der Linie durch Hinzufügen eines Halbkreises am Linienende mit halber lineWidth
als Radius. Bei der Methode square
wird der Halbkreis durch ein Rechteck mit halber Linienbreite als Höhe ersetzt.
Abgeschrägte Linienverbindungen erzeugt das Attribut lineJoin
über bevel;
für die Abrundung der Ecken steht round
zur Verfügung, und spitze Eckverbindungen, vergleichbar einer Gehrung, erhält man durch miter
, das gleichzeitig Defaultwert ist. Um zu verhindern, dass über miter
verbundene Linien zu spitz werden, hält die Spezifikation das Attribut miterLimit
mit Standardwert 10.0 bereit. Dabei handelt es sich um das Verhältnis von Länge der Spitze (das ist der Abstand zwischen dem Schnittpunkt der Linien und der Spitze) zur halben Linienbreite. Wird miterlimit
überschritten, erfolgt das Kappen der Spitze, wodurch der gleiche Effekt wie bei bevel
entsteht.
Wollen wir Pfade mit einer Farbe oder Gradiente füllen, müssen wir zuvor das entsprechende Stilattribut über fillStyle
setzen und im Anschluss daran die folgende Pfadmethode aufrufen:
So einfach das klingt, so kompliziert kann es werden, wenn sich Pfade selbst schneiden oder ineinander verschachtelt sind. Dann kommt die sogenannte Nonzero-Füllregel zum Tragen, die anhand der Laufrichtung der beteiligten Pfadbestandteile entscheidet, ob gefüllt wird oder nicht.
Abbildung 4 zeigt die Nonzero-Regel in Aktion. Links sind beide Kreise im Uhrzeigersinn gezeichnet, und rechts läuft der innere Kreis gegen den Uhrzeigersinn und bewirkt damit das Loch in der Mitte.
Beim Zeichnen der gerichteten Kreise hilft uns übrigens wieder das Helferlein aus dem arc()
-Abschnitt, diesmal mit einer kleinen Modifikation: Die gewünschte Richtung wird nun als Argument übergeben. Gültige Werte für anticlockwise
sind 0 und 1.
Der Code für den rechten Kreis mit Loch ergibt sich demnach folgendermaßen:
Nach stroke()
und fill()
fehlt uns damit nur noch eine einzige Methode beim Zeichnen von Pfaden, nämlich die Methode:
So kurz wie ihr Name fällt auch die Erklärung aus: clip()
sorgt dafür, dass der definierte Pfad nicht gezeichnet, sondern als Ausstechform für alle weiteren Zeichenelemente verwendet wird. Alles, was innerhalb der Maske liegt, ist sichtbar, der Rest bleibt verborgen. Zurücksetzen kann man diese Maske, indem man eine weitere Clipmaske mit der gesamten Canvas-Fläche als Geometrie erstellt.
Pixelmanipulation
Die Methoden unserer Wahl, um Pixelwerte zu lesen und zu manipulieren, lauten getImageData()
, putImageData()
und createImageData()
. Nachdem in allen dreien der Begriff ImageData heraussticht, gilt es diesen als ersten zu definieren.
Arbeiten mit dem ImageData-Objekt
Nähern wir uns dem ImageData-Objekt mit einem 2 x 2 Pixel großen Canvas, auf den wir vier 1 x 1 Pixel große, gefüllte Rechtecke in den benannten Farben navy
, teal
, lime
und yellow
zeichnen.
Über die Methode getImageData(sx, sy, sw, sh
) greifen wir im nächsten Schritt auf das ImageData-Objekt zu, wobei die vier Argumente den gewünschten Canvas-Ausschnitt als Rechteck festlegen.
Das ImageData-Objekt selbst besitzt die Attribute ImageData.width
, ImageData.height
und ImageData.data
, wobei sich hinter Letzterem die tatsächlichen Pixelwerte im sogenannten CanvasPixelArray verstecken. Dabei handelt es sich um ein flaches Array mit Rot-, Grün-, Blau- und Alpha-Werten für jedes Pixel im gewählten Ausschnitt, beginnend links oben, von links nach rechts und von oben nach unten. Die Anzahl aller Werte ist im Attribut ImageData.data.length
gespeichert.
Mithilfe einer einfachen for
-Schleife können wir nun die einzelnen Werte des CanvasPixelArray auslesen und mit alert()
sichtbar machen. Beginnend bei 0, arbeiten wir uns von Pixel zu Pixel vor, indem wir nach jedem Schleifendurchgang den Zähler um 4 erhöhen. Die RGBA-Werte ergeben sich dann über Offsets von der aktuellen Position aus, wobei Rot beim Zähler i, Grün bei i+1, Blau bei i+2 und die Alpha-Komponente bei i+3 zu finden ist.
Genau demselben Prinzip folgt das Modifizieren von Pixelwerten, indem wir jetzt das CanvasPixelArray in-place
durch Zuweisung neuer Werte verändern. In unserem Beispiel werden die RGB-Werte mit Math.random()
auf Zufallszahlen zwischen 0 und 255 gesetzt; die Alpha-Komponente bleibt unberührt.
Der Canvas erscheint nach diesem Schritt allerdings noch unverändert. Erst durch Zurückschreiben des modifizierten CanvasPixelArray über die Methode putImageData()
werden die neuen Farben sichtbar. Beim Aufruf von putImageData()
sind maximal sieben Parameter erlaubt.
Die ersten drei Argumente sind verpflichtend anzugeben und beinhalten neben dem ImageData-Objekt die Koordinate des Ursprungspunktes dx/dy, von dem aus das CanvasPixelArray über seine width
– und height
-Attribute aufgetragen wird. Die optionalen dirty
-Parameter dienen dazu, einen bestimmten Bereich des CanvasPixelArray auszuschneiden und nur diesen mit reduzierter Breite und Höhe zurückzuschreiben. Abbildung 1 zeigt unseren 4-Pixel-Canvas vor und nach der Modifikation und listet die jeweiligen Werte des CanvasPixelArray auf.
Auf direktem Weg lässt sich ein leeres ImageData-Objekt über die Methode createImageData()
initialisieren. Breite und Höhe entsprechen dabei den Argumenten sw/sh oder den Dimensionen eines beim Aufruf übergebenen ImageDataObjekts. In beiden Fällen werden alle Pixel des CanvasPixelArray auf transparent/schwarz, also rgba(0,0,0,0)
, gesetzt.
Den 2 x 2 Pixel großen, modifizierten Canvas in Abbildung 1 könnten wir mithilfe von createImageData()
demnach auch direkt erzeugen und über putImageData()
zeichnen:
So viel zur nüchternen CanvasPixelArray-Theorie, die Praxis ist viel spannender, denn mit getImageData()
, putImageData()
, createImageData()
und etwas Mathematik lassen sich sogar eigene Farbfilter zum Manipulieren von Bildern schreiben. Wie das geht, zeigt der folgende Abschnitt.
Farbmanipulation mit getImageData()
, createImageData()
und putImageData()
Das Musterbild für alle Beispiele ist wieder die Aufnahme aus dem Yosemite-Nationalpark, die onload
mit drawImage()
auf den Canvas gezeichnet wird. In einem zweiten Schritt definieren wir über getImageData()
das originale CanvasPixelArray, das wir dann im dritten Schritt modifizieren. Dabei werden in einer for
-Schleife die RGBA-Werte jedes Pixels nach einer mathematischen Formel neu berechnet und in ein zuvor über createImageData()
erzeugtes CanvasPixelArray eingetragen, das wir am Ende mit putImageData()
wieder auf den Canvas zurückschreiben.
Der Code liefert das JavaScript-Grundgerüst für alle Filter, die in Abbildung 2 Verwendung finden. Die Funktion grayLuminosity()
ist nicht Teil des Code-Beispiels, sondern wird, wie alle anderen Filter, im Anschluss behandelt.
Info
Das Server-Icon in der rechten unteren Ecke von Abbildung 2 signalisiert, dass dieses Beispiel bei Verwendung von Firefox als Browser nur über einen Server mit dem http://-Protokoll aufgerufen werden kann.
Mit grayLuminosity()
verwenden wir in Abbildung 2 die zweite Formel und ersetzen die RGB-Komponenten jedes Pixels durch den neu berechneten Wert. Nicht vergessen dürfen wir in dieser und allen folgenden Berechnungen, dass RGBA-Werte nur Integerzahlen sein dürfen – die JavaScript-Methode parseInt()
stellt dies sicher.
Durch Aufsummieren der multiplizierten Komponenten können in jeder der drei Berechnungen natürlich auch Werte größer als 255 entstehen – in diesen Fällen wird 255 als neuer Wert eingesetzt.
Sehr einfach ist das Invertieren von Farben im Filter invertColor()
, denn jede RGB-Komponente muss nur von 255 abgezogen werden.
Der Filter swapChannels()
modifiziert die Reihenfolge der Farbkanäle. Dazu müssen wir als vierten Parameter die gewünschte Neuanordnung in einem Array definieren, wobei 0 für Rot, 1 für Grün, 2 für Blau und 3 für den AlphaKanal anzugeben ist. Beim Vertauschen der Kanäle hilft uns das Array rgba
mit den entsprechenden Eingangswerten, das wir in neuer Reihung zurückliefern. Ein Wechsel von RGBA nach BRGA, wie in unserem Beispiel, lässt sich mit order=[2, 0, 1, 3]
realisieren.
Die letzte Methode, monoColor()
, setzt die RGB-Komponente jedes Pixels auf eine bestimmte Farbe und verwendet den Grauwert des Ausgangspixels als Alpha-Komponente. Der vierte Parameter beim Aufruf definiert die gewünschte Farbe als Array von RGB-Werten – in unserem Fall ist dies Blau mit color= [0, 0, 255]
.
Die vorgestellten Filter sind noch sehr einfach gestrickt, da sie Farbwerte einzelner Pixel immer ohne Berücksichtigung der Nachbarpixel verändern. Bezieht man diese in die Berechnung ein, sind komplexere Methoden wie Schärfen, Unschärfemasken oder Kantenerkennung möglich.
Die Methode getImageData()
liefert ein ImageData-Objekt, das rohe (nicht vorab mit dem Alphawert multiplizierte) Pixel (als R-, G-, B- und A-Komponenten) aus einem rechteckigen Bereich des Canvas liefert. Leere ImageData-Objekte können Sie mit createImageData()
erstellen. Die Pixel in einem ImageData-Objekt sind schreibbar, können also beliebig gesetzt werden. Dann können Sie diese Pixel mit putImageData()
wieder in das Canvas kopieren.
Diese Methoden zur Pixelmanipulation bieten einen sehr elementaren Zugriff auf das Canvas. Das Rechteck, das Sie an getImageData()
übergeben, bezieht sich auf das Standardkoordinatensystem: Seine Ausmaße werden in CSS-Pixeln gemessen, und die aktuelle Transformation wirkt sich darauf nicht aus. Rufen Sie putImageData()
auf, wird ebenfalls das Standardkoordinatensystem genutzt. Außerdem ignoriert putImageData()
alle Grafikattribute. Die Methode führt kein Compositing durch, multipliziert keine Pixel mit globalAlpha
und zeichnet keine Schatten.
Die Methoden zur Pixelmanipulation sind gut zur Implementierung einer Bildverarbeitung geeignet. Beispiel 1 zeigt, wie man eine einfache Bewegungsunschärfe oder einen „Verwischeffekt“ auf den Zeichnungen auf einem Canvas umsetzt. Das Beispiel illustriert die Verwendung von getImageData()
und putImageData()
und zeigt, wie man die Pixelwerte in einem ImageData-Objekt durchläuft und bearbeitet, erklärt die jeweiligen Aspekte allerdings nicht im Detail.
Beispiel 1: Bewegungsunschärfe mit ImageData
Beachten Sie, dass getImageData()
den gleichen Cross-Origin-Sicherheitsbeschränkungen unterliegt wie toDataURL()
: Die Methode funktioniert nicht, wenn in das Canvas (direkt mit drawImage()
oder indirekt über ein CanvasPattern) ein Bild gezeichnet wurde, das eine andere Herkunft hat als das Dokument, das das Canvas enthält.
Vier Grafikattribut eigenschaften des CanvasRenderingContext2D-Objekts steuern das Zeichnen von Schlagschatten. Wenn Sie diese Eigenschaften passend setzen, erhält jede Linie, jede Fläche, jeder Text und jedes Bild einen Schlagschatten, der es so aussehen lässt, als würde das Element über der Canvas-Oberfläche schweben. Abbildung 1 zeigt einen Schatten unter einem gefüllten Rechteck, einem nicht gefüllten Rechteck und einem gefüllten Text.
Die Eigenschaft shadowColor
gibt die Farbe des Schattens an. Der Standard ist vollständig transparentes Schwarz, und Schatten erscheinen nur, wenn Sie diese Eigenschaft auf eine durchscheinende oder blickdichte Farbe setzen. Diese Eigenschaft kann nur auf einen Farbstring gesetzt werden: Muster und Verläufe sind für Schatten nicht gestattet. Durchscheinende Schatten bewirken die realistischsten Schatteneffekte, weil sie den Hintergrund durchscheinen lassen.
Die Eigenschaften shadowOffsetX
und shadowOffsetY
geben die x– und y-Verschiebung des Schattens an. Der Standard für beide Eigenschaften ist 0; dies lässt den Schatten unmittelbar unter der Zeichnung erscheinen, wo er nicht sichtbar ist. Wenn Sie beide Eigenschaften auf einen positiven Wert setzen, erscheinen die Schatten rechts unter dem gezeichneten Objekt, so, als schiene die Lichtquelle von links oben und außerhalb des Bildschirms auf das Canvas. Größere Verschiebungen führen zu größeren Schatten und erwecken den Eindruck, als schwebten die Objekte „höher“ über dem Canvas.
Die Eigenschaft shadowBlur
gibt an, wie weich die Kanten der Schatten sind. Der Standardwert ist 0 und bewirkt scharfe und nicht verschwommene Schatten. Größere Werte führen zu größerer Unschärfe bis zu einer Implementierungs-Definierten Obergrenze. Diese Eigenschaft ist ein Parameter für die Funktion Gaußscher Weichzeichner und keine Größe oder Länge in Pixeln.
Beispiel 1 zeigt den Code, mit dem Abbildung 1 erzeugt wurde, und führt alle vier Schatteneigenschaften vor.
Beispiel 1: Schattenattribute setzen
Die Eigenschaften shadowOffsetX
und shadowOffsetY
werden immer im Standardkoordinatensystem gemessen und von den Methoden rotate()
oder scale()
nicht beeinflusst. Nehmen Sie zum Beispiel an, Sie drehen das Koordinatensystem um 90 Grad, um Text vertikal zu zeichnen, und stellen dann das ursprüngliche Koordinatensystem wieder her, um einen horizontalen Text zu zeichnen. Die Schatten des vertikalen Texts und des horizontalen Texts sind in die gleiche Richtung ausgerichtet, und genau so sollte es wohl auch sein. Die Schatten von Figuren, die mit der gleichen Skalierung gezeichnet wurden, haben ebenfalls Schatten der gleichen „Höhe.“
Sparklines sind kleine Diagramme, die unmittelbar in den Textfluss eingebettet werden. Der Begriff „Sparkline“ wurde von Edward Tufte geprägt, der sie so beschreibt: Sparklines sind kleine, in einen Wort-, Zahl- oder Bildkontext eingebettete Grafiken mit hoher Auflösung. Sie sind datenintensive, einfach gestaltete und wortgroße Diagramme.
Mehr zu Sparklines erfahren Sie in Tuftes Buch Beautiful Evidence.
Beispiel 1 ist ein recht einfaches Modul mit nicht interaktivem JavaScript-Code zur Einbettung von Sparklines in Webseiten. Die Kommentare erklären, wie er funktioniert.
Beispiel 1: Sparklines mit dem <canvas>
-Tag
Alle Elemente der CSS-Klasse „sparkline
“ finden, ihren Inhalt als Zahlenfolge parsen und durch eine grafische Darstellung ersetzen.
Definieren Sie Sparklines mit Markup wie diesem:
Stylen Sie Sparklines mit CSS wie diesem:
Die Sparkline-Farbe ist der berechnete Stil der CSS-Eigenschaft color
.
Sparklines sind transparent, es schimmert also die Hintergrundfarbe durch.
Die Höhe der Sparklines wird durch das Attribut data-height
festgelegt, falls dieses definiert ist, andernfalls mithilfe des berechneten Werts für font-size
.
Die Breite der Sparklines wird durch das Attribut data-width
festgelegt, wenn es definiert ist, oder durch die Anzahl von Datenpunkten mal data-dx
, wenn das definiert ist, oder die Anzahl von Datenpunkten mal der Höhe durch 6.
Die Minimum- und Maximumwerte der y-Achse werden aus den Attributen data-ymin
und data-ymax
entnommen, wenn diese definiert sind, andernfalls werden sie aus den größten und kleinsten Datenwerten ermittelt.
Ein Geschäft macht eine Menge auf der Website, und das obere Management wünscht sich eine grafische Darstellung der Webstatistiken. Die Backend-Programmierer können die Daten in Echtzeit liefern, möchten aber erst einmal sehen, ob Sie eine Möglichkeit finden, die Grafik im Browser darzustellen. Also haben sie Sie mit Testdaten versorgt. Unser Ziel ist es, die Testdaten in etwas umzuwandeln, das an Abbildung 1 auf der folgenden Seite erinnert.
Es gibt viele Möglichkeiten, Diagramme auf einer Webseite zu zeichnen. Manche Entwickler verwenden immer Flash, aber das hat den Nachteil, dass es auf einigen mobilen Geräten wie dem iPad oder iPhone nicht funktioniert. Es gibt serverseitige Lösungen, die gut funktionieren, aber für Echtzeitdaten zu rechenintensiv sein könnten. Eine standardbasierte clientseitige Lösung wie das canvas
-Element ist eine ausgezeichnete Möglichkeit, solange wir darauf achten, dass es auch in älteren Browsern funktioniert. Sie haben bereits gesehen, wie Sie Quadrate zeichnen können. Aber um etwas Komplexes zu zeichnen, ist eine Menge mehr JavaScript erforderlich. Wir brauchen eine Bibliothek für die grafische Darstellung.
Von der Tatsache, dass HTML5 noch nicht überall verfügbar ist, haben sich die Entwickler der
Wir müssen lediglich einige JavaScript-Arrays anlegen, und schon zeichnet die Bibliothek das Diagramm im canvas
-Element.
Daten mit HTML beschreiben
Wir könnten die Werte für die Browserstatistiken fest in den Java-Script-Code schreiben, aber dann können nur Benutzer mit JavaScript diese Werte sehen. Stattdessen schreiben wir die Daten als Text auf die Seite. Anschließend können wir die Daten mit JavaScript einlesen und die Diagramm-Bibliothek damit füttern.
Wir verwenden HTML5-Datenattribute, um die Browsernamen und Prozentsätze zu speichern. Diese Informationen stehen zwar auch im Text, wir können aber so viel leichter programmgesteuert damit arbeiten, da wir keine Strings einlesen müssen.
Wenn Sie die Seite in Ihrem Browser öffnen oder einfach einen Blick auf Abbildung 2 werfen, sehen Sie, dass die Diagrammdaten hübsch lesbar angezeigt werden – auch ohne Diagramm. Das ist unser Alternativinhalt für mobile Geräte und andere Benutzer, für die entweder das canvas
-Element oder JavaScript nicht verfügbar sind.
Jetzt machen wir aus diesem Markup ein Diagramm.
Ein Balkendiagramm aus HMTL erstellen
Wir verwenden ein Balkendiagramm, daher brauchen wir die Balkendiagrammbibliothek und die Hauptbibliothek von RGraph. Um die Daten aus dem Dokument auszulesen, verwenden wir jQuery. Im head der HTML-Seite müssen wir die erforderlichen Bibliotheken laden.
Zum Erstellen des Diagramms müssen wir den Titel, die Beschriftungen und die Daten aus dem HTML-Dokument auslesen und an die RGraph-Bibliothek übergeben. RGraph erwartet sowohl für die Beschriftungen als auch für die Daten Arrays. Diese Arrays können wir mit jQuery schnell erstellen.
Als Erstes rufen wir in Zeile 2 den Text für die Kopfzeile ab. In Zeile 4 wählen wir anschließend alle Elemente mit dem Attribut data-name
aus. Wir verwenden die map
-Funktion von jQuery, um die Werte dieser Elemente in ein Array zu schreiben.
In Zeile 8 wenden wir dieselbe Logik an, um ein Array mit den Prozentzahlen anzulegen.
Nachdem wir die Daten gesammelt haben, ist es für RGraph ein Leichtes, unser Diagramm zu zeichnen.
Alternativen Inhalt anzeigen
Im Abschnitt Daten mit HTML beschreiben hätte ich das Diagramm auch zwischen dem öffnenden und dem schließenden canvas
-Tag platzieren können. Dadurch würden diese Elemente in Browsern versteckt, die das canvas
-Element unterstützen und in Browsern angezeigt, die es nicht unterstützen. Jedoch würde der Inhalt auch verborgen bleiben, wenn der Browser zwar das canvas
-Element unterstützt, der Benutzer aber JavaScript deaktiviert hat.
jQuery CSS contra CSS
In diesem Artikel haben wir unsere Stilregeln mit jQuery auf die Elemente angewendet, während wir sie erstellt haben. Eine Menge dieser Stilinformationen, wie etwa die Farben der Beschriftungen und der Balken, sollten aber in einem separaten Stylesheet untergebracht werden. Umso wichtiger ist das, wenn Sie die Stilregeln unabhängig vom Skript ändern möchten. Für einen Prototyp ist unser bisheriger Ansatz in Ordnung, aber für eine Produktionsversion sollten Sie Darstellung, Verhalten und Inhalt strikt voneinander trennen.
Wir lassen die Daten einfach außerhalb des canvas
-Elements stehen und verstecken sie anschließend mit jQuery, sobald wir überprüft haben, ob das canvas
-Element existiert.
Damit ist unser Diagramm fertig, außer für diejenigen, deren Browser das canvas
-Element nicht unterstützen.
Ausweichlösung
Beim Erstellen dieser Lösung haben wir bereits Ausweichlösungen für Barrierefreiheit und fehlendes JavaScript abgedeckt. Aber wir können ein alternatives Diagramm für Menschen erstellen, die zwar nicht über die Unterstützung für das canvas
-Element verfügen, aber JavaScript verwenden können.
Es gibt tonnenweise Diagramm-Bibliotheken, von denen jede die Daten anders erfasst.
Balkendiagramme sind aber letztlich nur Rechtecke mit einer bestimmten Höhe, und wir haben alle Daten auf der Seite, um dieses Diagramm von Hand aufzubauen.
In Zeile 2 verstecken wir die ungeordnete Liste, damit die Textwerte nicht angezeigt werden. Anschließend nehmen wir das Element mit den Diagrammdaten und wenden einige grundlegende CSS-Stilregeln darauf an. In Zeile 7 legen wir für die Positionierung des Elements relative
fest, wodurch wir unsere Balkendiagramme und Beschriftungen innerhalb dieses Containers absolut positionieren können.
Anschließend durchlaufen wir die Absätze der Aufzählungsliste (Zeile 11) und erstellen die Balken. In jedem Durchlauf für die Beschriftungen werden zwei div
-Elemente erstellt: eins für den Balken und eins für die Beschriftung, die wir darunter positionieren. Mit ein bisschen Mathematik und ein bisschen jQuery sind wir also in der Lage, das Diagramm neu zu erfinden. Auch wenn es nicht genauso aussieht, so ist es doch ähnlich genug, um unser Konzept zu bestätigen.
Anschließend müssen wir es nur noch in unsere canvas
-Prüfung einbauen:
Sie können die Alternativversion in Abbildung 3 sehen. Mit einer Kombination aus JavaScript, HTML und CSS haben wir clientseitig ein Balkendiagramm und statistische Informationen zur Browsernutzung für jede beliebige Plattform bereitgestellt. Die Verwendung des canvas
-Elements hat einen weiteren Vorteil – sie hat uns dazu gebracht, von Anfang an über eine Ausweichlösung nachzudenken, anstatt nachträglich etwas hineinzuquetschen. Das ist ein echtes Plus in puncto Barrierefreiheit.
Diese Methode bietet mit die größtmögliche Barrierefreiheit und Wandlungsfähigkeit,
Daten grafisch darzustellen. Sie können die visuelle Darstellung alternativ auch auf einfache Weise textbasiert anbieten. Auf diese Weise kann jeder die wichtigen Daten nutzen, die Sie bereitstellen.
Joe fragt … Warum haben wir nicht ExplorerCanvas ausprobiert?
Explorer Canvas, von dem wir im Abschnitt Ausweichlösung gesprochen haben, und RGraph funktionieren wirklich gut zusammen. Die Distribution von RGraph enthält sogar eine Version von ExplorerCanvas. Allerdings funktioniert diese Kombination nur mit dem Internet Explorer 8. Für einen IE 7 oder ältere Versionen müssen Sie als Alternative eine Lösung wie unsere verwenden. Ich möchte Sie ermuntern, ExplorerCanvas im Auge zu behalten, da es ständig weiterentwickelt wird. Sie sollten auch mal darüber nachdenken, selbst an der Entwicklung mitzuwirken, damit Sie ExplorerCanvas für Ihr Projekt nutzen können.
Die Zukunft
Nachdem Sie jetzt ein bisschen über die Funktionsweise des canvas
-Elements wissen, können Sie über weitere Einsatzmöglichkeiten nachdenken. Sie könnten damit ein Spiel, eine Benutzeroberfläche für einen Medienplayer oder eine tolle Bildergalerie schreiben. Sie brauchen lediglich etwas JavaScript und ein bisschen Fantasie, um mit dem Zeichnen loszulegen.
Im Moment hat Flash einen gewissen Vorsprung gegenüber dem canvas
-Element, weil es weiter verbreitet ist. Aber wenn HTML5 aufholt und canvas
für ein breiteres Publikum zur Verfügung steht, werden mehr und mehr Entwickler für einfache 2D-Grafiken im Browser begeistert darauf zurückgreifen. Das canvas
-Element erfordert keine zusätzlichen Plugins und benötigt weniger CPU-Leistung als Flash, insbesondere unter Linux und OS X. Zu guter Letzt bietet Ihnen das canvas
-Element einen Mechanismus, um 2D-Grafiken auch in Umgebungen zu erstellen, in denen kein Flash verfügbar ist. Da das canvas
-Element auf immer mehr Plattformen unterstützt wird, können Sie mit zunehmend mehr Geschwindigkeit und Funktionen rechnen und davon ausgehen, dass Sie von immer mehr Entwicklertools und Bibliotheken dabei unterstützt werden, außergewöhnliche Dinge zu tun.
Aber 2D-Grafiken sind noch nicht alles. Die canvas
-Spezifikation wird irgendwann auch 3D-Grafiken unterstützen, und die Browserhersteller werden die Hardwarebeschleunigung implementieren. Mit dem canvas
-Element wird es möglich sein, allein mit JavaScript verblüffende Benutzeroberflächen und packende Spiele zu schreiben.
Die Eigenschaft lineWidth
ist uns schon begegnet. Sie gibt die Breite der Striche an, die mit stroke()
und strokeRect()
gezogen werden. Neben lineWidth
(und natürlich strokeStyle
) gibt es drei weitere Grafikattribute, die sich auf Striche auswirken.
Der Standardwert für die Eigenschaft lineWidth
ist 1, und Sie können sie auf jede positive Zahl, auch Teilwerte, kleiner 1 setzen. (Striche, die weniger als ein Pixel breit sind, werden mit durchscheinenden Farben gezeichnet, damit sie weniger dunkel erscheinen als 1 Pixel breite Striche). Wenn Sie die Eigenschaft lineWidth
vollständig verstehen wollen, ist es wichtig, dass Sie sich Pfade als unendlich dünne, eindimensionale Linien vorstellen. Die von der Methode stroke()
gezeichneten Linien und Kurven sind über dem Pfad zentriert mit der halben lineWidth
auf beiden Seiten. Wenn Sie einen geschlossenen Pfad zeichnen wollen, dessen Linie nur außerhalb des Pfads erscheinen soll, ziehen Sie zunächst den Pfad und füllen ihn dann mit einer undurchsichtigen Farbe, um den Teil des Strichs zu verbergen, der innerhalb des Pfads erscheint. Soll die Linie nur innerhalb eines geschlossenen Pfads sichtbar sein, rufen Sie erst die Methoden save()
und clip()
auf und dann stroke()
und restore()
.
Die anderen drei Strichattribute wirken sich auf das Aussehen
der nicht verbundenden Enden von Pfaden und den Eckpunkten zwischen zwei Pfadsegmenten aus. Bei schmalen Linien haben diese kaum Auswirkungen, zeigen aber einen erheblichen Unterschied, wenn breite Linien gezogen werden. Zwei dieser Eigenschaften werden in Abbildung 1 illustriert. Die Abbildung zeigt den Pfad als dünne schwarze Linie und den Strich als einen grauen Bereich, der ihn umgibt.
Die Eigenschaft lineCap
gibt an, wie die Enden offener Teilpfade aussehen. Der Wert butt
(der Standard) bedeutet, dass die Linie am Endpunkt abrupt endet. Der Wert square
heißt, dass sich die Linie um die halbe Strichbreite über den Endpunkt erstreckt. Und der Wert round
bedeutet, dass die Linie mit einem Halbkreis (mit der halben Strichbreite als Radius) über den Endpunkt gezogen wird.
Die Eigenschaft lineJoin
gibt an, wie die Eckpunkte zwischen Teilpfadsegmenten verbunden werden. Der Standardwert ist miter und bedeutet, dass die äußeren Kanten der beiden Pfadsegmente verlängert werden, bis sie sich an einem Punkt treffen. Der Wert round
bedeutet, dass der Eckpunkt abgerundet wird, und der Wert bevel
heißt, dass der Anschlusspunkt mit einer geraden Linie abgeschnitten wird.
Die letzte Stricheigenschaft ist miterLimit
, die nur in Kraft tritt, wenn lineJoin
gleich miter ist. Wenn zwei Linien in spitzem Winkel zusammentreffen, kann die Eckverlängerung zwischen beiden sehr lang werden. Derart lange, spitze Verlängerungen sind visuell störend. Die Eigenschaft miterLimit
richtet eine Obergrenze für die Verlängerung ein. Wenn die Verlängerung an einem Eckpunkt länger als die Strichbreite mal miterLimit
ist, wird der Eckpunkt mit einer gerade abgeschnittenen Verbindung statt einer verlängerten Verbindung gezeichnet.
Auf den ersten Blick lautet die Antwort wohl eher Ja, denn die Einsatzmöglichkeiten für Text in Canvas sind begrenzt und beschränken sich auf das Formatieren und Positionieren von einfachen Zeichenketten. Fließtext mit automatischen Zeilenumbrüchen wird man ebenso vermissen wie Absatzformate oder auch die Selektierbarkeit bereits erstellter Texte.
Was bleibt, sind drei Attribute zur Bestimmung der Texteigenschaften, zwei Methoden zum Zeichnen von Texten und eine Methode zur Ermittlung der Textlänge einer Zeichenkette unter Berücksichtigung des aktuell eingestellten Formats. Das scheint nicht viel zu sein, doch auf den zweiten Blick wird klar, dass sich auch hinter vier Seiten Spezifikation gut durchdachte Details verstecken können.
Fonts
Die Definition des Font-Attributs verweist kurzerhand auf die CSS-Spezifikation und legt fest, dass context.font
der gleichen Syntax unterliegt wie die CSS-font
-Kurznotation.
Auf diese Weise können alle Font-Eigenschaften bequem in einem einzigen String spezifiziert werden. Tabelle zeigt die einzelnen Komponenten und listet deren mögliche Werte auf.
Eigenschaft | Werte |
---|---|
font-style |
*normal, italic, oblique |
font-variant |
*normal, small-caps |
font-weight |
*normal, bold, bolder, lighter 100, 200, 300, 400, 500, 600, 700, 800, 900 |
font-size |
xx-small, x-small, small, *medium, large, x-large, xx-large, larger, smaller em, ex, px, in, cm, mm, pt, pc, % |
line-height |
*normal, <Nummer>, em, ex, px, in, cm, mm, pt, pc, % |
font-family |
Schriftfamilie oder generische Schriftfamilie wie serif, sans-serif, cursive, fantasy, monospace |
Beim Zusammensetzen des font
-Attributs sind nur die Eigenschaften font-size
und font-family
zwingend anzugeben. Alle anderen können entfallen und nehmen dann ihre in der Tabelle durch einen Stern gekennzeichneten Standardwerte ein. Da Text in Canvas keine Zeilenumbrüche kennt, ist auch das Attribut line-height
ohne Wirkung und wird in jedem Fall ignoriert. Das bereinigte Muster beim Zusammensetzen der Komponenten lautet damit:
Bezüglich der font-family
gelten dieselben Regeln wie beim Definieren von Schriften in Stylesheets: Es dürfen beliebige Kombinationen von Schriftfamilien und/oder generischen Schriftfamilien ausgewiesen werden. Der Browser pickt sich dann die erste ihm bekannte Schrift aus dieser Prioritätenliste heraus.
Völlige Unabhängigkeit vom Browser beziehungsweise der jeweiligen Plattform und ihren Schriften
erzielt man durch die Verwendung von Webfonts. Einmal mittels @font-face
in einem Stylesheet eingebunden, stehen sie über den vergebenen Schriftnamen auch in Canvas als font-family
zur Verfügung.
Abbildung 1 zeigt kurze Beispiele gültiger CSS-font
-Attribute und deren Darstellung in Canvas.
Zu dem Zeitpunkt, als dieses Artikel geschrieben wurde, unterstützte kein Browser @font-face
ohne Probleme. So findet bei Firefox der Webfont Scriptina in der letzten Zeile nur dann den Weg in den Canvas, wenn er im HTML-Dokument zumindest einmal verwendet wird. Ebenso fehlt bei Firefox die korrekte Umsetzung von small-caps
, weshalb auch das vorletzte Beispiel nicht richtig angezeigt wird.
Horizontaler Textanfasspunkt
Zum Festlegen des Textanfasspunktes von Canvas-Texten in horizontaler Richtung dient das Attribut textAlign
.
Sind die Keywords left
, right
oder center
noch vom CSS-Attribut text-align
bekannt, handelt es sich bei start
und end
schon um CSS3-Erweiterungen, die die Laufrichtung des Textes in Abhängigkeit von der jeweiligen Sprache berücksichtigen. Schriften können nämlich nicht nur von links nach rechts, sondern wie im Fall von Arabisch oder Hebräisch, um nur zwei Beispiele zu nennen, auch von rechts nach links laufen.
Abbildung 2 präsentiert die horizontalen Textanfasspunkte für Schriften mit Textfluss ltr
(left to right) und rtl
(right to left) und verdeutlicht die Auswirkung der Richtungsabhängigkeit auf die Attribute start
und end
.
Info
Im Browser kann die Richtungsabhängigkeit eines Dokumentes über das globale Attribut document.dir
geändert werden:
Vertikaler Textanfasspunkt
Den Textanfasspunkt in vertikaler Richtung und damit die Grundlinie, an der alle Glyphen ausgerichtet werden, bestimmt das dritte und letzte textbezogene Attribut, textBaseline
.
Oben, Mitte, alphabetisch, unten, hängend und ideografisch lautet die Übersetzung der gültigen textBaseline
-Keywords, wobei die ersten vier davon wohl selbsterklärend sind. Eine hängende Grundlinie benötigen Devanagari, Gurmukhi und Bengali, drei indische Schriften, in denen die Sprachen Sanskrit, Hindi, Marathi, Nepali beziehungsweise Panjabi und Bengalisch geschrieben werden. Zur Gruppe der ideografischen Schriften zählen Chinesisch, Japanisch, Koreanisch und Vietnamesisch.
Text zeichnen und messen
Sind Font und Anfasspunkt erst einmal festgelegt, muss nur noch der Text gezeichnet werden. Ähnlich wie bei Rechtecken können Sie sich für eine Füllung und/oder Randlinie entscheiden und sogar die erlaubte Breite des Textes durch einen optionalen Parameter maxwidth
beschränken.
Zum Messen der Dimension eines Textes steht zu guter Letzt noch die Methode measureText()
zur Verfügung, die zumindest die Breite unter Berücksichtigung des aktuellen Formats ermitteln kann. Im Beispiel aus Abbildung 3 wurde der Wert rechts unten (759) auf diese Art berechnet.
Die Bestimmung von Höhe oder Ursprungspunkt des Hüllrechtecks (Bounding-Box) ist zum derzeitigen Zeitpunkt nicht möglich, könnte aber in einer zukünftigen Version der Spezifikation ebenso implementiert werden wie mehrzeiliges Text-Layout. Vielversprechend hört sich die letzte Anmerkung im Textkapitel der Canvas-Spezifikation an, wonach in Zukunft durchaus auch Fragmente von Dokumenten (z. B. Absätze mit Formatierungen) über CSS den Weg nach Canvas finden könnten.
Nicht erst in der Zukunft, sondern bereits jetzt bietet die Canvas-API
eine Vielzahl an Möglichkeiten, um mit rasterbasierten Formaten in Canvas zu arbeiten. Neben dem Einbinden von Bildern und Videos besteht sogar die Möglichkeit, auf jedes einzelne Pixel der Canvas-Fläche sowohl lesend als auch schreibend zuzugreifen.
Text zeichnen Sie in ein Canvas üblicherweise mit der Methode fillText()
. Sie zeichnet Text in der Farbe (oder mit dem Verlauf oder dem Muster), die die Eigenschaft fillStyle
definiert. Für besondere Effekte bei großen Textgrößen können Sie die Methode strokeText()
nutzen, um den Umriss der einzelnen Textzeichen zu ziehen (ein Beispiel für umrissenen Text sehen Sie in Abbildung 4). Beide Methoden erwarten als erstes Argument den zu zeichnenden Text und als zweites und drittes Argument die x– und y-Koordinaten für den Text. Keine dieser Methoden wirkt sich auf den aktuellen Pfad oder den aktuellen Punkt aus.
Die Eigenschaft font
gibt die für den Text zu nutzende Schriftart an. Der Wert sollte ein String mit der gleichen Syntax wie für das CSS–font
-Attribut sein. Einige Beispiele:
Die Eigenschaft textAlign
gibt an, wie Text horizontal in Bezug auf die an fillText()
oder strokeText()
übergebene x-Koordinate ausgerichtet wird. Die Eigenschaft textBaseline
legt fest, wie Text vertikal in Bezug auf die y-Koordinate ausgerichtet wird. Abbildung 4 illustriert die erlaubten Werte für diese Eigenschaften. Die dünne Linie neben dem Text ist die Basislinie, und das kleine Rechteck markiert den an fillText()
übergebenen Punkt (x,y).
Der Standardwert für textAlign
ist start
. Beachten Sie, dass bei Links-rechts-Text die Ausrichtung start der Ausrichtung left
entspricht und die Ausrichtung end der Ausrichtung right
. Setzen Sie das dir
-Attribut des <canvas>
-Tags auf rtl
(right-to-left), entspricht die Ausrichtung start
der Ausrichtung right
und die Ausrichtung end
der Ausrichtung left
.
Der Standardwert für textBaseline
ist alphabetic
, dieser ist für das lateinische Alphabet und ähnliche Schriftsysteme geeignet. Der Wert ideographic
wird für ideografische Schriften wie das Chinesische und Japanische genutzt. Der Wert hanging
ist für Devangari und ähnliche Schriftsysteme (die für viele in Indien gebräuchliche Sprachen verwendet werden) geeignet. Die Basislinien top
, middle
und bottom
sind rein geometrische Basislinien, die auf dem „em-Quadrat“ der Schrift basieren.
fillText()
und strokeText()
akzeptieren ein optionales viertes Argument. Wird dieses angegeben, gibt es die maximale Breite des anzuzeigenden Texts an. Würde der Text, wenn er mit dem aktuellen Wert der Eigenschaft font
gezeichnet wird, breiter werden als der angegebene Wert, wird er passend gemacht, indem er skaliert oder mit einer schmaleren oder kleineren Schrift gezeichnet wird.
Wenn Sie den Text selbst messen wollen, bevor Sie ihn zeichnen, übergeben Sie ihn an die Methode measureText()
. Diese Methode liefert ein TextMetrics-Objekt, das die Maße des Texts angibt, wenn er auf Basis des aktuellen font
-Werts gezeichnet wird. Als dies geschrieben wurde, war die einzige „Metrik“ im TextMetrics-Objekt die Breite. Den Raum, den ein String auf dem Bildschirm einnimmt, fragen Sie folgendermaßen ab:
Canvas-Transformationen manipulieren direkt das Koordinatensystem. So wird beim Verschieben eines Rechtecks nicht nur das Element selbst bewegt, sondern gleich das gesamte Koordinatensystem neu gesetzt und erst dann das Rechteck gezeichnet. Die drei einfachen Grundtransformationen sind scale()
, rotate()
und translate()
.
Beim Skalieren über scale()
benötigen wir zwei Multiplikanden als Argumente für die Größenänderung der x- und y-Dimension, Rotationen mit rotate()
verlangen den Drehwinkel im Uhrzeigersinn in Radiant, und Verschiebungen durch translate()
definieren Offsets in x– und y-Richtung in Pixel. Bei Kombination der Methoden müssen die einzelnen Transformationen in umgekehrter Reihenfolge ausgeführt werden – aus Sicht des JavaScript-Codes also quasi von hinten nach vorne gelesen werden:
Um zuerst zu skalieren und dann zu rotieren, schreiben wir:
Wollen wir zuerst rotieren und dann verschieben, lautet der JavaScript-Code:
Vorsicht ist in jedem Fall geboten, wenn Rotationen im Spiel sind, denn diese werden immer mit dem Ursprungspunkt 0/0 als Drehmittelpunkt ausgeführt. Als Faustregel für die Reihenfolge der JavaScript-Aufrufe gilt: rotate()
ist meist die letzte Aktion. Abbildung 1 zeigt ein Beispiel, das alle drei Grundmethoden verwendet und unser Yosemite-Bild einmal aus anderer Perspektive, quasi als Sprungschanze darstellt.
Werfen wir einen kurzen Blick auf den ebenso kurzen Quellcode für Abbildung 1:
Sobald das Bild geladen ist, definieren wir den Rotationswinkel rotate
mit 15°, die Start- und Endskalierungen scaleStart
mit 0.0 sowie scaleEnd
mit 4.0 und daraus abgeleitet das Inkrement für die Skalierung scaleInc
mit dem Ziel, innerhalb einer ganzen Umdrehung die Endskalierung 4.0 zu erreichen. In der for
-Schleife rotieren wir dann das Bild gegen den Uhrzeigersinn jeweils um 15°, skalieren es von 0.0 bis 4.0 und setzen seine linke obere Ecke auf die Koordinate 540/260.
Offen bleibt, was es mit der Methode setTransform()
am Ende der for
-Schleife auf sich hat und um wen es sich bei dem Skispringer handelt, der sich wagemutig vom Taft Point in den Abgrund stürzt. Ersteres werden wir gleich klären, Zweiteres wird sich erst bei einem Blick in den Quellcode des Beispiels auflösen.
Neben den drei Grundtransformationen scale()
, rotate()
und translate()
stellt Canvas noch zwei weitere Methoden zur Veränderung des Koordinatensystems und damit der sogenannten Transformationsmatrix bereit: transform()
und das bereits in dem Code angesprochene setTransform()
:
Beiden gemeinsam sind die Argumente m11, m12, m21, m22, dx und dy, die folgende Tranformationseigenschaften repräsentieren:
Komponente | Inhalt |
---|---|
m11 | Skalierung in x-Richtung |
m12 | Horizontaler Scherfaktor |
m21 | Vertikaler Scherfaktor |
m22 | Skalierung in y-Richtung |
dx | Verschiebung in x-Richtung |
dy | Verschiebung in y-Richtung |
Der Hauptunterschied zwischen beiden liegt darin, dass transform()
die zum Zeitpunkt des Aufrufs bestehende Transformationsmatrix durch Multiplikation weiter verändert, wohingegen setTransform()
die bestehende Matrix mit der neuen überschreibt.
Die drei Grundmethoden könnten ebenso als Attribute für transform()
oder setTransform()
formuliert werden und sind im Grunde genommen nichts anderes als bequeme Kürzel für entsprechende Matrixtransformationen. Tabelle zeigt diese Attribute und listet noch weitere nützliche Matrizen zum Spiegeln (flipX/Y
) und Neigen (skewX/Y
) auf. Gradangaben beim Neigen erfolgen wieder in Radiant.
Methode | Transformationsmatrix (m11, m12, m21, m22, dx, dy) |
---|---|
scale(x, y) |
x,0,0,y,0,0 |
rotate(angle) |
cos(angle),sin(angle),-sin(angle), cos(angle),0,0 |
translate(x, y) |
1,0,0,1,x,y |
flipX() |
-1,0,0,1,0,0 |
flipY() |
1,0,0,-1,0,0 |
skewX(angle) |
1,0,tan(angle),1,0,0 |
skewY(angle) |
1,tan(angle),0,1,0,0) |
Bevor wir uns einem ausführlichem Beispiel zuwenden, muss noch erwähnt werden, dass sowohl getImageData()
als auch putImageData()
gemäß Spezifikation von Transformationen unabhängig sind. Der Aufruf getImageData
(0,0,100,100) greift immer auf das 100 x 100 Pixel große Quadrat in der linken oberen Ecke des Canvas zu, egal ob das Koordinatensystem verschoben, skaliert oder rotiert wurde. Ebenso verhält es sich bei putImageData
(imagedata,0,0), wo wiederum die linke obere Ecke als Anfasspunkt zum Auftragen des Inhalts von imagedata
dient.
Widmen wir uns jetzt dem angekündigten Beispiel, in dem wir alle gelernten Methoden zum Transformieren noch einmal anwenden. Abbildung 2 zeigt das ansprechende Resultat – eine Collage von drei Bildausschnitten unseres Yosemite-Bildes mit Spiegeleffekt im Pseudo-3D-Raum.
Beginnen wir mit dem Ausstanzen der drei quadratischen Ausschnitte für Taft Point, Merced River und El Capitan. Das Ergebnis speichern wir im Array icons
.
Das Zuschneiden und Anpassen der unterschiedlich großen Ausschnitte erledigt die Funktion clipIcon()
. In ihr wird zuerst ein neuer In-memory-Canvas mit 320 x 320 Pixeln Größe erzeugt, auf den wir dann mit drawImage()
das entsprechend verkleinerte (oder vergrößerte) Icon kopieren und mit einem 15 Pixel breiten, weißen Rahmen versehen.
Für jeden dieser drei Ausschnitte erzeugen wir in einem zweiten Schritt den Spiegeleffekt und speichern ihn im Array effects
.
Die Hauptarbeit findet dabei in der Funktion createReflection()
statt, deren leicht modifizierter Code einem Posting in Charles Yings blog about art, music, and the art of technology über den CoverFlow-Effekt des iPhones entstammt.
In createReflection()
wird zuerst über einen weiteren In-memory-Canvas die untere Hälfte des in icon
übergebenen Bildausschnitts umgeklappt. Erinnern wir uns an die Kürzel für Transformationsmatrizen, könnten wir das Umklappen mit der Matrix für flipY()
realisieren. In diesem Fall verwenden wir allerdings eine weitere Variante zum Spiegeln, und zwar jene über die Methode scale()
. Dabei entspricht scale(1,-1)
der Methode flipY()
und scale(-1,1)
der Methode flipX()
. Der Fade-out
-Effekt entsteht durch eine Gradiente von semitransparentem Weiß zu opakem Weiß, die mithilfe der Compositing Methode destination-out
über das Icon gelegt wird.
Damit sind die einzelnen Bildausschnitte definiert, und wir können mit dem Zeichnen beginnen. Eine Gradiente von Schwarz nach Weiß mit beinahe Schwarz in der Hälfte des Verlaufs erweckt den Eindruck eines Raums, in dem wir die drei Ausschnitte anschließend platzieren.
Am einfachsten können wir das mittlere Bild vom Merced River über setTransform()
positionieren und dann mit einem Spiegeleffekt zeichnen.
Die Breite des El-Capitan-Bildes skalieren wir für einen besseren 3D-Eindruck um den Faktor 0.9, neigen das Resultat mit der Matrix für skeyY()
um 10° nach unten und positionieren das Resultat rechts von der Mitte.
Etwas komplizierter ist dann das Zeichnen des Taft-Point-Bildes links, denn nachdem beim Neigen die linke obere Ecke des Ausschnitts den Ankerpunkt bildet, müssen wir um 10° nach oben neigen und das Resultat dann wieder nach unten schieben. Der Satz des Pythagoras hilft uns beim Ermitteln des nötigen dy-Wertes: Er ergibt sich als Tangens des Drehwinkels in Radiant mal der Länge der Ankathete, die der Breite des Icons entspricht, also Math.tan(0.175)*320
.
Zusätzlich muss noch die Skalierung der Bildbreite um 0.9 durch Verschiebung um 320*0.1 nach rechts ausgeglichen werden.
Damit wäre unser bisher schwierigstes Canvas-Beispiel geschafft – das Ergebnis kann sich sehen lassen und verlangt geradezu danach, im JPEG- oder PNG-Format gespeichert zu werden.
Beispiel demonstriert die Macht von Koordinatensystemtransformationen, indem mit den Methoden translate()
, rotate()
und scale()
rekursiv ein Koch-Schneeflocken-Fraktal gezeichnet wird. Die Ausgabe dieses Beispiels sehen Sie in Abbildung 1, das Koch-Schneeflocken mit 0, 1, 2, 3 und 4 Rekursionsebenen zeigt.
Der Code, der diese Figuren erzeugt, ist elegant, aber sein Gebrauch rekursiver Koordinatensystemtransformationen macht ihn recht schwer verständlich. Selbst wenn Sie nicht allen Feinheiten folgen können, sollten Sie beachten, dass der Code nur einen einzigen Aufruf der Methode lineTo()
enthält. Jedes Liniensegment in Abbildung 1 wird sögezeichnet:
Der Wert der Variablen len ändert sich während der Ausführung des Programms nicht. Die Länge der Liniensegmente wird alsödurch Translations-, Rotations- und Skalierungsoperationen bestimmt.
Beispiel: Eine Koch-Schneeflocke mit Transformationen
Die Methode isPointInPath()
ermittelt, ob ein angegebener Punkt in den aktuellen Pfad (oder auf seine Grenzen) fällt, und liefert true
, wenn das der Fall ist, andernfalls false
. Der Punkt, den Sie dieser Methode übergeben, befindet sich im Standardkoordinatensystem und wird nicht transformiert. Das macht die Methode zur Treffererkennung geeignet, was bedeutet, zu prüfen, ob ein Mausklick über einer bestimmten Figur erfolgte.
Sie können die clientX
– und clientY
-Felder eines MouseEventObjekts
allerdings nicht direkt an isPointInPath()
übergeben. Erst müssen die Koordinaten des Maus-Events so übersetzt werden, dass sie sich auf das Canvas-Element und nicht auf das Window-Objekt beziehen. Außerdem müssen die Koordinaten des Maus-Events entsprechend skaliert werden, wenn die Bildschirmgröße des Canvas nicht den tatsächlichen Maßen entspricht. Beispiel 1 zeigt eine Hilfsfunktion, mit der Sie prüfen können, ob ein Maus-Event über dem aktuellen Pfad eintrat.
Beispiel 1: Prüfen, ob ein Maus-Event über dem aktuellen Pfad liegt
Folgendermaßen könnten Sie die Funktion hitpath()
in einem Event-Handler nutzen:
Statt einer pfadbasierten Treffererkennung können Sie auch getImageData()
nutzen, um zu prüfen, ob das Pixel unter der Maus gezeichnet wurde. Ist das Pixel (bzw. sind die Pixel) vollständig transparent, wurde an diesem Punkt nicht gezeichnet. Dann liegt also kein Treffer vor. Beispiel 2 zeigt, wie Sie eine derartige Treffererkennung umsetzen können.
Beispiel 2: Prüfen, ob ein Maus-Event über einem gemalten Pixel erfolgte
Formulare mit HTML5
Ein Vorteil der neuen Elemente und Attribute bei Formularen ist, dass die Eingabe für Benutzer erleichtert wird (zum Beispiel wird ein Kalender zum Eingeben eines Datums angeboten). Ein weiterer großer Vorteil ist die Möglichkeit, den Formular-Inhalt bereits vor dem Abschicken überprüfen zu können und den Benutzer auf mögliche Fehler hinzuweisen. Jetzt werden Sie vielleicht sagen, dass das ein alter Hut ist, denn diese Form der Überprüfung kennt man bereits seit vielen Jahren. Das stimmt, aber bisher musste dieser Schritt immer mithilfe von selbst programmiertem JavaScript-Code erledigt werden. Durch jQuery und ähnliche Bibliotheken wurde diese Aufgabe zwar deutlich erleichtert und der Code wartbarer, aber es bleibt die Abhängigkeit von einer externen Bibliothek.
Mit HTML5 ändert sich das grundlegend: Sie definieren die Vorgaben für die Eingabefelder in HTML, und der Browser überprüft, ob die Felder korrekt ausgefüllt wurden. Das ist ein großer Schritt vorwärts, der viele redundante Zeilen JavaScript-Code unnötig macht. Ein Minimalbeispiel wird Sie überzeugen:
Was passiert, wenn Sie das Formular in dem oben abgedruckten Listing ohne die Angabe einer E-Mail-Adresse abschicken, sehen Sie in Abbildung 1. Opera zeigt die Fehlermeldung: Sie müssen einen Wert eingeben. Wenn Sie die Opera-Benutzeroberfläche auf eine andere Sprache eingestellt haben, so erscheint diese Meldung in der entsprechenden Sprache. Natürlich kann man diese Fehlermeldungen auch noch mit JavaScript anpassen, mehr dazu erfahren Sie etwas später.
Damit aber noch nicht genug:
Da das Feld vom Typ email
definiert ist, meldet Opera auch einen Fehler, wenn keine gültige E-Mail-Adresse eingegeben wurde (vergleiche Abbildung 2).
Webkit-basierte Browser wie Google Chrome oder Safari unterstützen aktuell zwar die Überprüfung, geben aber keine Fehlermeldung aus. Sie umrahmen das ungültige Feld und positionieren den Cursor innerhalb des Feldes, um zumindest anzuzeigen, dass irgendetwas nicht stimmt.
Info
Bei aller Euphorie über die clientseitige Überprüfung von Formular-Eingaben dürfen Sie nicht vergessen, dass dieser Schritt die serverseitige Kontrolle nicht überflüssig machen kann. Ein potenzieller Angreifer kann diese Mechanismen mit wenig technischem Aufwand umgehen.
Das invalid
-Event
Bei der Überprüfung des Formulars wird für Elemente, die einen ungültigen Inhalt haben, das Event invalid
ausgelöst. Das können wir uns zunutze machen und individuell auf fehlerhafte Werte reagieren.
Nachdem die Seite geladen ist, wird eine Liste aller input
-Elemente generiert. An jedes Element wird anschließend ein Event-Listener angehängt, der den Fehlerfall behandelt. Im vorliegenden Beispiel wird ein alert
-Fenster geöffnet, und das Element erhält einen rot gepunkteten Rahmen. Für den Text im alert
-Fenster wird die Beschriftung des input
-Elements verwendet.
Bei Formularen mit vielen Eingabefeldern ist diese Vorgehensweise nicht ideal. Der Anwender muss für jede fehlerhafte Eingabe die OK-Schaltfläche anklicken und anschließend im Formular das betreffende Feld suchen und erneut ausfüllen. Manchmal wäre es günstiger, wenn der Anwender sofort nach dem Ausfüllen eine Benachrichtigung bekäme, sollte das Feld einen ungültigen Inhalt enthalten. Das wollen wir im nächsten Abschnitt probieren.
Die checkValidity
-Funktion
Um die Überprüfung eines input
-Elements auszulösen, wird die checkValidity
-Funktion für dieses Element aufgerufen. Was normalerweise passiert, wenn das Formular abgeschickt wird, kann man aber auch „von Hand“ starten:
Gibt man eine ungültige E-Mail-Adresse ein und verlässt man das Eingabefeld (entweder mit der Tabulator-Taste oder durch einen Mausklick auf eine andere Stelle im Browser), so meldet der Browser (zurzeit zumindest Opera) den Fehler unmittelbar (vergleiche Abbildung 2). Noch eleganter wird die Fehlerbehandlung, wenn wir an das onchange
-Event von allen input
-Elementen eine Funktion zum Überprüfen der Eingabe hängen.
In der bereits bekannten Schleife über alle input
-Elemente wird als Erstes kontrolliert, ob das Element für eine Überprüfung zur Verfügung steht. Enthält willValidate
nicht den Wert true
, wird die Schleife mit dem nächsten Element fortgesetzt. Andernfalls wird das onchange
-Event mit einer anonymen Funktion belegt, in der die checkValidity
-Funktion aufgerufen wird. this
bezieht sich innerhalb der anonymen Funktion auf das input
-Element. Schlägt die Gültigkeitsprüfung fehl, so wird das Element mit einer roten Umrandung versehen; im anderen Fall wird der Hintergrund des Elements hellgrün eingefärbt. Das Zurücksetzen der Hintergrundfarbe beziehungsweise des Rahmens auf eine leere Zeichenkette ist notwendig, damit der Browser bei einer richtigen Eingabe nach einer falschen Eingabe die Formatierung wieder auf den Standardwert stellt. Abbildung 3 zeigt, wie die checkValidity
-Funktion einen Fehler bei der Zeiteingabe anmahnt.
Wenn Sie die Fehlerbehandlung lieber noch interaktiver gestalten möchten,
können Sie statt des onchange
-Events auch das in HTML5 neue oninput
-Event verwenden. Anders als onchange
, das beim Verlassen des Feldes gestartet wird, kommt oninput
nach jedem veränderten Zeichen zum Einsatz. Was bisher etwas mühsam mithilfe der Tastatur-Events keyup
beziehungsweise keydown
programmiert wurde, übernimmt jetzt das oninput
-Event. Ein weiterer Vorteil von oninput
ist, dass der Event-Listener nur einmal an das ganze Formular angehängt werden muss und nicht an jedes einzelne input
-Element. Für das vorangegangene Beispiel könnte man damit auf den gesamten JavaScript-Code verzichten und die Formular-Definition wie folgt ändern:
Man verzichtet damit zwar auf das Verändern von Rahmen und Hintergrundfarbe, verkürzt aber auch den Quelltext deutlich. Das unmittelbare Reagieren auf einen Tastendruck kann in manchen Fällen sehr hilfreich sein, beim Ausfüllen eines Formularfelds reicht es aber meist, wenn der Inhalt erst dann überprüft wird, wenn das Feld vollständig ausgefüllt wurde.
Fehlerbehandlung mit setCustomValidity()
Wenn Ihnen all die bisher vorgestellten Möglichkeiten zur Fehlerbehandlung noch nicht ausreichend erscheinen, können Sie sich auch selbst eine Funktion zur Überprüfung des Inhalts programmieren. Im folgenden Beispiel wird ein Eingabefeld vom Typ email
definiert, wodurch der Browser schon die Überprüfung der gültigen E-Mail-Adresse übernimmt. Zusätzlich möchten wir aber noch drei E-Mail-Domains ausschließen.
Jedes Element im Array invalidMailDomains
wird mit dem Wert des input
-Elements verglichen. Die JavaScript-Funktion match()
arbeitet mit regulären Ausdrücken, weshalb wir an den Domain-Namen noch ein $
-Zeichen anhängen, das das Ende der Zeichenkette spezifiziert. Stimmen die Zeichenketten überein, so wird die setCustomValidity
-Funktion aufgerufen und ihr die entsprechende Fehlermeldung übergeben. Handelt es sich nicht um einen Domain-Namen aus dem Array, wird setCustomValidity()
mit einer leeren Zeichenkette aufgerufen. Intern wird dadurch die Variable validationMessage
an das input
-Element angehängt, die Opera anschließend auch korrekt anzeigt (vergleiche Abbildung 4). Der abschließende Aufruf der checkValidity
-Funktion löst die Überprüfung aus und führt zu der eben erwähnten Fehlermeldung.
Zusammenfassung der Gültigkeitsprüfungen
Tabelle zeigt eine Auflistung aller input
-Attribute beziehungsweise Validierungsfunktionen, die bei der Gültigkeitsüberprüfung zur Verfügung stehen, und die Szenarien, in denen sie auftreten.
Attribut/Funktion | Problem |
---|---|
required |
Es wurde kein Wert für das Feld eingegeben. |
type=email, url |
Der eingegebene Wert entspricht nicht dem verlangten Typ. |
pattern |
Der eingegebene Wert entspricht nicht dem geforderten Muster. |
maxlength |
Der eingegebene Wert ist länger als erlaubt. |
min, max |
Der eingegebene Wert ist zu klein bzw. zu groß. |
step |
Die verlangte Schrittweite beim eingegebenen Wert wurde nicht eingehalten. |
setCustomValidity() |
Die zusätzlich aufgestellten Kriterien für dieses Feld wurden nicht erfüllt. |
Oder doch nicht prüfen? formnovalidate
Nun, da wir uns so ausführlich mit der Fehlerbehandlung beschäftigt haben, folgt die Erklärung, wie man sich an all den Regeln vorbeimogeln kann: mit dem Attribut formnovalidate
. Im ersten Moment erscheint es vielleicht ein bisschen merkwürdig, all die mühevoll definierten Regeln einfach so beiseite zu lassen und das Formular auch ohne eine Prüfung abzuschicken. Die Spezifikation enthält dazu eine kurze Erklärung, die das Rätsel schnell löst. Der typische Anwendungsfall für das Überspringen der Prüfung ist ein Formular, das der Anwender nicht auf einmal ausfüllen kann oder will. Dadurch, dass man das formnovalidate
-Attribut einer submit
-Schaltfläche hinzufügt, kann der bisher eingegebene Inhalt zwischengespeichert werden.
Info
Beim Abschicken des Formulars mit formnovalidate
werden die bereits ausgefüllten Felder an den Server gesendet. Um ein mögliches Zwischenspeichern muss sich die Server-Anwendung kümmern.
Stellen Sie sich vor, Sie füllen ein Support-Formular für Ihre defekte Digitalkamera aus.
Nachdem Sie ausführlich alle Angaben zu dem aufgetretenen Fehler gemacht haben, wird auf der Internet-Seite nach der Seriennummer der Kamera gefragt. Da die Sie die Kamera aber gerade nicht zur Hand haben und die mühevoll eingegebenen Informationen nicht verlieren möchten, klicken Sie auf die ZWISCHENSPEICHERN-Schaltfläche und können sich in Ruhe auf die Suche nach der Kamera begeben. Diese Schaltfläche wird wie folgt definiert:
Im abschließenden Beispiel wird die Idee mit dem Support-Formular vollständig ausgearbeitet.
Beispiel: Ein Support-Formular
In diesem Beispiel werden die bisher vorgestellten neuen Elemente und Attribute in einem Formular verwendet. Das Formular könnte, in einer erweiterten Form, auf der Webseite eines Elektronik-Verkäufers Verwendung finden.
Zu Beginn werden persönliche Informationen vom Klienten abgefragt (in diesem Beispiel nur der Name, eine E-Mail-Adresse, eine Telefon- und eine Faxnummer). Der zweite Teil des Formulars betrifft die technischen Daten und den Defekt des Geräts. Im untersten Teil der Webseite wird ein Fortschrittsbalken angezeigt, der den Anwender aufmuntern soll, das Formular fertig auszufüllen (vergleiche Abbildung 5).
Der HTML-Code für das Formular beginnt mit dem Laden einer externen JavaScript-Datei und dem bereits bekannten Aufruf window.onload
.
Die initEventListener
-Funktion läuft über alle input
-Elemente und belegt das onchange
-Event mit einer anonymen Funktion, die das entsprechende Element auf seine Gültigkeit überprüft.
Der Event-Listener wird nur dann angehängt, wenn das Element eine Möglichkeit der Überprüfung hat. Im vorliegenden Beispiel haben die beiden Schaltflächen zum Absenden beziehungsweise zum Zwischenspeichern keine Überprüfungsmöglichkeit und bekommen daher kein onchange
-Event. Wie im vorangegangenen Abschnitt schon erklärt wurde, ist die Überprüfung der einzelnen Formularfelder nach dem Ausfüllen des Feldes angenehmer als die Überprüfung des gesamten Formulars mit dem oninput
-Event.
Um die Benutzerfreundlichkeit des Formulars zu verbessern,
wollen wir die als required
markierten Elemente hervorheben, damit dem Anwender sofort klar wird, welches die wichtigen Felder sind. Glücklicherweise müssen wir dazu nicht jedes Element mit einem extra Stil versehen, CSS3 bringt den neuen Selektor :required
mit, der genau für diesen Fall gedacht ist. Die folgende Anweisung rahmt alle vorgeschriebenen Elemente mit oranger Farbe ein.
Die Definition der einzelnen input
-Felder birgt keine großen Überraschungen. E-Mail-Adresse und Telefonnummer haben ihre eigenen Typen und sind vorgeschrieben; das Datum, an dem der Defekt auftrat, ist vom Typ date
und kann daher mit einem Kalenderfenster ausgewählt werden. Das zweispaltige Layout im oberen Teil der Webseite wird mit div
-Elementen erreicht, die nebeneinander liegen. Trotzdem möchten wir, dass Anwender, die die Tabulatortaste zum Weiterspringen verwenden, das Formular von oben nach unten ausfüllen und nicht, der HTML-Logik folgend, zuerst die linke und dann die rechte Spalte.
Erreicht wird das mithilfe des tabindex
-Attributs, wodurch ein Drücken der Tabulatortaste in einem Feld den Cursor auf das Feld mit dem nächsthöheren tabindex
-Wert stellt.
Das meter
-Element wird mit einem Maximalwert von 200 initialisiert, exakt dem Wert, der im title
-Attribut der textarea
als Maximum angegeben ist. Gibt ein Anwender mehr als die vorgeschriebenen Zeichen ein, wird das meter
-Element rot und warnt vor dem zu langen Text. Der Browser wird den zu langen Text aber trotzdem abschicken, da wir die textarea
nicht limitiert haben. Es handelt sich hier also mehr um einen Hinweis, als um eine strikte Vorgabe. Die JavaScript-Funktion zum Aktualisieren der meter
-Elemente lautet updateTAMeters()
und wird für alle textareas
ausgeführt:
Der Vorteil der Schleife ist, dass wir nun beliebig viele textarea
-Elemente hinzufügen können, und sofern sie ein meter
-Element besitzen, werden diese automatisch aktualisiert. Um das zu erreichen, müssen wir zu einem Trick aus der DOM-Kiste greifen: Die in dem oben stehenden Code fett gedruckte Zuweisung greift auf die DOM-Funktion nextSibling
zu, einen Verweis auf das folgende Element. Führen wir uns zum besseren Verständnis noch einmal den HTML-Code für das Textfeld und den Status-Balken vor Augen. Das textarea
-Element ist von einem label
-Element eingeschlossen, auf das das gesuchte meter
-Element folgt. Um vom textarea
-Element auf das meter
-Element zu kommen, verwenden wir die labels
-Eigenschaft des Textfeldes. Dabei handelt es sich um ein NodeList-Array, von dem uns das erste Element (also das mit dem Index 0) interessiert, weil das darauffolgende Element (der nextSibling
) das meter
-Element ist.
Bei genauerer Betrachtung ist die Vorgehensweise also gar nicht so kompliziert,
sie birgt aber ihre Tücken. Sollte sich zwischen dem abgeschlossenen label
-Element und dem meter
-Element ein Leerzeichen oder ein Zeilenumbruch verirren, dann funktioniert unsere Status-Anzeige nicht mehr. Der next-Sibling
ist dann nämlich ein Text-Element, und in der for
-Schleife erreichen wir das meter
-Element nicht mehr.
Als Nächstes wollen wir uns um die Fortschrittsanzeige am Ende des Formulars kümmern. Leicht zu erraten war, dass es sich dabei um ein progress
-Element handelt, spannender wird, wie sich das Aktualisieren dieses Elements in JavaScript elegant ausdrücken lässt. Zuerst sehen Sie hier den HTML-Code für das Element:
Das progress
-Element bekommt eine id
, einen Anfangswert von 0 (value
) und einen negativen tabindex
zugewiesen, was dazu führt, dass das Element nie mit der Tabulator-Taste angesprungen wird. Um alles Weitere kümmert sich die JavaScript-Funktion updateProgress()
.
Da sich der Fortschrittsbalken nur auf diese Elemente beziehen soll, die unbedingt eingegeben werden müssen, verwenden wir die Funktion querySelectorAll()
und übergeben ihr die Zeichenkette :required
. Als Ergebnis erhalten wir eine NodeList, die nur Elemente beinhaltet, die das required
-Attribut gesetzt haben. Anschließend läuft eine Schleife über diese Elemente und überprüft, ob das value
-Attribut nicht mit einer leeren Zeichenkette übereinstimmt. Trifft diese Bedingung zu (das bedeutet, es wurde schon ein Wert eingegeben), so wird die Zählervariable count
um einen Wert erhöht. Abschließend wird der Maximalwert für das progress
-Element auf die Anzahl aller required
-Felder gesetzt und der Wert (value
) auf die Zahl der nicht leeren Elemente.
Zum Abschicken des Formulars stehen zwei Schaltflächen zur Verfügung: eine mit der Beschriftung ZWISCHENSPEICHERN und eine mit dem Schriftzug ABSCHICKEN. Die Zwischenspeichern-Funktion wurde bereits in Abschnitt Oder doch nicht prüfen? formnovalidate
, erklärt, neu ist hier das Attribut accesskey
.
Zwar sind Tastaturkürzel nicht neu in HTML5, viel Verwendung fanden sie bisher aber nicht. Ein Problem mit Tastaturkürzeln ist, dass sie auf unterschiedlichen Plattformen mit unterschiedlichen Tastaturkombinationen aktiviert werden und man nie so genau weiß, welche Taste man jetzt zu dem Kürzel drücken muss. Die HTML5-Spezifikation hat auch hierzu einen Vorschlag parat: Der Wert des accessKeyLabel
soll eine Zeichenkette zurückgeben, die dem korrekten Wert auf der verwendeten Plattform entspricht. Diesen Wert könnte man dann in der Beschriftung der Schaltfläche oder in deren title
-Attribut verwenden. Leider war zu dem Zeitpunkt, als dieses Buch geschrieben wurde, kein Browser in der Lage, diese Zeichenkette auszugeben.
So viel zu den neuen Möglichkeiten, die HTML5 für Formulare vorgesehen hat.
Für Webentwickler brechen angenehmere Zeiten an, da sie sich nicht mehr mit JavaScript-Bibliotheken für gängige Eingabeelemente wie zum Beispiel Datum und Uhrzeit herumschlagen müssen. Vor allem im Bereich der mobilen Endgeräte, auf denen die Texteingabe meist nicht so angenehm wie am Computer ist, werden die neuen Formular-Funktionen für ein angenehmeres Arbeiten sorgen. Auch die Formular-Überprüfung im Browser wird wesentlich zu einem übersichtlicheren und damit leichter wartbaren Code beitragen. Dabei sollten Sie nicht vergessen, dass die clientseitige Überprüfung kein Sicherheitsgewinn für die Server-Anwendung ist, da es einem Angreifer leicht möglich ist, diese Prüfungen zu umgehen.
Sollten Sie jetzt Lust bekommen haben, das neu erworbene Wissen über Formulare auf Ihrer Webseite auszuprobieren, so können Sie das bereits heute bedenkenlos tun. Die Syntax der neuen Elemente und Attribute ist so aufgebaut, dass auch ältere Browser keine Fehler produzieren. Zwar kommen Benutzer von diesen Browsern nicht in den Genuss der neuen Eingabeelemente und Funktionen, eine Texteingabe ist aber allemal möglich.
Falls Sie jemals eine komplexere Benutzeroberfläche gestaltet haben, wissen Sie, was die einfachen HTML-Formularelemente für eine Einschränkung darstellen. Sie müssen sich mit Textfeldern, Auswahlmenüs, Optionsschaltflächen, Kontrollkästchen und manchmal sogar mit den noch klobigeren Mehrfach-Auswahllisten herumärgern, die Sie dann auch noch Ihren Benutzern erklären müssen. („Halten Sie die Strg-Taste gedrückt, und klicken Sie auf die gewünschten Einträge. Außer Sie verwenden einen Mac. In diesem Fall drücken Sie die Cmd-Taste.
Also tun Sie, was alle guten Webentwickler tun – Sie nehmen Prototype, jQuery oder basteln Ihre eigenen Steuerelemente und Funktionen aus einer Kombination von HTML, CSS und JavaScript. Aber wenn Sie sich ein Formular mit Slidern, Kalendersteuerelementen, Spinboxen, Autovervollständigungsfeldern und visuellen Editoren ansehen, merken Sie schnell, was Sie sich da für einen Albtraum eingebrockt haben. Sie müssen sicherstellen, dass die von Ihnen eingefügten Steuerelemente keine Konflikte mit den anderen Steuerelementen von Ihnen oder aus anderen JavaScript-Bibliotheken auf der Seite verursachen. Sie können Stunden damit verwenden, einen Kalender-Picker zu implementieren, nur um dann festzustellen, dass die Prototype-Bibliothek Probleme hat, weil jQuery die Funktion $()
übernommen hat. Also verwenden Sie die Methode noConict()
von jQuery. Allerdings stellen Sie dann fest, dass der von Ihnen verwendete Farbwähler nicht mehr funktioniert, weil das Plugin nicht sorgfältig genug programmiert wurde.
Zum Abschluss zeigen wir Ihnen, wie Sie mit dem neuen Attribut
contenteditable
jedes beliebige HTML-Feld
in ein Steuerelement umwandeln, das Ihre Benutzer bearbeiten können.
Im Einzelnen behandeln wir die folgenden Funktionen:
E-Mail-Feld [<input type="email">]
Zeigt ein Formularfeld für E-Mail-Adressen an. [O10.1, IOS]
URL-Feld [<input type="url">]
Zeigt ein Formularfeld für URLs an. [O10.1, IOS]
Telefonnummern-Feld [<input type="tel">]
Zeigt ein Formularfeld für Telefonnummern an. [O10.1, IOS]
Suchfeld [<input type="search">]
Zeigt ein Formularfeld für Suchschlüsselwörter an. [C5, S4, O10.1, IOS]
Slider (Bereich) [<input type="range">]
Zeigt ein Slider-Steuerelement an. [C5, S4, O10.1]
Zahl [<input type="number">]
Zeigt ein Formularfeld für Zahlen an, häufig als Spinbox. [C5, S5, O10.1, IOS]
Datumsfelder [<input type="date">]
Zeigt ein Formularfeld für Datumswerte an. Unterstützt date, month oder week. [C5, S5, O10.1]
Datumswerte mit Uhrzeit [<input type="datetime">]
Zeigt ein Formularfeld für Datumswerte mit Uhrzeit an. Unterstützt datetime
, datetime-local
oder time
. [C5, S5, O10.1]
Farbe [<input type="color">]
Zeigt ein Feld zum Auswählen von Farben an. [C5,S5] (Chrome 5 und Safari 5 verstehen das color
-Feld, zeigen aber kein spezielles Steuerelement dafür an.)
Autofokus [<input type="text" autofocus>]
Bietet die Möglichkeit, einem bestimmten Formularelement automatisch den Fokus zu erteilen. [C5, S4]
Unterstützung für Platzhalter [<input type="email" placeholder="me@example.com">]
Ermöglicht die Anzeige von Platzhaltertext in einem Formularfeld. [C5, S4, F4]
Unterstützung für In-Place-Editing [<p contenteditable>loremipsum</p>]
Unterstützung für das In-Place-Editing von Inhalten im Browser. [C4, S3.2, IE6, O10.1]
Fangen wir damit an, etwas über die extrem nützlichen neuen Formularfeldtypen zu lernen.
Daten mit neuen Eingabefeldern beschreiben
In HTML5 werden mehrere neue Typen von Eingabefeldern eingeführt, mit denen Sie den Datentyp besser beschreiben können, den Ihre Benutzer eingeben sollen. Zusätzlich zu den standardmäßigen Textfeldern, Optionsschaltflächen und Kontrollkästchen können Sie Elemente wie E-Mail-Felder, Kalender, Farbwähler, Spinboxen und Slider verwenden. Browser bieten den Benutzern für diese neuen Felder besser funktionierende Steuerelemente an – auch ohne JavaScript. Mobile Geräte und virtuelle Tastaturen für Tablet-PCs und Touchscreens können für diese Feldtypen unterschiedliche Tastaturlayouts anzeigen. So zeigt beispielsweise der iPhone-Browser Mobile Safari ein anderes Tastaturlayout an, wenn Benutzer Daten vom Typ URL
oder email
eingeben, wodurch Sonderzeichen wie (@
), (.
), (:
) und (/
) leichter zugänglich sind.
Verbessertes Formular für unser Projekt
Ein Geschäft arbeitet an einer neuen Webanwendung für das Projektmanagement, die es Entwicklern und Managern erleichtern soll, bezüglich der aktuellen Projekte auf dem neuesten Stand zu bleiben. Jedes Projekt hat einen Namen, eine Kontakt-E-Mail-Adresse sowie eine Test-URL, damit Manager während der Entwicklungsphase eine Vorschau der Website ansehen können. Es gibt auch Felder für das Startdatum, für die Priorität sowie für die geschätzte Stundenzahl bis zur Fertigstellung des Projekts. Außerdem möchte der Entwicklungsmanager die Möglichkeit haben, jedem Projekt eine Farbe zuzuweisen, damit er in Berichten das jeweilige Projekt schnell erkennen kann.
Bauen wir also mit den neuen HTML5-Feldern ein Gerüst der Seite für die Projekteinstellungen.
Das grundlegende Formular
Wir erstellen ein einfaches HTML-Formular, das eine POST-Anfrage sendet. Nachdem es mit dem Namensfeld nichts Besonderes auf sich hat, verwenden wir dafür ein text
-Feld.
Beachten Sie, dass wir die Beschriftungen für das Formular in eine geordnete Liste schreiben. Beschriftungen sind von grundlegender Bedeutung bei der Erstellung barrierefreier Formulare. Das Attribut for
referenziert die id
des entsprechenden Formularelements. Dies hilft Bildschirmlesegeräten bei der Erkennung der Felder auf einer Seite. Die geordnete Liste bietet eine gute Möglichkeit, die Felder aufzulisten, ohne auf eine komplizierte Tabelle oder div
-Strukturen zurückzugreifen. Außerdem können Sie das Markup auf diese Weise auch in der Reihenfolge schreiben, in der Ihre Besucher die Felder ausfüllen sollen.
Slider mithilfe von range
erstellen
Slider werden häufig verwendet, damit Benutzer einen numerischen Wert bequem vergrößern oder verkleinern können. Dies ist eine ausgezeichnete Lösung, damit Manager schnell die Priorität eines Projekts visualisieren und anpassen können. Einen Slider implementieren Sie mit dem Typ range
.
Fügen Sie diesen Code genau wie das vorherige Feld in einem li
-Element in das Formular ein.
Chrome und Opera implementieren beide ein Slider-Widget, das folgendermaßen aussieht:
Beachten Sie, dass wir den Wertebereich des Sliders mit min
und max
festgelegt haben. Dadurch wird auch der Wertebereich des Formularfelds eingegrenzt.
Zahlen und Spinboxen
Wir verwenden sehr häufig Zahlen. Und obwohl das Eingeben von Zahlen relativ einfach ist, können Spinboxen kleinere Anpassungen erleichtern. Eine Spinbox ist ein Steuerelement mit Pfeilen, über die der Wert im Feld erhöht oder verringert werden kann. Wir verwenden eine Spinbox für die geschätzte Stundenzahl. Auf diese Weise können die Stunden einfach angepasst werden.
Opera unterstützt das Spinbox-Steuerelement. Es sieht folgendermaßen aus:
Standardmäßig können auch Werte direkt in die Spinbox eingetippt werden. Ebenso wie beim range
-Slider können wir einen Mindest- und einen Maximalwert festlegen. Der so festgelegte Wertebereich gilt jedoch nicht für Werte, die direkt in das Feld eingetippt werden.
Beachten Sie auch, dass Sie die Größe der Schritte beim Erhöhen oder Verringern des Wertes festlegen können, indem Sie den Parameter step
angeben. Dieser beträgt standardmäßig 1, kann aber ein beliebiger numerischer Wert sein.
Datumswerte
Die Erfassung des Startdatums für das Projekt ist ziemlich wichtig, wir möchten es daher so einfach wie möglich gestalten. Der input type date
ist dafür die perfekte Wahl.
Als dieser Artikel geschrieben wurde, war Opera der einzige Browser, der einen vollwertigen Kalender-Picker unterstützt.
Hier ein Beispiel für die Implementierung:
Safari 5.0 zeigt ein Feld, das dem number
-Feld ähnlich ist und über Pfeile verfügt, mit denen das Datum erhöht oder verringert werden kann. Wird es leer gelassen, steht der Standardwert auf „1582“. Andere Browser rendern ein Textfeld.
E-Mail
Die HTML5-Spezifikation legt fest, dass der input type email
für die Anzeige einer einzelnen E-Mail-Adresse oder einer Liste von E-Mail-Adressen entwickelt wurde. Er ist daher die perfekte Wahl für unser E-Mail-Feld.
Auf mobilen Geräten kommt der größtmögliche Nutzen dieser Art von Formularfeld zum Tragen, da das Layout der virtuellen Tastatur angepasst und so die Eingabe von E-Mail-Adressen entsprechend vereinfacht wird.
URL
Es gibt auch einen eigenen Feldtyp für URLs. Das ist besonders praktisch, wenn Ihre Besucher ein iPhone verwenden. Denn dann wird ein ganz anderes Tastaturlayout mit Hilfsschaltflächen für die schnelle Eingabe von Webadressen angezeigt, ganz ähnlich wie die Tastatur zur Eingabe einer URL in die Adressleiste von Mobile Safari. Das Feld für die Eingabe der Test-URL lässt sich mit dem folgenden Code einfach einfügen:
Virtuelle Tastaturen zeigen für diesen Feldtyp ebenfalls ein anderes Layout an.
Farbe
Zu guter Letzt müssen wir noch eine Möglichkeit finden, Farbcodes einzugeben. Dafür verwenden wir den Typ color
.
Als dieser Artikel geschrieben wurde, zeigte kein Browser ein Farbwähler-Steuerelement an, aber das soll Sie nicht davon abhalten, es zu verwenden. Sie schreiben korrekten Markup für die Beschreibung Ihres Inhalts, und das wird sich in Zukunft als praktisch erweisen. Vor allem, wenn Sie Ausweichlösungen bereitstellen müssen.
Opera unterstützt bereits jetzt die meisten der neuen Steuerelemente, wie Sie in Abbildung 4 sehen können. Öffnen Sie die Seite dagegen in Firefox, Safari oder Google Chrome, werden Sie keinen nennenswerten Unterschied erkennen. Das müssen wir ändern.
Ausweichlösungen
Browser, die die neuen Typen nicht verstehen, verwenden stattdessen den Typ text
. Ihre Formulare bleiben also trotzdem verwendbar. Außerdem können Sie eines der jQueryUI- oder YUI-Widgets mit dem Feld verknüpfen, um es entsprechend anzupassen. Wenn im Laufe der Zeit immer mehr Browser diese Steuerelemente unterstützen, können Sie die JavaScript-Krücken nach und nach entfernen.
Den Farbwähler ersetzen
Den Farbwähler können wir mit jQuery und den Attribut-Selektoren von CSS3 einfach suchen und ersetzen. Wir suchen alle input
-Felder vom Typ color
und wenden das jQuery-Plugin „SimpleColor“ darauf an.
Da wir die neuen Formulartypen in unserem Markup verwenden, brauchen wir keinen zusätzlichen Klassennamen oder anderes Markup einfügen, um Farbwähler zu kennzeichnen. Attributselektoren und HTML5 ergänzen sich prima.
Wir möchten das Farbwähler-Plugin nicht verwenden, wenn der Browser es nativ unterstützt. Also ermitteln wir mit JavaScript, ob der Browser Eingabefelder unterstützt, deren type den Wert color
hat.
Zunächst erstellen wir mit JavaScript ein Element und legen das type
-Attribut auf den Wert color
fest. Anschließend lesen wir das type
-Attribut wieder aus, um festzustellen, ob wir das Attribut erfolgreich setzen konnten. Wenn wir den Wert color
zurückbekommen, wird dieser Typ unterstützt. Falls nicht, müssen wir unser Skript anwenden.
Interessant wird es in Zeile 6. Safari 5 und Google Chrome 5 haben den Typ color
teilweise implementiert. Sie unterstützen das Feld, zeigen aber kein Farb-Widget an, und so erhalten wir trotzdem nur ein Textfeld. Daher legen wir in unserer Prüfmethode einen Wert für das Eingabefeld fest und überprüfen, ob der Wert erhalten bleibt. Falls nein, können wir davon ausgehen, dass der Browser einen Farbwähler implementiert hat, da sich das Eingabefeld nicht wie ein Textfeld verhält.
Der vollständige Code zum Ersetzen das Farbwählers sieht folgendermaßen aus:
Diese Lösung funktioniert, ist aber ein bisschen wackelig. Sie richtet sich nur an bestimmte Browser und funktioniert auch nur für das color
-Steuerelement. Andere Steuerelemente haben wieder ganz andere Macken, mit denen Sie sich ebenfalls auskennen müssen. Glücklicherweise gibt es eine Alternative.
Modernizr
Bevor Sie Modernizr in Ihren Projekten einsetzen, sollten Sie sich etwas Zeit nehmen zu verstehen, wie er funktioniert. Ob Sie den Code nun selbst geschrieben haben oder nicht, sobald Sie ihn in Ihrem Projekt einsetzen, sind Sie auch dafür verantwortlich. Modernizr war nicht von Anfang an so weit, mit der teilweisen Unterstützung des Farbfelds in Safari klarzukommen. Wenn die nächste Version von Chrome oder Firefox herauskommt, müssen Sie unter Umständen selbst eine Lösung basteln. Wer weiß, vielleicht können Sie ja genau diese Lösung zu Modernizr beitragen!
Ausweichlösungen für Steuerelemente wie den Datums-Picker und den Slider implementieren Sie auf dieselbe Weise.
Nach und nach können Sie die JavaScript-Steuerelemente ausrangieren und ausschließlich auf die Steuerelemente des Browsers zurückgreifen. Aufgrund der Komplexität bei der Ermittlung dieser Typen wird Ihnen Modernizr eine große Hilfe sein. Allerdings werden wir auch weiterhin unsere eigenen Prüftechniken schreiben, damit Sie sehen können, wie sie funktionieren.
Neben den neuen Formularfeldtypen werden in HTML5 auch einige andere Attribute für Formularfelder eingeführt, die dabei helfen können, die Benutzerfreundlichkeit zu verbessern. Sehen wir uns als Nächstes autofocus
an.
Mit autofocus
zum ersten Feld springen
Sie können die Dateneingabe deutlich beschleunigen, wenn Sie schon beim Laden der Seite den Cursor des Benutzers im ersten Feld des Formulars platzieren. Viele Suchmaschinen machen das mit JavaScript, aber in HTML5 ist diese Funktionalität jetzt Teil der Sprache.
Dafür müssen Sie lediglich das Attribut autofocus
zu einem beliebigen Formularfeld hinzufügen, genau wie wir das schon im Abschnitt Daten mit neuen Eingabefeldern beschreiben gemacht haben.
Damit das Attribut autofocus
zuverlässig funktioniert, dürfen Sie es nur einmal pro Seite verwenden. Wenn Sie es auf mehr als ein Feld pro Seite anwenden, setzt der Browser den Cursor auf das zuletzt „autofokussierte“ Formularfeld.
Ausweichlösung
Wir überprüfen mit JavaScript, ob das autofocus
-Attribut vorhanden ist. Wenn der Browser keine Unterstützung für autofocus
bietet, setzen wir den Fokus mit jQuery. Das ist bestimmt die einfachste Ausweichlösung, die es gibt.
Fügen Sie einfach dieses JavaScript in Ihre Seite ein, und Sie haben jederzeit autofocus
-Unterstützung, wenn Sie sie brauchen.
Mit autofocus
erleichtern Sie Ihren Benutzern die Arbeit mit Ihren Formularen, unmittelbar nachdem sie geladen wurden. Aber Sie möchten ihnen auch etwas klarer sagen, welche Art von Informationen sie eingeben sollen.
Anzeigen von Messgrößen mit meter
Mithilfe des meter
-Elements wird der Anteil an einer gewissen Größe grafisch dargestellt. Denken Sie zum Beispiel an die Tankanzeige in Ihrem Auto: Die Nadel zeigt den aktuellen Füllstand im Tank irgendwo zwischen 0 und 100 Prozent an. Bisher wurden solche grafischen Darstellungen in HTML unter anderem mit verschachtelten div
-Elementen codiert, eine relativ unelegante Lösung, die wohl etwas am Sinn des div
-Elements vorbeigeht. Außerdem lässt sich eine Statusanzeige auch grafisch, als Bild darstellen. Dabei können freie Webservices herangezogen werden, wie zum Beispiel die Google Chart API. Alle diese Möglichkeiten werden Sie im folgenden Beispiel sehen.
Die Verwendung des meter
-Elements ist sehr einfach: Über das value
-Attribut wird der gewünschte Wert eingestellt; alle anderen Attribute sind optional. Wird kein min
– und max
-Wert eingestellt, so verwendet der Browser 0 beziehungsweise 1 für diese Attribute. Folgendes meter
-Element zeigt also ein halb volles Element an:
Außer value
, min
und max
gibt es noch die Attribute low
, high
und optimum
, wobei der Browser diese Werte in der Darstellung mit einbeziehen kann. So zeigt zum Beispiel Google Chrome (im Sommer 2010 der einzige Browser, der das meter
-Element darstellen konnte) den ansonsten grünen Balken in Gelb an, wenn der optimum
-Wert überschritten wird.
Im folgenden Beispiel wird der Anteil der vergangenen Tage im aktuellen Jahr grafisch dargestellt. Die Webseite soll die Ausgabe auf vier verschiedene Arten visualisieren: als Text mit einer Angabe in Prozent, mithilfe des neuen meter
-Elements, durch verschachtelte div
-Elemente und als Grafik, die durch den Webservice von Googles Chart API erzeugt wird. Das Resultat sehen Sie in Abbildung 1.
Der HTML-Code für das Beispiel enthält die noch leeren Elemente, die mithilfe von JavaScript befüllt werden:
Für die Textausgabe verwenden wir zusätzlich das in Abschnitt Berechnungen mit output
, vorgestellte output
-Element. Als Erstes wird in Java-Script aber das aktuelle Datum erzeugt und das meter
-Element initialisiert:
Die Variable today
enthält die Anzahl an Millisekunden seit dem Beginn der UNIX-Epoche (dem 1.1.1970). Damit unser meter
-Element eine vernünftige Skala erhält, wird der min
-Wert auf den 1. Januar des aktuellen Jahres eingestellt, der max
-Wert entsprechend auf den 31. Dezember. Der Wert des meter
-Elements wird in der letzten Zeile des Listings eingestellt, und die grafische Anzeige ist komplett. Wer den hier ausgeklammerten optimum
-Wert aktiviert (in diesem Fall die Mitte des Jahres), wird je nachdem, ob das Script in der ersten oder in der zweiten Jahreshälfte aufgerufen wird, eine entsprechende Veränderung der Anzeige erkennen. Wunderbar, wie einfach das neue Element zu verwenden ist.
Doch kommen wir nun zu den restlichen Elementen auf unserer HTML-Seite. Das mit der ID op
gekennzeichnete output
-Element wollen wir mit dem Prozentwert der vergangenen Tage belegen. Die Prozentrechnung wird mit Math.round()
von ihren Kommastellen befreit, eine Genauigkeit, die für unser Beispiel ausreichend ist. Anschließend wird der span
-Bereich (opText
) mit diesem Wert belegt.
Der Rest dieses Beispiels hat zwar nichts mehr mit neuen HTML5-Techniken zu tun, wird aber aus Gründen der Vollständigkeit auch noch erklärt. Die verschachtelten div
-Elemente wollen wir ebenfalls mit dem Prozentwert befüllen. Die Idee dahinter ist simpel: Ein erster div
-Bereich wird mit einer fixen Breite in HTML definiert (hier 150px). Ein darin verschachteltes div
-Element wird mit der Breite von der berechneten Prozentzahl angegebenen und mit grüner Hintergrundfarbe gefüllt – ein einfacher Trick mit guter Wirkung. Abschließend wollen wir noch die Google Chart API mit einbeziehen. Beim Aufruf des Webservice müssen die Größe der Grafik (chs, hier 200×125 Pixel), der Typ der Grafik (cht, hier gom, Google-O-Meter) und die darzustellenden Daten (chd, hier der Prozentwert op.value
) übergeben werden:
Fortschrittsanzeige mit progress
progress
funktioniert ähnlich wie das eben vorgestellte meter
-Element, mit dem Unterschied, dass es den Fortschritt eines laufenden Prozesses darstellt. Mögliche Prozesse sind ein Datei-Upload, den der Benutzer auslöst, oder der Download von externen Bibliotheken, wenn eine Applikation diese benötigt.
Für ein kurzes Beispiel wollen wir aber keine Dateien hochladen oder große Datenmengen herunterladen, es reicht, wenn wir uns selbst eine Aufgabe stellen und diese zu 100 Prozent erfüllen. Im Folgenden werden zehn Eingabeelemente vom Typ checkbox
definiert, und sobald alle aktiviert sind, soll der Fortschrittsbalken 100 % anzeigen.
Das progress
-Element wird mit einem Wert von 0 und einem Maximalwert von 10 initialisiert. Sobald ein Eingabeelement aktiviert wird, ruft es die Funktion updateProgress()
auf, die wie folgt aussieht:
Die Variable ip
enthält eine NodeList mit allen input
-Elementen. Jedes dieser Elemente wird in der for
-Schleife auf seinen Zustand überprüft. Sollte dieser aktiviert sein (checked == true
), so erhöht sich die Zählervariable cnt
um den Wert 1. Abschließend wird der Wert des progress
-Elements auf den Wert der Zählervariable gestellt.
Auswahllisten mit datalist
Eine sehr häufig gewünschte neue Funktion für Formulare ist ein Aufklappmenü, das um eigene Einträge erweitert werden kann. Da das altbekannte select
-Element auf die als option
-Elemente angegebenen Werte beschränkt ist, ersannen Webentwickler verschiedene JavaScript-Kunstgriffe, durch die Textfelder um eine erweiterbare Auswahlliste ergänzt werden können.
Die HTML5-Spezifikation beinhaltet eine sehr elegante Lösung für dieses Problem. Das neue datalist
-Element wurde so definiert, dass es als Container für das schon bekannte option
-Element dient. Jedem input
-Element kann nun ein datalist
-Element zugewiesen werden, das bei Bedarf die Auswahlmöglichkeiten anzeigt. Browser, die das datalist
-Element nicht unterstützen, zeigen nur das leere Textfeld an.
Code unten zeigt die Verwendung des neuen Elements. Das input
-Element wird vom Typ text
definiert, und das Attribut list
verweist auf die id
des datalist
-Elements (in diesem Fall homepages).
Für die option
-Elemente innerhalb der datalist
ist es ausreichend, das value
-Attribut zu befüllen. Weitere Attribute und ein Text-Node sind zwar möglich, werden aber bei dieser Verwendung nicht benötigt. Beim Anklicken der SUBMIT-Schaltfläche wird dem Inhalt des Textfeldes die Zeichenkette http:// vorangestellt und der Browser an die so entstandene URL umgeleitet (window.location
).
Wenn Sie ältere Browser ebenfalls mit einer Auswahlliste ausstatten möchten, ohne den HTML-Code zu duplizieren, können Sie auf folgenden Trick zurückgreifen. Da Browser, die das datalist
-Element unterstützen, ein eingeschlossenes select
-Element ignorieren, zeigen sie das neue HTML5-Auswahlelement an. Ältere Browser hingegen zeigen zu dem Textfeld eine Auswahlliste mit vorgegebenen Links an, die bei einer Änderung der Auswahl in das Textfeld eingefügt werden.
Wie in diesem Code zu sehen ist, müssen die option
-Elemente mit einem Text-Node versehen werden, da das „alte“ select
-Element nicht den Inhalt des value
-Attributs anzeigt, sondern den Text. Das onchange
-Event in dem select
-Element setzt den aktuellen Text des Auswahlmenüs in das Textfeld ein (vergleiche Abbildung 3).
Kryptografische Schlüssel mit keygen
Das keygen
-Element hat bereits eine lange Geschichte im Browser Mozilla Firefox (enthalten seit Version 1.0), trotzdem meldete Microsoft große Bedenken bei der Implementierung in HTML5 an. keygen
wird zur Erzeugung von kryptografischen Schlüsseln verwendet, und so kompliziert das klingt, so kompliziert ist es leider auch.
Ganz einfach gesprochen ist die Idee hinter diesem Element folgende: Der Browser erzeugt ein Schlüsselpaar, das aus einem öffentlichen Schlüssel (public key) und einem privaten Schlüssel (private key) besteht. Der öffentliche Schlüssel wird mit den anderen Formulardaten verschickt und steht anschließend der Server-Anwendung zur Verfügung, während der private Schlüssel im Browser gespeichert bleibt. Nach diesem Schlüsselaustausch haben Server und Browser die Möglichkeit, verschlüsselt zu kommunizieren, und zwar ohne SSL-Zertifikate. Das klingt nach einer praktischen Lösung für die lästigen selbst signierten Zertifikate, die die Browser immer beanstanden müssen, ist es aber leider nicht, denn die Identität des Servers kann nur aufgrund eines Zertifikats gewährleistet werden, das von einer vertrauenswürdigen Zertifizierungsstelle (der Certificate Authority, CA) unterschrieben worden ist.
Da keygen
SSL nicht ablösen kann, wofür soll das neue Element dann verwendet werden? Wie die Dokumentation von Mozilla erklärt, hilft das keygen
-Element, ein Zertifikat zu erstellen, das vom Server unterschieben werden kann (signed certificate). Um diesen Schritt ganz sicher zu gestalten, ist es normalerweise notwendig, dass der Antragsteller persönlich bei der Behörde erscheint. Da das Ausstellen von signierten Zertifikaten eher etwas für Experten ist, werden wir die Beschreibung dieses Elements und seiner Attribute eher kurz halten.
Folgendes kurze HTML-Dokument erzeugt eine keygen-Schaltfläche:
Außer den bekannten Attributen wie autofocus
, disabled
, name
und form
besitzt das keygen
-Element zwei spezielle Attribute: keytype
und challenge
. Vor allem keytype
ist interessant, da der Browser anhand dieses Eintrags entscheidet, ob er die Funktion dieses Elements unterstützt. Momentan gibt es nur einen gültigen keytype
, und zwar rsa
, ein kryptografisches System, das im Jahr 1977 am Massachusetts Institute of Technology (MIT) entwickelt wurde. Wird kein keytype
angegeben (wie im vorangegangenen Beispiel), wird als Standardwert rsa
verwendet. Die Spezifikation sieht auch vor, dass ein Browser überhaupt keinen keytype
unterstützen muss, was wohl auf das Veto von Microsoft gegen das Element zurückgeht. Das optionale challenge
-Attribut erhöht die Sicherheit beim Schlüsselaustausch. Für weiterführende Informationen verwenden Sie bitte die Links am Ende dieses Abschnitts.
Unterstützt der Browser die RSA-Schlüsselerzeugung, so kann er dem Benutzer eine Auswahlliste für die Länge und damit die Sicherheit des Schlüssels anbieten (vergleiche Abbildung 4).
Das Resultat nach dem Abschicken dieses Formulars zeigt Abbildung 5: Die POST
-Variable kg
enthält den zur Verschlüsselung notwendigen öffentlichen Schlüssel.
Berechnungen mit output
„Das output
-Element enthält das Ergebnis einer Berechnung“. So lautet die sehr knappe Erklärung in der HTML5-Spezifikation, und genau das findet man auch auf den meisten Internet-Seiten, die das neue Element beschreiben. Das klingt sehr vernünftig, aber was für eine Art von Berechnung ist damit gemeint? Wieso braucht es dazu ein eigenes Element?
In der Regel handelt es sich dabei um Berechnungen, die aus Eingabefeldern auf einer Webseite zustande kommen. Ein Beispiel, das vielleicht allen geläufig ist, wäre ein elektronischer Einkaufswagen, in dem die Stückzahl für die Produkte in einem input
-Feld eingestellt werden kann. Mithilfe des optionalen for
-Attributs lässt sich festlegen, welche Felder in die Berechnung mit einfließen. Dabei werden ein oder mehrere id
-Attribute anderer Felder des Dokuments referenziert.
Um das output
-Element auszuprobieren, wollen wir so einen kleinen Einkaufswagen programmieren, in dem drei verschiedene Produkte vorhanden sind. Die Stückzahl jedes dieser Produkte kann mithilfe eines Eingabefeldes verändert werden. Gleichzeitig wird unter dem Einkaufswagen die Anzahl der Waren und die Gesamtsumme angezeigt. Abbildung 6 zeigt einen Warenkorb mit fünf Einträgen.
Der Code für das Beispiel ist schnell erklärt: Um bei jeder Änderung der Stückzahl die output
-Elemente zu aktualisieren, verwenden wir das oninput
-Event des Formulars:
Die output
-Elemente sind im Anschluss an die Tabelle mit den Produkten definiert und verweisen über das for
-Attribut auf die IDs der input
-Felder:
Im JavaScript-Code läuft eine Schleife über alle input
-Elemente. Sie zählt die Stückzahlen zusammen und errechnet den Gesamtpreis.
Den Preis des Produkts holen wir uns direkt aus der Tabelle. Dabei verwenden wir den innerHTML
-Wert der entsprechenden Tabellenspalte und wandeln diesen mit der JavaScript-Funktion Number()
in eine Zahl um. Gleiches gilt auch für den Wert im input
-Feld (ips.value
), denn ohne diese Umwandlung würde JavaScript die Zeichenketten addieren, was nicht zu dem gewünschten Ergebnis führt. Abschließend werden die errechneten Werte in die value
-Attribute der output
-Elemente eingesetzt.
Egal ob Sie einen Flug buchen, Ihre Bankgeschäfte online abwickeln oder einen Suchbegriff bei Google eingeben – ohne Formularfelder wären diese Dienste nicht nutzbar. Seit der Version 2.0 von HTML aus dem Jahr 1995 sind die meisten Elemente für interaktive Formulare unverändert, was einerseits für ein sehr vorausschauendes Design von Tim Berners Lee spricht; andererseits hat sich dadurch aber auch ein großer Nachholbedarf entwickelt. Die HTML5-Spezifikation widmet dem Thema Formulare einen großen Abschnitt und wird die Arbeit von Webentwicklern drastisch erleichtern.
Die HTML5-Spezifikation wertet das input
-Element deutlich auf, indem es für das type
-Attribut mehrere neue Werte vorsieht. Die neuen Typen wie date
, color
oder range
machen es einerseits für Browserhersteller möglich, bedienerfreundliche Eingabeelemente zur Verfügung zu stellen, andererseits kann der Browser sicherstellen, dass die Eingaben vom gewünschten Typ sind. Erkennt ein Browser den type des input
-Elements nicht, so fällt er auf type=text
zurück und zeigt ein Textfeld an, was in jedem Fall hilfreich ist. Dieses Verhalten zeigen auch ältere Browser, wodurch dem Einsatz der neuen Typen nichts im Wege steht.
Den größten Nutzen werden wohl die Typen für Datum und Uhrzeit mit sich bringen.
Aktuell kursieren unzählige verschiedene Versionen von mehr oder weniger gelungenen JavaScript-Kalendern im Internet. Egal ob Sie einen Flug buchen, ein Hotel reservieren oder sich bei einer Tagung anmelden, die komfortable Eingabe eines Datums ist ein Problem, das bisher immer Handarbeit verlangte. Natürlich bieten JavaScript-Bibliotheken wie jQuery fertige Kalender an, aber eigentlich sollte diese Funktion vom Browser direkt unterstützt werden.
Im Sommer 2010 gab es nur einen Desktop-Browser, der ein grafisches Bedienungselement für die Datumseingabe mitlieferte, nämlich Opera. Abbildung 1 zeigt den aufgeklappten Kalender, der beim Anklicken eines input
-Elements vom Typ date erscheint. Aber der Reihe nach – zuerst verschaffen wir Ihnen in Tabelle 1 einen Überblick über die neuen Typen und zeigen Ihnen dann in Abbildung 1 deren Umsetzung im Opera-Browser.
Typ | Beschreibung | Beispiel |
---|---|---|
tel |
Text ohne Zeilenumbrüche | +49 6473 3993443 |
search |
Text ohne Zeilenumbrüche | suchbegriff |
url |
eine absolute URL | http://www.example.com |
email |
eine gültige E-Mail-Adresse | user@host.com |
datetime |
Datum und Uhrzeit (immer in der UTC-Zeitzone) | 2010-08-11T11:58Z |
date |
Datumsangabe ohne Zeitzone | 2010-08-11 |
month |
Monatsangabe ohne Zeitzone | 2010-08 |
week |
Jahr und Woche im Jahr ohne Zeitzone | 2010-W32 |
time |
Uhrzeit ohne Zeitzone | 11:58 |
datetime-local |
Datum und Uhrzeit ohne Angabe einer Zeitzone | 2010-08-11T11:58:22.5 |
number |
Zahl | 9999 oder 99.2 |
range |
Numerischer Wert eines Wertebereichs | 33 oder 2.99792458E8 |
color |
Hexadezimale Darstellung von RGB-Werten im sRGB-Farbraum | #eeeeee |
Die Input-Typen tel
und search
tel
und search
unterscheiden sich nicht wesentlich von normalen Textfeldern. In beiden sind Zeichenketten ohne Zeilenumbrüche zulässig. Auch Telefonnummern sind nicht auf Zahlen beschränkt, da hier oft Klammern oder das Pluszeichen verwendet wird. Bei tel
könnte der Browser Vorschläge aus dem lokalen Adressbuch anbieten, eine Situation, die vor allem auf Mobiltelefonen sehr nützlich sein kann. Der search
-Typ wurde eingeführt, damit der Browser die Sucheingabe in einem konsistenten Layout zur jeweiligen Plattform gestalten kann. Zum Beispiel sind Benutzer des Mac OS X-Betriebssystems an abgerundete Ecken bei Suchfeldern gewohnt.
Die Input-Typen url
und email
Bei url
und email
ist außer möglichen Vorschlägen auch eine Prüfung der Syntax möglich. Da es sowohl für E-Mail-Adressen als auch für Internet-Adressen in Form von URLs konkrete Vorschriften gibt, kann der Browser bereits während der Eingabe eine Rückmeldung über mögliche Fehler geben.
Datum und Uhrzeit mit datetime
, date
, month
, week
, time
und datetime-local
Die Datums- und Uhrzeitformate bedürfen einer genaueren Betrachtung. datetime
enthält Datumsangabe und Uhrzeit, wobei als Zeitzone immer UTC verwendet wird. Laut Spezifikation kann der Browser den Anwender auch eine andere Zeitzone auswählen lassen, der Wert des input
-Elements muss aber in UTC umgerechnet sein.
Bei date
und month
fällt die Angabe der Uhrzeit und der Zeitzone weg. Für date
wird in der Spezifikation außerdem erwähnt, dass es sich um eine gültige Tagesangabe innerhalb des ausgewählten Monats handeln muss, wobei Schaltjahre mit einzubeziehen sind. Jahr, Monat und Tag sind mit einem Minuszeichen zu trennen, wobei die Jahresangabe mindestens vier Stellen enthalten und größer als 0 sein muss. Damit sind, anders als in dem etwas ausführlicheren ISO-Standard 8601, keine Zeitpunkte vor Christus darstellbar.
Der Typ week
wird als Woche im Jahr dargestellt, und ihm wird zwingend das Jahr vorangestellt. Als Trennzeichen zwischen Jahr und Woche dient abermals das Minuszeichen. Damit die Angabe nicht mit der von month
verwechselt werden kann, muss der Woche das Zeichen W vorangestellt werden.
datetime-local
funktioniert analog zu dem oben beschriebenen datetime
, mit dem einzigen Unterschied, dass die Angabe der Zeitzone entfällt.
Opera verwendet für die Auswahl aller Datumsangaben ein Kalenderfenster; die Angaben zur Uhrzeit können manuell eingegeben oder über Pfeiltasten am Rand verändert werden (vergleiche Abbildung 1).
Die Input-Typen number
und range
Die Typen number
und range
verlangen, dass die Eingabe in einen numerischen Wert umgewandelt werden kann, wobei auch die Notation für Gleitkommazahlen (zum Beispiel 2.99792458E8) gültig ist. Für den range
-Typ enthält die Spezifikation die Anmerkung, dass der genaue Wert nicht entscheidend ist. Es handelt sich um eine ungefähre Angabe, die vom Anwender gut mit einem Schieberegler eingestellt werden kann. Sowohl Webkit-basierte Browser wie Safari und Google Chrome als auch Opera verwenden zur Darstellung dieses Typs einen Schieberegler (vergleiche Abbildung 1 und Abbildung 2).
Der Input-Typ color
Leider gänzlich ohne Implementierung ist der neue Typ color
. Ähnlich wie bei dem Typ date
gibt es auch hier schon etliche Versuche in JavaScript; im Vergleich zum Datum ist die Farbauswahl aber wesentlich seltener notwendig. Zukünftige Implementierungen im Browser werden aber wahrscheinlich einen Farbwähler vorsehen, wie man ihn von Bildbearbeitungsprogrammen her kennt. Der Wert für das input
-Element muss die 8-Bit-Rot-, -Grün- und -Blau-Werte in hexadezimaler Notation mit führendem #
-Zeichen enthalten. Die Farbe Blau wird in dieser Notation zum Beispiel als #0000ff
geschrieben.
Die neuen Input-Typen im Einsatz
Genug der Theorie. In einem ersten Beispiel werden alle neuen Elemente untereinander dargestellt. Da das allein noch keine besondere Herausforderung darstellt, soll jedes Element noch auf seine Funktion geprüft werden. Der Trick dabei ist, dass der Browser den Typ eines unbekannten Elements auf text
setzt, und diese Eigenschaft können wir in JavaScript abfragen:
Sobald die Webseite vollständig geladen ist, läuft eine Schleife über alle input
-Elemente, in der deren type
-Attribute analysiert werden. Sofern das type
-Attribut dem Standard-Typ text
entspricht, wird dessen Wert auf nicht erkannt gesetzt. Der HTML-Code für die neuen input
-Elemente sieht folgendermaßen aus:
Wie das Ergebnis dieses Tests auf einem Android-Mobiltelefon ausfällt, zeigt Abbildung 3. Der Webkit-basierte Browser des Systems (links) gibt zwar vor, die Typen tel
, search
, url
und email
zu kennen, leistet aber bei der Eingabe der Telefonnummer über die Tastatur (Mitte) keine besondere Hilfe. Opera Mini in Version 5.1 (rechts) gibt direkt zu, dass es keinen der neuen Typen unterstützt.
Das ist eine enttäuschende Bilanz für die sonst so modernen mobilen Browser. Auf dem iPhone sieht die Sache etwas besser aus: So passt das Smartphone zumindest die Software-Tastatur so an, dass bei der Eingabe von Telefonnummern ein Zahlenfeld erscheint, und bei dem input
-Typ email
wird das @
-Zeichen auf der Tastatur hinzugefügt.
Noch etwas besser funktioniert das mit BlackBerry, dem Betriebssystem des kanadischen Smartphone-Herstellers Research in Motion (RIM), dessen Geräte vor allem in Nordamerika weit verbreitet sind. Wie Abbildung 4 zeigt, werden sowohl tel
als auch number
und Datumstypen unterstützt, wobei vor allem Letztere grafisch sehr ansprechend aufbereitet sind. Unter der Haube arbeitet Webkit, wobei die Software um diese Funktionen erweitert wurde.
Neben neuen Elementen und vielen neuen Typen für das input
-Element bietet HTML5 auch einige neue Attribute für Formular-Elemente.
Fokussieren mit autofocus
Google überraschte viele Anwender vor Jahren mit einem einfachen Trick, der die Suchseite deutlich komfortabler machte: Beim Laden der Seite positionierte sich der Cursor automatisch im Suchfeld. Dadurch konnte man unmittelbar den Suchbegriff eingeben, ohne erst mit der Maus das Eingabefeld aktivieren zu müssen. Was bisher mit einem kurzen JavaScript-Schnipsel erledigt wurde, kann in HTML5 mit dem autofocus
-Attribut erreicht werden.
Für ältere Browser stellt autofocus
kein Hindernis dar, weil sie das unbekannte Attribut einfach ignorieren. Den Gewinn an Benutzerfreundlichkeit haben freilich nur neue Browser.
Platzhalter-Text mit placeholder
Eine weitere Verbesserung der Benutzbarkeit von HTML-Formularen erreicht man durch das neue placeholder
-Attribut.
Der Wert von placeholder
kann dem Benutzer einen kurzen Hinweis darauf geben, wie das Feld auszufüllen ist, und sollte nicht als Ersatz für das label
-Element verwendet werden. Das bietet sich vor allem bei solchen Feldern an, wo ein bestimmtes Eingabeformat erwartet wird. Der Browser zeigt den Hinweistext innerhalb eines nicht aktiven Eingabefelds an. Sobald das Feld aktiviert wird und den Fokus erhält, wird der Text nicht mehr angezeigt (vergleiche Abbildung 1).
Verpflichtende Felder mit required
required
ist ein boolean
-Attribut, das mit einem Wort schon alles über seine Funktion verrät: Ein Formular-Element, dem dieses Attribut zugewiesen ist, muss ausgefüllt werden. Wenn ein required
-Feld beim Abschicken des Formulars leer ist, so erfüllt es nicht die erforderten Vorgaben, und der Browser muss darauf entsprechend reagieren.
Noch mehr neue Attribute für das input
-Element
Das input
-Element wurde nicht nur durch neue Typen aufgewertet, sondern auch durch neue Attribute, die die Handhabung von Formularen erleichtern.
Attribut | Typ | Beschreibung |
---|---|---|
list |
String | Verweis auf die ID eines datalist-Elements mit Vorschlägen |
min |
Numerisch/Datum | Minimalwert für numerische Felder und Datumsfelder |
max |
Numerisch/Datum | Maximalwert für numerische Felder und Datumsfelder |
step |
Numerisch | Schrittweite für numerische Felder und Datumsfelder |
multiple |
Boolean | Mehrfachauswahl möglich |
autocomplete |
Enumerated (on/off/default) | Vorausfüllen von Formularfeldern mit gespeicherten Daten |
pattern |
String | Regulärer Ausdruck zum Überprüfen des Werts |
Dem list
-Attribut verweist auf das datalist
-Element, das mögliche Einträge als Vorschläge bereitstellt.
min
, max
und step
eignen sich nicht nur für numerische Felder; auch bei Datums- und Zeitangaben können diese Attribute verwendet werden.
In Browsern, die den input
-Typ number
unterstützen, wird das erste input
-Element (id=minMax
) jeweils um den Wert von 0.1 erhöht. Das funktioniert durch den Klick auf die Pfeiltasten am Ende des Textfeldes oder durch das Drücken der Pfeiltasten auf der Tastatur. Das Element mit der ID minMaxDate
springt jeweils um sieben Tage weiter. Opera zeigt dabei in dem Kalender nur jene Tage als aktiv an, die dem Wochenzyklus entsprechen. Google Chrome bietet zum Einstellen dieses Elements die gleiche Navigation wie beim input
-Typ number
: zwei Pfeiltasten, die das Datum um sieben Tage vor oder zurück stellen. Bei dem dritten input
-Element in diesem Beispiel wird die Schrittweite mit 3600 angegeben, was dazu führt, dass die Zeitangabe jeweils um eine Stunde vor oder zurückgestellt wird. Obwohl in der Spezifikation erwähnt ist, dass die Eingabeelemente für Zeitangaben normalerweise mit einer Genauigkeit von Minuten arbeiten, interpretieren sowohl Opera als auch Google Chrome diese Angabe als Sekunden.
Die Mehrfachauswahl ist uns allen vom Kopieren von Dateien her bekannt; im Browser gibt es diese Möglichkeit jetzt auch. Wollte man bisher mehrere Dateien auf einer Webseite laden, so musste man für jede Datei ein input
-Feld vorsehen. Das multiple
-Attribut ermöglicht es, im Dateidialog mehrere Dateien zu markieren. Für das select
-Element war die multiple
-Option schon immer vorgesehen, neu ist die Verwendung für Eingabefelder vom Typ email
. Im Sommer 2010 konnte aber keiner der gängigen Desktop-Browser diese Funktion für email
-Typen umsetzen.
Moderne Browser verfügen über eine Funktion, durch die Formular-Eingaben gespeichert werden,
damit sie bei einem neuerlichen Zugriff auf das Formular als Hilfe beim Ausfüllen dienen. Was meist sehr praktisch ist, kann bei sicherheitskritischen Eingabefeldern auch unerwünscht sein (die Spezifikation erwähnt hier als Beispiel die Abschusscodes von Nuklearwaffen). Das autocomplete
-Attribut wurde eingeführt, damit Webentwickler dieses Verhalten steuern können. Wird ein Element mit dem Attribut autocomplete="off"
versehen, so bedeutet das, dass die einzugebende Information vertraulich ist und nicht im Browser gespeichert werden soll. Enthält ein Formular-Element keinen Hinweis, ob autocomplete
ein- oder ausgeschaltet sein soll, so ist der Standardwert, dass Vorschläge angezeigt werden sollen. Das autocomplete
-Attribut kann auch auf das ganze Formular angewendet werden, indem man es dem form
-Element zuweist.
Um eine sehr flexible Überprüfung der Eingabe zu ermöglichen, wurde das pattern
-Attribut eingeführt. Durch die Angabe eines regulären Ausdrucks wird das Formularfeld auf eine Übereinstimmung geprüft. Reguläre Ausdrücke stellen eine sehr mächtige, aber leider auch nicht ganz einfache Methode zur Behandlung von Strings dar. Stellen Sie sich vor, Sie suchen eine Zeichenkette, die mit einem Großbuchstaben beginnt, auf den eine beliebige Anzahl von Kleinbuchstaben oder Zahlen folgt, und die auf .txt endet. Mit einem regexp
(eine Kurzform für Regular Expression, d. h. regulärer Ausdruck) ist das kein Problem:
Beim Einsatz von regulären Ausdrücken im pattern
-Attribut ist zu beachten, dass das Suchmuster immer auf den gesamten Inhalt des Feldes zutreffen muss. Außerdem wird in der Spezifikation vorgeschlagen, dass das title
-Attribut dazu verwendet wird, dem Anwender einen Hinweis zu geben, wie das Format der Eingabe ist. Opera und Google Chrome zeigen diese Informationen dann in Form eines Tool-Tipps an, sobald sich der Mauszeiger über dem Feld befindet. Nach so viel Theorie folgt nun endlich ein kurzes Beispiel:
Die Vorgabe für das pattern
lautet, dass die Zeichenkette nur Zeichen zwischen a und z (also Kleinbuchstaben) enthalten darf ([a-z]
) und davon mindestens 3 und höchstens 32. Umlaute und andere Sonderzeichen sind damit nicht erlaubt, was für einen Benutzernamen, wie in oben stehendem Beispiel, auch ganz gut sein kann. Wollte man zumindest die deutschen Umlaute und das scharfe ß mit einbeziehen, müsste man die Gruppe um diese erweitern: [a-zäöüß]
.
Mit Platzhaltertext können Sie Benutzern Anweisungen geben, womit sie das entsprechende Feld ausfüllen sollen. Abbildung 1 zeigt ein Registrierungsformular mit Platzhaltertexten. Dieses Formular bauen wir jetzt nach.
Ein einfaches Registrierungsformular
Benutzer der Support-Website müssen zunächst ein Konto erstellen. Eines der größten Probleme bei der Registrierung ist, dass Benutzer immer wieder versuchen, unsichere Kennwörter zu wählen. Wir werden unsere Benutzer mit Platzhaltertext an die Hand nehmen und ihnen mitteilen, welche Anforderungen wir an Kennwörter haben. Der Konsistenz halber werden wir auch die anderen Felder mit Platzhaltertexten versehen.
Zum Hinzufügen von Platzhaltertexten müssen Sie lediglich für jedes Eingabefeld das placeholder
-Attribut angeben:
Das Markup für unser Formular mit Platzhaltertexten für jedes Feld sieht in etwa so aus:
Autovervollständigung verhindern
Vielleicht ist Ihnen aufgefallen, dass wir für die Kennwortfelder des Formulars das Attribut autocomplete
angegeben haben. In HTML5 wird das Attribut autocomplete
eingeführt, um Webbrowsern mitzuteilen, dass sie nicht versuchen sollen, die Daten für dieses Feld automatisch auszufüllen. Einige Browser merken sich Daten, die die Benutzer zuvor eingetippt haben. Aber in manchen Fällen möchten wir den Browser anweisen, das lieber nicht zu tun.
Da wir wieder einmal eine geordnete Liste als Container für unsere Formularfelder verwenden, schreiben wir ein bisschen CSS, damit das Formular besser aussieht.
Safari, Opera und Chrome zeigen nun in den Formularfeldern einen hilfreichen Text für die Benutzer an. Als Nächstes bringen wir Firefox und den Internet Explorer dazu, ebenfalls mitzuspielen.
Ausweichlösung
Mit JavaScript können Sie Platzhaltertext ohne größeren Aufwand in Formularfelder schreiben. Sie testen den Wert jedes Formularfelds. Und wenn er leer ist, belegen Sie den Wert mit dem Platzhalter. Erhält das Formularfeld den Fokus, löschen Sie den Wert wieder. Und wenn das Feld den Fokus verliert, überprüfen Sie den Wert erneut. Falls er sich geändert hat, lassen Sie ihn unverändert. Und falls der Wert leer ist, ersetzen Sie ihn wieder durch den Platzhaltertext.
Die Unterstützung für placeholder
ermitteln Sie genauso wie die Unterstützung für autofocus
.
Dann schreiben Sie das JavaScript, um die Änderungen zu verarbeiten. Um das zum Laufen zu bekommen, verwenden wir eine Lösung, die auf der Arbeit von Andrew January und anderen basiert. Wir füllen die Werte aller Formularfelder mit dem Text, der im Attribut placeholder
abgelegt ist. Wählen die Benutzer ein Feld aus, entfernen wir den Text aus dem Feld. Das Ganze verpacken wir in ein jQuery-Plugin, damit wir dieses Verhalten einfach auf unser Formular anwenden können.
In diesem Plugin stehen einige interessante Dinge, die Sie wissen sollten. In Zeile 46 laden wir den Platzhaltertext erneut in die Felder, wenn diese keinen Wert haben oder die Seite neu geladen wurde. Firefox und andere Browser speichern die Formularwerte. Wir belegen das value
-Attribut mit dem Platzhalter und möchten auf keinen Fall, dass dieser versehentlich zum Wert des Benutzers wird. Wenn wir die Seite laden, übergeben wir true
an diese Methode, wie Sie in Zeile 61 sehen.
jQuery-Plugins
Sie können jQuery um eigene Plugins erweitern. Dazu fügen Sie eigene Methoden zur jQuery-Funktion hinzu, und schon steht Ihr Plugin nahtlos allen Entwicklern zur Verfügung, die Ihre Bibliothek einbinden. Hierzu ein triviales Beispiel, das eine JavaScript-Alertbox anzeigt:
Wenn Sie nun für jeden Absatz einer Seite ein Pop-up-Feld anzeigen möchten, geht das so:
jQuery-Plugins sind so konzipiert, dass sie eine Sammlung von jQuery-Objekten durchlaufen. Gleichzeitig liefern sie diese Objektsammlung auch zurück, damit mehrere Methoden miteinander verkettet werden können. Da unser debug
-Plugin ebenfalls die jQuery-Sammlung zurückliefert, können wir in derselben Zeile gleich noch mit der css
-Methode von jQuery die Textfarbe der Absätze ändern:
Kennwortfelder verhalten sich ein bisschen anders als andere Formularfelder. Also müssen wir auch anders damit umgehen. Werfen Sie einen Blick auf Zeile 12. Wir überprüfen, ob ein Kennwortfeld vorhanden ist, und müssen seinen Typ in ein reguläres Textfeld ändern, damit der Wert nicht mit Sternchen maskiert wird. Manche Browser melden Fehler, wenn Sie versuchen, Kennwortfelder zu konvertieren. Also müssen wir die Kennwortfelder durch Textfelder ersetzen. Wir werden die Felder austauschen, während die Benutzer damit interagieren.
Durch diesen Trick werden die Werte der Formulare verändert. Im Zweifel möchten Sie aber verhindern, dass die Platzhalter zum Server gelangen. Nachdem wir den Platzhalter-Code nur einsetzen können, wenn JavaScript aktiviert ist, können wir auch mit JavaScript die Übermittlung des Formulars überprüfen. In Zeile 66 fangen wir daher die Übermittlung des Formulars ab und löschen alle Werte aus allen Eingabefeldern, die den Platzhalterwerten entsprechen.
Nachdem wir jetzt alles als Plugin zusammengefasst haben, können wir das Plugin auf der Seite aufrufen, indem wir es mit dem Formular verknüpfen:
Damit haben wir eine ziemlich gute Lösung, die Ihnen Platzhaltertexte als brauchbare Option für Ihre Webanwendungen zur Verfügung stellt, egal welchen Browser Ihre Benutzer verwenden.
Im Web ist standardmäßig alles eckig. Formularfelder, Tabellen und sogar die Abschnitte von Webseiten sehen klobig und kantig aus. Deshalb haben viele Designer im Laufe der Jahre auf die verschiedensten Techniken zurückgegriffen, um Elementen runde Ecken zu verpassen und so die Oberflächen ein bisschen weicher zu gestalten.
CSS3 bietet eine einfache Möglichkeit, Ecken abzurunden, die von Firefox und Safari bereits seit langer Zeit unterstützt wird. Leider ist der Internet Explorer noch nicht mit an Bord. Aber das können wir leicht ändern.
Ein Anmeldeformular auflockern
Die Modelle und Entwürfe für Ihr aktuelles Projekt zeigen Formularfelder mit abgerundeten Ecken. Zunächst runden wir die Ecken nur mit CSS3
ab. Das Ergebnis soll so aussehen wie in Abbildung 1.
Für das Anmeldeformular schreiben wir ganz einfaches HTML.
Wir stylen das Formular noch ein bisschen, damit es besser aussieht.
Diese einfachen Stilregeln entfernen die Aufzählungszeichen von der Liste und gewährleisten, dass alle Eingabefelder dieselbe Größe haben. Nachdem wir damit fertig sind, können wir unsere Elemente abrunden.
Browserspezifische Selektoren
Da die CSS3-Spezifikation noch nicht endgültig ist, haben die Browserhersteller selbst einige Funktionen hinzugefügt und den Namen jeweils das Präfix ihrer eigenen Implementierungen vorangestellt. Durch diese Präfixe können Browserhersteller frühzeitig Funktionen einführen, bevor diese Teil einer endgültigen Spezifikation werden. Und da sie nicht der eigentlichen Spezifikation folgen, können die Browserhersteller die tatsächliche Spezifikation und ihre eigene Version parallel implementieren. In den meisten Fällen entspricht die Version mit dem Hersteller-Präfix der CSS-Spezifikation, aber gelegentlich auch nicht. Für Sie bedeutet das leider, dass Sie den Rahmenradius für jeden Browsertyp gesondert deklarieren müssen.
Firefox verwendet diesen Selektor:
WebKit-basierte Browser wie Safari und Chrome nutzen den folgenden Selektor:
Um alle Eingabefelder unseres Formulars abzurunden, benötigen wir eine CSS-Regel wie die folgende:
Fügen Sie das in Ihre Datei style.css ein, und die Ecken sind rund.
Ausweichlösung
Nun funktioniert alles in Firefox, Safari und Google Chrome. Aber wie Sie wissen, funktioniert es nicht im Internet Explorer. Und natürlich wissen Sie, dass es auch im Internet Explorer funktionieren muss und Sie daher etwas implementieren müssen, das dem so nahe wie möglich kommt.
Webentwickler runden Ecken nun schon seit geraumer Zeit mit Hintergrundbildern und anderen Techniken ab, aber wir möchten es möglichst einfach halten. Wir können mit JavaScript den Radius der Ecken ermitteln und eine ganze Reihe von Abrundungstechniken anwenden. In diesem Beispiel verwenden wir das jQuery-Plugin
„Corner“ sowie eine Abwandlung des Corner-Plugins, die auch Textfelder abrundet.
Unterstützung für abgerundete Ecken ermitteln
Wir binden die jQuery-Bibliothek und das Plugin ein und überprüfen, ob der Browser das Attribut unterstützt. Falls nicht, aktivieren wir das Plugin. In diesem Fall müssen wir das Vorhandensein der CSS-Eigenschaft border-radius
, aber auch browserspezifischer Präfixe wie webkit
und moz
ermitteln.
Erstellen Sie corner.js, und fügen Sie die folgende Funktion ein:
Nun können wir überprüfen, ob es bei unserem Browser an Unterstützung für abgerundete Ecken hapert. Schreiben wir also den Code für das eigentliche Abrunden der Ecken. Erfreulicherweise gibt es ein Plugin, das wir als Ausgangspunkt verwenden können.
jQuery Corners
Entscheiden, ob sich der Aufwand lohnt
In unserem Beispiel möchte der Kunde, dass die abgerundeten Ecken wirklich in allen Browsern funktionieren. Allerdings sollten Sie solche Funktionen nach Möglichkeit immer optional halten. Natürlich argumentieren manche Leute, dass es einen realen Nutzen bringt, wenn das Aussehen des Formulars aufgelockert wird. Trotzdem sollten Sie sich zunächst einen Eindruck davon verschaffen, wie viele Menschen überhaupt Browser verwenden, die die CSS-basierte Abrundung nicht unterstützen. Wenn Ihre Besucher hauptsächlich Safari und Firefox benutzen, lohnt sich unter Umständen der zeitliche Aufwand zum Schreiben und Pflegen eines Skripts zur Überprüfung und für die Ausweichlösung nicht.
Als Erstes verweisen Sie von Ihrer HTML-Seite auf jQuery Corners. Bei dieser Gelegenheit binden Sie auch gleich noch die Datei corner.js mit ein.
Nun müssen wir lediglich den Code schreiben, der die Abrundung aufruft.
Unser formCorners-Plugin
Fügen Sie Folgendes in die Datei corners.js ein:
Wir nehmen ein jQuery-Objekt, das entweder ein Element oder eine Sammlung von Elementen sein kann, und verpacken es in zwei div
-Tags, die wir anschließend abrunden. Zuerst geben wir dem inneren div
dieselbe Farbe wie dem Hintergrund des ursprünglichen Eingabefelds und deaktivieren den Rahmen des eigentlichen Formularfelds. Anschließend verschachteln wir das Feld in ein anderes Feld, das die Rahmenfarbe des ursprünglichen Eingabefelds als Hintergrundfarbe sowie ein bisschen Padding erhält. Und dieses Padding ergibt die Rahmenlinie. Stellen Sie sich einfach zwei Blätter Buntpapier vor: ein grünes, das 10 cm breit ist, und ein rotes, das 8 cm breit ist. Wenn Sie das kleinere auf das größere legen, sehen Sie um das rote Stück Papier einen grünen Rahmen herum. So funktioniert das.
Die Abrundung aufrufen
Mit dem Plugin und unserer Prüfbibliothek können wir nun die Abrundung starten.
Fügen Sie Folgendes in die Datei corners.js ein:
Wir runden die drei Formularfelder und die Feldgruppe ab. In Zeile 5 runden wir schließlich den oberen Teil der Legende ab und legen fest, dass für den Ausschnitt der Ecken Weiß verwendet werden soll. Das Plugin verwendet die Hintergrundfarbe des übergeordneten Elements als Farbe für den Ausschnitt, aber das ist hier nicht richtig.
Wenn der Browser die Eigenschaft border-radius
unterstützt, führt er unser Plugin aus. Falls nicht, verwendet er das CSS, das wir vorhin eingefügt haben.
Ein kleiner IE-Trick
Der IE behandelt Legenden ein bisschen anders. Wir können einen Trick einsetzen, durch den der IE die Legende der Feldgruppe ein bisschen nach oben schiebt, sodass sie genauso aussieht wie in Firefox und Chrome.
Jetzt sieht unser Formular auf allen wichtigen Browsern relativ ähnlich aus. Die Internet Explorer-Version sehen Sie in Abbildung 2.
Abgerundete Ecken lassen Ihre Oberflächen weicher erscheinen und sind extrem einfach zu verwenden. Sie sollten diese Technik aber wie jede andere Gestaltungsmöglichkeit mit Bedacht und nicht übermäßig verwenden.
Abgerundete Ecken genießen natürlich eine Menge Ansehen, aber das ist nur der Anfang. Wir können noch viel mehr mit CSS3 machen: Elemente mit Schlagschatten vom restlichen Inhalt abheben, Hintergründe mit Verläufen definierter aussehen lassen und Elemente mit Transformationen rotieren. Kombinieren wir einige dieser Techniken, um ein Banner zusammenzubasteln.
Der Grafikdesigner hat ein PSD geschickt, das wie die Abbildung 1 aussieht. Wir können das Namensschild, den Schatten und sogar die Transparenz vollständig in CSS
erstellen. Vom Grafikdesigner brauchen wir lediglich das Hintergrundbild mit den Menschen.
Die grundlegende Struktur
Beginnen wir mit der grundlegenden HTML-Struktur der Seite:
Diesen Code stylen wir folgendermaßen:
Sobald wir dieses Stylesheet auf unsere Seite anwenden, werden unser Namensschild und der Inhaltsbereich wie in Abbildung 2 nebeneinander angezeigt. Jetzt kann es mit dem Styling des Namensschilds losgehen.
Einen Verlauf hinzufügen
Firefox verwendet die Methode -moz-linear-gradient
, mit der wir den Anfangspunkt des Verlaufs, gefolgt von der Startfarbe und der Zielfarbe, angeben.
Für WebKit-basierte Browser können wir Farbstopps angeben. In unserem Beispiel soll der Verlauf von Weiß bis Grau gehen. Für zusätzliche Farben müssen wir lediglich einen weiteren Farbstopp definieren.
Schatten für das Ansteckschild
Für den Schatten des Namensschilds brauchen wir nur die folgende Regel in unser Stylesheet einzufügen:
Die Eigenschaft box-shadow
erwartet vier Parameter. Der erste ist der horizontale Versatz: Eine positive Zahl bedeutet, dass der Schatten zur rechten Seite des Objekts fällt. Bei einer negativen Zahl fällt der Schatten zur linken Seite. Der zweite Parameter gibt den vertikalen Versatz an: Positive Zahlen lassen den Schatten unterhalb der Box erscheinen, negative Zahlen oberhalb.
Der dritte Parameter gibt den Weichzeichnungsradius an. Beim Wert 0 wird der Schatten sehr scharf dargestellt, bei höheren Werten wird der Schatten entsprechend weichgezeichnet. Der letzte Parameter bestimmt die Farbe des Schattens.
Mit diesen Werten müssen Sie experimentieren, bis Sie ein Gefühl dafür entwickeln, wie sie funktionieren, und die passenden Werte finden. Für die Arbeit mit Schatten sollten Sie erforschen, wie Schatten in der physischen Welt funktionieren.
Textschatten
Zusätzlich zu Elementen können Sie auch Text ganz einfach Schatten werfen lassen. Das funktioniert genauso wie box-shadow
.
Sie geben den X- und Y-Versatz an sowie den Grad der Weichzeichnung und die Farbe des Schattens. IE 6, 7 und 8 bieten mit dem Filter „Shadow“ dieselbe Möglichkeit.
Das ist derselbe Ansatz wie beim Schlagschatten für Elemente. Schatten ergänzen Texte um einen hübschen Effekt, aber zu starke Schatten schaden der Lesbarkeit.
Schnappen Sie sich eine Taschenlampe, und beleuchten Sie Objekte damit, oder gehen Sie vor die Tür, und beobachten Sie, wie die Sonne Gegenstände Schatten werfen lässt. Diese Perspektive ist sehr wichtig. Inkonsistente Schatten lassen Ihre Oberflächen schnell verwirrend erscheinen, insbesondere dann, wenn Sie für mehrere Elemente die Schatten falsch zeichnen. Die einfachste Variante besteht darin, für jeden Schatten dieselben Einstellungen zu verwenden.
Das Namensschild drehen
Drehungen sind mit CSS3
ziemlich einfach. Wir müssen lediglich den Drehwinkel angeben, und los geht es. Alle Elemente innerhalb des rotierten Elements werden mitgedreht.
Die Drehung ist genauso simpel wie die abgerundeten Ecken, aber auch damit sollten Sie es nicht übertreiben. Das Ziel beim Interface-Design besteht darin, Oberflächen benutzerfreundlicher zu machen. Wenn Sie Elemente mit viel Inhalt drehen, sollten Sie sicherstellen, dass Ihre Benutzer den Inhalt auch lesen können, ohne den Kopf zu weit zur Seite legen zu müssen!
Transparente Hintergründe
Grafikdesigner verwenden schon seit einer ganzen Weile semitransparente Ebenen hinter Text. Dafür erstellen sie entweder ein komplettes Bild im Photoshop oder legen mit CSS ein transparentes PNG über ein Element. In CSS3 können wir Hintergrundfarben mit einer neuen Syntax definieren, die auch Transparenz unterstützt.
Als Neuling in der Webentwicklung lernen Sie zunächst, Farben mit hexadezimalen Farbcodes zu definieren: Sie geben die Menge an Rot, Grün und Blau in Zahlenpaaren. 00 bedeutet „ganz aus“, und FF bedeutet „ganz an“. FF0000 entspricht der Farbe Rot und bedeutet „Rot ganz an, Blau ganz aus und Grün ganz aus“.
In CSS3 werden die Funktionen rgb
und rgba
eingeführt. Die rgb
-Funktion funktioniert wie ihr hexadezimales Gegenstück, jedoch verwenden Sie Werte zwischen 0 und 255 für die jeweiligen Farben. Rot wird beispielsweise als rgb(255,0,0)
definiert.
Die rgba
-Funktion arbeitet auf die gleiche Weise wie die rgb
-Funktion, erwartet aber einen vierten Parameter, der die Deckkraft zwischen 0 und 1 angibt. Für den Wert 0 wird überhaupt keine Farbe angezeigt, die Farbe ist vollkommen transparent. Mit der folgenden Stilregel machen wir den weißen Kasten semitransparent:
Wenn Sie mit solchen Transparenzwerten arbeiten, können sich die Kontrasteinstellungen der Benutzer auf das Endergebnis auswirken. Sie sollten unbedingt auf verschiedenen Bildschirmen mit den Werten experimentieren, um konsistente Ergebnisse zu gewährleisten.
Während wir am Infobereich unseres Banners arbeiten, können wir auch gleich die Ecken abrunden:
Damit sieht unser Banner in Safari, Firefox und Chrome ziemlich gut aus. Als Nächstes implementieren wir ein Stylesheet für den Internet Explorer.
Ausweichlösung
Die in diesem Abschnitt verwendeten Techniken funktionieren wunderbar im IE 9, sind aber auch mit Internet Explorer 6, 7 und 8 möglich! Dafür müssen wir die DirectX-Filter von Microsoft verwenden. Das bedeutet, dass wir auf einen bedingten Kommentar zurückgreifen und ein IE-spezifisches Stylesheet laden müssen. Außerdem müssen wir mit JavaScript das section
-Element erstellen, damit wir es mit CSS stylen können, da diese Versionen des IE das Element nicht nativ erkennen.
Die DirectX-Filter funktionieren in IE 6, 7 und 8. Aber im IE 8 werden die Filter anders aufgerufen. Daher müssen Sie jeden Filter zweimal deklarieren. Sehen wir uns zunächst an, wie wir Elemente drehen können.
Drehen
Wir können Elemente mit diesen Filtern drehen. Aber es reicht nicht aus, einfach nur den Winkel vorzugeben. Für den gewünschten Effekt müssen wir den Matrixfilter verwenden und den Cosinus bzw. Sinus des gewünschten Winkels angeben. Genauer gesagt müssen wir den Cosinus, den negativen Sinus, den Sinus und nochmals den Cosinus angeben:
Kompliziert? Ja, vor allem, wenn Sie sich das vorherige Beispiel genauer ansehen: Erinnern Sie sich, dass unser ursprünglicher Winkel minus 7,5 Grad beträgt? Entsprechend brauchen wir für den negativen Sinus einen positiven Wert, und unser Sinus erhält einen negativen Wert.
Mathe ist kompliziert. Machen wir lieber einen Verlauf.
Farbverläufe
Der Verlaufsfilter des IE funktioniert wie im Standard vorgesehen, allerdings haben Sie eine ganze Menge mehr Tipparbeit. Eigentlich geben Sie nur die Start- und die Endfarbe an – und schon wird der Verlauf angezeigt.
Anders als bei den anderen Browsern wenden Sie im IE den Verlauf direkt auf das Element an statt auf die Eigenschaft background-image
.
Wir wenden den Filter gleich noch einmal für die transparente Hintergrundfarbe unseres Infobereichs an.
Transparenz
Sie können dem Verlaufsflter erweiterte Hexadezimalwerte für die Start- und Endfarbe übergeben, wobei die ersten beiden Stellen den Transparenzgrad angeben:
Die achtstelligen Hexcodes funktionieren ähnlich wie die rgba
-Funktion, jedoch steht der Transparenzwert an erster Stelle, nicht am Ende. In Wahrheit haben wir es also mit Alpha, Rot, Grün und Blau zu tun.
Wir müssen die Hintergrundeinstellungen des Elements entfernen, damit das im IE 7 funktioniert. Falls Sie das Stylesheet mittippen, haben Sie sicherlich festgestellt, dass es noch nicht funktioniert. Aber das können wir ändern.
Alles zusammengenommen
Eines der schwierigeren Probleme mit den IE-Filtern besteht darin, dass wir sie nicht stückchenweise definieren können. Um mehrere Filter auf ein einzelnes Element anzuwenden, müssen wir die Filter als mit Kommata getrennte Liste definieren. So sieht das tatsächliche IE-Stylesheet aus:
Das ist eine Menge Code für das gewünschte Ergebnis, beweist aber, dass es möglich ist, diese Funktionen zu verwenden. Wenn Sie einen Blick auf Abbildung 3 werfen, sehen Sie, dass wir unserem Ziel ziemlich nahe kommen.
Auch wenn diese Filter unhandlich und ein bisschen seltsam sind, sollten Sie sie in eigenen Projekten weiter erforschen, weil Sie dadurch IE-Benutzern ein ähnliches Ergebnis bieten können.
Denken Sie daran: Die Effekte, die wir in diesem Artikel untersucht haben, betreffen nur die Präsentation. Beim ursprünglichen Stylesheet haben wir sorgfältig Hintergrundfarben ausgesucht, mit denen der Text gut lesbar ist. Auch Browser, die kein CSS3 verstehen, können die Seite also auf lesbare Art und Weise darstellen.
Geolocation mit HTML5
Die Verwendung des kurzen Beispiels macht ausschließlich auf mobilen Geräten Sinn. Zwar kann der Anwendung zu Demonstrationszwecken auch künstlich „Beine gemacht“ werden, das Erfolgserlebnis stellt sich wahrscheinlich aber erst bei einer präzisen Positionsbestimmung mittels GPS und einem Browser ein, der in Bewegung ist. Versuchsanordnung für das folgende Beispiel war ein Android-Mobiltelefon, das die HTML-Seite während einer Fahrt auf der Autobahn anzeigte.
Wie in Abbildung 1 zu sehen ist, werden die jeweils letzten fünf ermittelten Positionen auf der Straßenkarte von Google Maps markiert. Sobald der Beobachter den dargestellten Bereich der Karte verlässt, wird die Karte um den nächsten Punkt zentriert. Der Aufruf der Geolocation-API wird wieder in window.onload
ausgeführt und sieht folgendermaßen aus:
Die eigentliche Arbeit findet in der Funktion moveMe()
statt:
Die Variable latlng
wird als LatLng-Objekt aus der Google Maps API erzeugt, wobei diesem Objekt die aktuellen Koordinaten übergeben werden. Sollte die aktuelle Position außerhalb des dargestellten Bereichs sein (!bounds.contains(latlng)
), wird die Karte über dem aktuellen Punkt neu zentriert. Wie das Array marker
wurde auch die Variable maxMarkers
am Anfang des Scripts global definiert und mit dem Wert 5 belegt. Enthält das Array marker
mehr als fünf Elemente, so wird das erste Element mit der shift
-Funktion aus dem Array entfernt und anschließend durch den Aufruf von setMap()
ohne weitere Parameter von der Karte gelöscht. Abschließend wird dem Array ein neues Objekt vom Typ Marker an der aktuellen Position hinzugefügt.
Im folgenden Beispiel wird die aktuelle Position auf einer Karte von OpenStreetMap dargestellt und mit einem Marker gekennzeichnet. Außerdem werden unterschiedliche Layer und eine Navigationsleiste von OpenStreetMap angezeigt. Abbildung 1 zeigt den Mapnik-Layer der OpenStreetMap mit dem Positions-Icon in der Mitte des Browsers.
Neben dem Navigationselement mit den vier Pfeilen wird die Zoom-Leiste an die Karten-Variable (map
) angehängt. Anschließend wird das Auswahl-Element für die unterschiedlichen Layer erzeugt (Control.LayerSwitcher
) und werden mehrere Layer zu der Karte hinzugefügt. Der Funktionsaufruf von map.addLayers()
nimmt dabei ein Array von neu erzeugten Karten-Objekten entgegen.
Abschließend erhält die Karte noch einen Layer für den Marker:
Das Success-Callback nach einer erfolgreichen Positionsbestimmung sieht so aus:
Die Idee zum abschließenden Beispiel entstand bei einem Auslandsaufenthalt mit einem neuen Smartphone: Das Programm ist ein digitales (Reise-)Tagebuch, das jeden Eintrag automatisch mit geografischen Koordinaten versieht und alle Einträge auf einer Karte anzeigen kann. Die hohen Daten-Roaming-Gebühren in Europa machten bald die Einbeziehung einer weiteren Technologie aus dem HTML5-Umfeld erforderlich, um die Kosten zu senken: Web Storage. Mithilfe der Web Storage API werden die Einträge lokal, in einem persistenten Speicher gehalten, wodurch die Anwendung auch ohne bestehende Datenverbindung funktioniert.
Bedienung
Die Anwendung ist sehr einfach aufgebaut (siehe Abbildung 1): Im linken oberen Bereich befindet sich das Textfeld zur Eingabe der Notiz. Durch das in HTML5 neu eingeführte placeholder
-Attribut zeigt der Browser eine Aufforderung zum Eingeben einer neuen Nachricht. Sind bereits Einträge vorhanden, erscheint im rechten Bereich ein Kartenausschnitt von Google Maps. Unterhalb folgt die Liste der Einträge, wobei außer dem Nachrichtentext noch die Position, der Zeitpunkt der Eingabe und eine Entfernung zum aktuellen Standort angegeben wird. Außerdem besteht die Möglichkeit, Nachrichten zu löschen oder den Standort vergrößert auf Google Maps darzustellen. Wie in Abbildung 1 zu sehen ist, wird bei vergrößerter Darstellung die Position mit einem Google-typischen Marker gekennzeichnet. Der Kreis um den Punkt bezeichnet die Genauigkeit der Positionsmessung.
Da man beim Entwickeln der Anwendung seine Position nicht ständig ändert,
Um die Anwendung gleich ausprobieren zu können, gibt es die Möglichkeit, einen Testdatensatz einzuspielen. Es handelt sich dabei um zum Teil frei erfundene Einträge und um solche, die der Autor während der Entwicklung selbst aufgenommen hat.
Wichtige Code-Fragmente
Der HTML-Code für die Anwendung stellt einige div
-Container-Elemente bereit, in denen später die Nachrichten (id='items'
) und die Karte (id='map'
) dargestellt werden. Wie schon eingangs erwähnt wurde, enthält das textarea
-Element das neue placeholder
-Attribut, das Anwendungen deutlich benutzerfreundlicher machen kann. Den drei button
-Schaltflächen wird der entsprechende onclick
-Event-Listener direkt zugewiesen.
Wesentlich interessanter als die wenigen Zeilen HTML-Code ist der dazugehörige JavaScript-Code. Zuerst werden eine Hilfsfunktion und drei globale Variablen definiert:
$
ist Ihnen bereits aus Positionsausgabe im Browser, bekannt. Sie erspart Ihnen auch hier Tipparbeit und trägt zur Lesbarkeit des Codes bei.Die Variable map
dient als Referenz auf den HTML-Bereich, in dem die Google-Maps-Darstellung Platz finden wird. my_pos
wird zur Berechnung der Entfernung benötigt und enthält die aktuelle Position, von der aus das Script aufgerufen wird. diaryItem
stellt die Struktur dar, nach der die einzelnen Einträge aufgebaut sind. Jeder Tagebucheintrag enthält eine ID (id
), Informationen zur Position (pos
), einen Zeitstempel (ts
) sowie die eingegebene Nachricht aus dem Textfeld (msg
).
Sobald die Seite vollständig geladen ist, beginnen die Bestimmung der aktuellen Position und die Anzeige bestehender Einträge:
Für den bereits bekannten Aufruf von getCurrentPosition()
wird die Option enableHighAccuracy
aktiviert. Die maximale Zeit für die Wiederverwendung einer bereits ermittelten Position ist eine Minute. Bei einer erfolgreichen Positionsbestimmung wird die vorher definierte globale Variable my_pos
mit den Werten aus der soeben bestimmten Position belegt und anschließend die Funktion showItems()
aufgerufen. Ein Fehler bei der Positionsbestimmung führt zum Aufruf von posError()
, einer Funktion, die die entsprechende Fehlermeldung in einem Dialogfenster ausgibt. Ist die Anzahl der Elemente im localStorage
größer als 0, wird außerdem noch die Funktion drawAllItems()
ausgeführt, die vorhandene Einträge auf Google Maps darstellt.
Die showItems
-Funktion setzt eine Zeichenkette aus allen Einträgen zusammen und belegt anschließend das HTML-Element mit der ID items
damit.
Die Variable i_array
wird mit dem Ergebnis der Funktion getAllItems()
befüllt, die den localStorage
ausliest, anschließend die Inhalte als Objekte in einem Array zurückgibt und außerdem eine Sortierung der Objekte nach dem Datum vornimmt.
Der Aufruf localStorage.getItem()
holt ein Element aus dem persistenten Speicher, das anschließend mit der Funktion JSON.parse
in ein JavaScript-Objekt umgewandelt wird. Voraussetzung dafür ist, dass das Objekt beim Abspeichern mit JSON.stringify
in eine Zeichenkette umgewandelt wurde (siehe weiter unten). Die Objekte werden mit i_array.push()
an das Ende des Arrays i_array
gehängt und im nächsten Schritt nach dem Datum sortiert. Um der JavaScript-Funktion sort
mitzuteilen, nach welchen Kriterien sortiert werden soll, wird sie mit einer anonymen Funktion erweitert. Um eine zeitliche Sortierung der Objekte zu ermöglichen, wird die Variable ts
aus den Objekten ausgewertet. Sie enthält die Zahl der Millisekunden seit dem 1.1.1970, ein Wert, der durch die JavaScript-Funktion new Date().getTime()
erzeugt wird. Liefert die anonyme Funktion einen negativen Wert zurück, so wird a hinter b angereiht, und bei einem positiven Wert kommt a vor b.
Jetzt bleibt noch die Frage zu klären, wie neue Einträge erzeugt und gespeichert werden. Die Funktion saveItem()
übernimmt diesen Teil und beginnt mit der Initialisierung einer lokalen Variable d, der die Struktur diaryItem
zugewiesen wird.
Sollte das Textfeld leer sein (d.msg = ''
), wird ein entsprechendes Dialogfenster angezeigt und die Funktion mit return
abgebrochen. Andernfalls wird der Zeitstempel auf den aktuellen Millisekundenwert gesetzt und die ID des Eintrags aus der Zeichenkette geonotes
_ und dem Zeitstempel zusammengesetzt. Sollten von einem Server mehrere Anwendungen auf den localStorage
zugreifen, so kann man über die vorangestellte Zeichenkette eine Unterscheidung der Daten vornehmen. Nach erfolgreicher Positionsbestimmung wird die Variable pos
innerhalb des diaryItem
-Objekts mit Koordinaten und den dazugehörigen Meta-Informationen befüllt und anschließend über JSON.stringify()
als JSON-Zeichenkette im localStorage
gespeichert.
Sollte der Browser keine Unterstützung für die Geolocation-API haben, speichert die Anwendung den Text dennoch und weist darauf hin, dass die entsprechende Unterstützung fehlt. Der abschließende Aufruf von showItems()
sorgt dafür, dass die Liste der Nachrichten aktualisiert wird.
Die Geolocation-API wurde zwar aus dem Kern der HTML5-Spezifikation entfernt und befindet sich nach der W3C-Nomenklatur
erst in einem frühen Stadium, sie ist aber vor allem bei mobilen Browsern schon weitgehend implementiert. Ein Grund für die rasche Umsetzung liegt sicher in der Kürze und der Abstraktion der Schnittstelle: Mit nur drei JavaScript-Aufrufen wird der gesamte Funktionsumfang abgedeckt. Die Spezifikation schreibt nicht vor, auf welche Art der Browser die Position zu bestimmen hat, sondern nur das Format, in dem das Ergebnis zurückgegeben wird.
Nach einer kurzen Einführung in das Wesen geografischer Daten stellen wir die neuen Funktionen anhand von mehreren kurzen Beispielen vor. Wenn Sie die Beispiele mit einem Smartphone ausprobieren, sollte sich schnell der Aha-Effekt einstellen.
Ein Wort zu geografischen Daten
Vielleicht ist Ihnen schon einmal eine Koordinatenangabe in der Form N47 16 06.6 E11 23 35.9 begegnet. Die Position wird in Grad-Minuten-Sekunden angegeben. In diesem Fall befindet sich der gesuchte Ort bei 47 Grad, 16 Minuten und 6,6 Sekunden nördlicher Breite und 11 Grad, 23 Minuten und 35,9 Sekunden östlicher Länge. Man bezeichnet solche Angaben als geografische Koordinaten. Leider haben diese Werte den großen Nachteil, dass man nur schwer mit ihnen rechnen kann, was nicht nur daran liegt, dass Menschen gewohnt sind, im Dezimalsystem zu denken. Da die Koordinaten eine Position auf dem Rotationsellipsoid Erde beschreiben, muss bei der Berechnung von Entfernungen die Krümmung der Oberfläche miteinbezogen werden.
Um diesen Zustand zu vereinfachen, werden in der Praxis meist projizierte Koordinaten verwendet.
Dabei wird das Rotationsellipsoid in Streifen zerschnitten, in denen die Entfernung zwischen Punkten linear gemessen werden kann. Viele Länder verwenden ein eigenes, auf die lokalen Bedürfnisse abgestimmtes Koordinatensystem. In Österreich werden zum Beispiel Daten meist im Bundesmeldenetz referenziert. Alle gängigen Koordinatensysteme sind mit einer numerischen Kennung versehen, ihrem EPSG-Code (verwaltet von der European Petroleum Survey Group).
Natürlich kann die Geolocation-API
nicht alle Koordinatensysteme berücksichtigen. Die X- und Y-Koordinaten werden daher nicht projiziert, sondern in geografischen Koordinaten in Dezimalgrad angegeben. Als geodätisches Referenzsystem schreibt der Standard das weit verbreitete World Geodetic System 1984 (WGS84) vor. Es beschreibt im Wesentlichen das darunterliegende Referenzellipsoid. Der Z-Wert wird in Metern über diesem Ellipsoid angegeben. Damit lässt sich jeder Punkt auf der Erde und im erdnahen Raum mit ausreichender Genauigkeit beschreiben.
Online-Kartendienste
Zur Darstellung von geografischen Daten im Browser gibt es mehrere Möglichkeiten: SVG eignet sich durch das flexible Koordinaten-System sehr gut, und mit canvas
könnten die Daten als Rasterbild gezeichnet werden. Am bequemsten ist es aber, eine bereits bestehende JavaScript-Bibliothek zu Hilfe zu nehmen. Von den im Internet frei verfügbaren Bibliotheken beschreiben wir im Folgenden Google Maps und OpenStreetMap genauer. Da der Kartendienst von Microsoft, Bing Maps, nur nach vorheriger Registrierung verwendet werden kann, gehen wir hier nicht weiter darauf ein.
Die zwei hier vorgestellten Bibliotheken verwenden zur Anzeige eine Mischung aus Raster- und Vektordaten. Um schnelle Ladezeiten zu ermöglichen, werden die Rasterbilder in Kacheln zerlegt und für alle Zoomstufen im Voraus berechnet. Dadurch entsteht der schrittweise Bildaufbau. Vektorinformationen werden je nach Browser in SVG oder für den Internet Explorer im Microsoftspezifischen Format VML angezeigt.
Google Maps
Google Maps ist zweifellos der am weitesten verbreitete Kartendienst im Internet. Viele Firmen verwenden den kostenlosen Service, um den eigenen Firmenstandort kartografisch darzustellen. Doch Google Maps hat weit mehr zu bieten, als nur Positionsmarker auf eine Karte zu setzen. Wie die Webseite Case Studies verrät, setzen über 150.000 Webseiten Google Maps ein, unter ihnen auch große Unternehmen wie die New York Times.
Die aktuelle Version der Bibliothek, V3, unterscheidet sich stark von vorangegangenen Versionen:
Zur Verwendung wird kein API-Schlüssel mehr benötigt (also keine Registrierung bei Google), und die Bibliothek wurde für die Verwendung auf mobilen Endgeräten optimiert. Wie so oft bei Produkten von Google ist der Komfort in der Programmierung sehr hoch. Für eine einfache Straßenkarte von Mitteleuropa reichen diese wenigen Zeilen HTML und JavaScript aus:
Beim Laden der Bibliothek muss der sensors
-Parameter angegeben werden. Wenn dieser Wert auf true
steht, kann das Endgerät seine Position bestimmen und der Anwendung mitteilen. Das ist vor allem bei mobilen Geräten (zum Beispiel Smartphones mit GPS) von Bedeutung. Ist die gesamte Seite geladen (window.onload
), wird ein neues Objekt vom Typ google.maps.Map
erzeugt, dessen Konstruktor als ersten Parameter das HTML-Element entgegennimmt, das zur Anzeige der Karte vorgesehen ist. Der zweite Parameter bestimmt als Liste von Optionen, wie und was auf der Karte dargestellt werden soll. In diesem Fall wird der Kartenmittelpunkt auf 47 Grad Nord und 11 Grad Ost mit der Zoomstufe 7 gesetzt (Zoomstufe 0 entspricht der Ansicht der gesamten Erde) und der Kartentyp über die Konstante google.maps.MapTypeId.ROADMAP
als Straßenkarte festgelegt.
Info
Da der Konstruktor des Kartenobjekts einen Verweis auf den Inhalt der HTML-Seite enthält, darf er erst aufgerufen werden, wenn die Webseite geladen ist – also zum Zeitpunkt window.onload
.
OpenStreetMap / OpenLayers
OpenStreetMap startete im Jahr 2004 mit dem sehr ambitionierten Ziel, eine umfassende und freie Plattform für Geodaten
weltweit zu werden. Anknüpfend an die erfolgreiche Methode von Wikipedia wollte man es den Benutzern einfach machen, geografische Elemente im persönlichen Umfeld aufzunehmen und im Internet zu speichern. Wenn Sie bedenken, wie ungleich schwieriger es ist, Geodaten zu editieren, ist der bisherige Stand des Projekts beeindruckend.
Im Umfeld des Projekts entstanden unterschiedliche Werkzeuge,
mit denen man Daten von den Servern von OpenStreetMap herunterladen und – eine entsprechende Berechtigung vorausgesetzt – hinaufladen und dort speichern kann. Die offenen Schnittstellen machen es für Software-Entwickler einfach, ihre Produkte an das System anzubinden.
Eine wesentliche Komponente für den Erfolg von OpenStreetMap ist eine einfache Möglichkeit für Webentwickler, Karten in ihre Webseiten einzubauen. Darum kümmert sich das Projekt OpenLayers. Die JavaScript-Bibliothek ist nicht auf den Einsatz mit OpenStreetMap beschränkt, kann aber in diesem Zusammenspiel ihre Stärken sehr gut ausspielen. Mit OpenLayers können Sie auch auf die Karten von Google, Microsoft, Yahoo und unzähligen anderen Geodiensten (auf der Basis der Standards WMS und WFS) zugreifen.
Ein minimales Beispiel für eine Straßenkarte von Mitteleuropa mit OpenLayers und OpenStreetMap sieht folgendermaßen aus:
Dem OpenLayers.Map
-Objekt wird ähnlich wie bei Google Maps ein HTML-div
-Element zur Darstellung zugewiesen und ein Layer vom Typ Osmarender
hinzugefügt, der Standard-Kartenansicht von OpenStreetMap (OSM
). Hier kommt eine Besonderheit von OpenStreetMap ins Spiel: Wie schon in Ein Wort zu geografischen Daten, erwähnt wurde, müssen dreidimensionale Informationen projiziert werden, um sie am Bildschirm in 2D darzustellen. Während Google Maps den Anwender nicht mit diesen Details belästigt und man ganz einfach die X- und Y-Koordination in Dezimalgrad angibt, verlangt OpenLayers, dass Angaben in Dezimalgrad zuerst in das entsprechende Koordinatensystem projiziert werden. Intern verwendet OpenLayers genauso wie Google Maps, Yahoo! Maps und Microsoft Bing Maps für die Kartendarstellung eine Projektion, die als Spherical Mercator (EPSG-Code 3785) bezeichnet wird. In Spherical Mercator werden Koordinaten nicht in Dezimalgrad, sondern in Metern verwaltet, weshalb die hier verwendeten Gradangaben mit dem Aufruf transform()
unter Angabe des EPSG-Codes der gewünschten Projektion (EPSG:4326) in das in der Karte verwendete Koordinatensystem (ermittelt durch die Funktion map.getProjectionObject())
konvertiert werden müssen.
Info
fixed
oder absolute
In der ersten Zeile des Listings wird eine Hilfsfunktion mit dem Namen $
definiert, die eine verkürzte Schreibweise der Funktion document.getElementById()
erlaubt (ähnlich einem Alias). Dieser Trick wurde aus der bekannten jQuery-Bibliothek übernommen und ist für das vorliegende Beispiel sehr angenehm, da die zu befüllenden Elemente auf der Webseite alle mit einem ID-Attribut gekennzeichnet sind.
window.onload
sicher, dass die Inhalte der Webseite vollständig geladen sind, bevor Referenzen auf HTML-Elemente gesetzt werden. Die erste if
-Abfrage überprüft, ob der Browser die Geolocation API unterstützt. Sollte dies nicht der Fall sein, wird eine entsprechende Meldung in das Element mit der ID status geschrieben. Andernfalls kommt die eigentliche Funktion zur Bestimmung der Position zum Einsatz: navigator.geolocation.getCurrentPosition()
.Beim Aufruf dieser Funktion muss der Browser laut Spezifikation nachfragen, ob es gewünscht ist,
dass die aktuelle Position ermittelt und der Webseite bekannt gegeben wird.
- Dem Funktionsaufruf werden drei Argumente übergeben:
- eine Funktion, die nach erfolgreicher Positionsbestimmung ausgeführt werden soll (Success-Callback),
- eine Funktion, die auf Fehler nach einer gescheiterten Positionsbestimmung reagieren kann (Error-Callback) sowie
- Wertepaare, die die Art der Positionsbestimmung beeinflussen.
Laut Spezifikation sind die beiden letzten Argumente optional, das Success-Callback muss immer angegeben werden. Um den JavaScript-Ablauf nicht zu blockieren, muss getCurrentPosition()
asynchron, sozusagen im Hintergrund ausgeführt werden, und erst nachdem die Position bekannt ist oder ein Fehler aufgetreten ist, kann die entsprechende Callback-Funktionen aufgerufen werden.
In diesem sehr kurzen Beispiel sind beide Callback-Funktionen als anonyme Funktionen implementiert, wobei der Fehlerfall nicht weiter behandelt wird. Das Wertepaar enableHighAccuracy: true
weist den Browser an, eine möglichst genaue Positionsbestimmung durchzuführen. Bei einem Android-Mobiltelefon bewirkt diese Einstellung zum Beispiel die Aktivierung des internen GPS-Sensors (mehr dazu finden Sie in Abschnitt Technischer Hintergrund der Positionsbestimmung). maximumAge
legt schließlich die Zeit in Millisekunden fest, in der eine bereits bestimmte Position wieder verwendet werden darf. Nach Ablauf dieser Zeitspanne muss die Position neu bestimmt werden – in unserem Fall alle zehn Minuten.
Nach erfolgreicher Positionsbestimmung enthält die Variable pos
des Success-Callbacks im sogenannten Position-Interface Angaben zur Koordinate (pos.coords
) sowie einen Zeitstempel in Millisekunden seit 1970 (pos.timestamp
). Abbildung 2 zeigt die verfügbaren Attribute mit ihren jeweiligen Werten, sofern welche vorhanden sind.
Zusätzlich zu latitude
, longitude
und altitude
liefert pos.coords
auch noch Informationen zur Genauigkeit der Position (accuracy
, altitudeAccuracy
) sowie zu möglicher Geschwindigkeit (speed
) und Richtung (heading
). Während Google-Chrome sich auf die in der Spezifikation geforderten Attribute beschränkt, gibt Firefox eine ganze Reihe zusätzlicher Informationen aus – unter anderem sogar Angaben zur Adresse, wie der Code zeigt, einen Auszug aus dem Ergebnis von JSON.stringify(pos)
bietet:
Erstaunlich viele Informationen, die der Browser hier zur Verfügung stellt! Woher diese Informationen kommen, erklärt der folgende Abschnitt.
Technischer Hintergrund der Positionsbestimmung
Browser, die die Geolocation-API unterstützen, können eine wesentlich höhere Genauigkeit erreichen, indem sie auf weitere technische Möglichkeiten zurückgreifen. Aktuell sind folgende Methoden im Einsatz:
- 1. Bei PCs mit kabelgebundenem Internetanschluss wird die Position anhand der IP-Adresse bestimmt. Diese Bestimmung ist erwartungsgemäß ziemlich ungenau.
- 2. Bei einer vorhandenen Wireless-LAN-Verbindung kann eine wesentlich präzisere Positionsbestimmung durchgeführt werden. Google sammelte dazu weltweit Daten von öffentlichen und privaten WLANs.
- 3. Verfügt die Hardware über einen Mobilfunk-Chip (zum Beispiel bei einem Smartphone), wird versucht, die Position innerhalb des Mobilfunk-Netzwerks zu errechnen.
- 4. Verfügt die Hardware außerdem über einen GPS-Sensor, kann die Position noch genauer bestimmt werden. GPS ist ein satellitengestütztes Positionierungsverfahren und kann bei günstigen Verhältnissen (außerhalb von Gebäuden, wenig Abschattung des Horizonts, …) selbst mit billigen Sensoren eine Genauigkeit im Meterbereich erreichen.
Nur der GPS-Sensor funktioniert offline, die Methoden 1-3 benötigen Internet-Zugriff und werden durch einen Serverdienst umgesetzt. Diese Serverdienste gibt es von Google (Google Location Service, verwendet in Firefox, Chrome und Opera) und von einer weiteren amerikanischen Firma namens Skyhook Wireless (verwendet in Safari und in frühen Versionen von Opera).
Aber wie kommen diese Dienstleister zu den Standort-Informationen von Wireless- und Mobilfunk-Netzwerken?
Parallel zu den Fotos, die Google für den Dienst Street View aufnimmt, speichern die Google-StreetView-Aufnahmefahrzeuge auch Informationen zu öffentlichen und privaten WLANs ab. Nachdem im Frühjahr 2010 bekannt wurde, dass dabei nicht nur die MAC-Adresse und die SSID des WLANs, sondern auch Nutzdaten mitprotokolliert wurden, fiel ein schlechtes Licht auf Google, und der Konzern musste sich mehrmals öffentlich entschuldigen.
Damit aber noch nicht genug: Sofern der Browser Zugriff auf die Informationen eines Mobilfunk-Netzwerks oder Wireless-LAN-Routers hat, werden diese bei jedem Aufruf des Dienstes mitgesendet. Für Google betrifft das vor allem Mobilfunkgeräte mit dem Betriebssystem Android, Skyhook profitiert hier von den iPhone-Benutzern. Die Kombination der vorgestellten Methoden führt zu einem sehr großen Datensatz von Geodaten, über den diese beiden Dienstleister verfügen und den sie durch Crowdsourcing ständig aktualisieren (auch wenn die Benutzer als Datenlieferanten davon nichts bemerken).
Struktur in HTML5
Wir machen heutzutage eine Menge mit Ajax in unseren Webanwendungen. Typischerweise lösen wir irgendeinen visuellen Effekt aus, um den Benutzer darauf hinzuweisen, dass sich etwas auf der Seite verändert hat. Personen, die einen Screenreader verwenden, sind aber aus naheliegenden Gründen nicht dazu in der Lage, solche visuellen Hinweise zu erkennen. Die WIA-ARIA-Spezifikation bietet eine Alternativlösung, die derzeit in IE, Firefox und Safari mit verschiedenen beliebten Bildschirmlesegeräten funktioniert.
Der Kommunikationsdirektor möchte eine neue Homepage. Sie soll Links auf die Abschnitte „Services“, „Contact“ und „About“ enthalten. Er besteht darauf, dass die Seite nicht gescrollt werden soll, weil „viele Leute es hassen, zu scrollen“. Er möchte, dass Sie einen Prototyp für die Seite mit einem horizontalen Menü erstellen, über das per Klick der Hauptinhalt der Seite geändert werden kann. Das ist einfach zu implementieren, und mit dem Attribut aria-live
können wir sogar etwas tun, das bisher nicht so ohne Weiteres möglich war – eine derartige Oberfläche screenreader freundlich implementieren.
Erstellen wir eine einfache Oberfläche wie in Abbildung 1. Wir stellen den gesamten Inhalt auf die Homepage. Falls JavaScript verfügbar ist, blenden wir alles bis auf den ersten Eintrag aus. Wir lassen die Navigationslinks mit Seitenankern auf den jeweiligen Abschnitt verweisen und verwenden jQuery, um diese Links in Events umzuwandeln, die den Hauptinhalt austauschen. Leute mit JavaScript sehen, was unser Direktor möchte, und Menschen ohne JavaScript können trotzdem den gesamten Inhalt der Seite sehen.
Die Seite erstellen
Wir beginnen damit, eine einfache HTML5-Seite zu erstellen und fügen unseren Abschnitt „Welcome“ ein, der standardmäßig angezeigt wird, wenn Benutzer die Seite besuchen. Hier sehen Sie den Code für die Seite mit der Navigationsleiste und den Jump-Links:
Der Abschnitt „Welcome“ erhält die ID welcome, die dem Anker in der Navigationsleiste entspricht. Die übrigen Seitenabschnitte können wir auf dieselbe Weise deklarieren.
Unsere vier Inhaltsbereiche verpacken wir in das folgende Markup:
Die Attribute in dieser Zeile teilen Screenreadern mit, dass dieser Seitenbereich aktualisiert wird.
Höfliche, aber bestimmte Aktualisierung
Es gibt zwei verschiedene Methoden, um den Benutzer bei der Verwendung von aria-live
auf Änderungen der Seite hinzuweisen. Die Methode polite
wurde entwickelt, um den Arbeitsfluss des Benutzers nicht zu unterbrechen. Wenn der Screenreader beispielsweise einen Satz vorliest, während ein Bereich der Seite aktualisiert wird und der Modus polite
gewählt ist, liest das Bildschirmlesegerät den aktuellen Satz zu Ende. Ist dagegen der Modus assertive
festgelegt, wird für diesen Inhalt eine hohe Priorität angenommen: Der Screenreader hört auf, den aktuellen Satz vorzulesen und beginnt mit dem Vorlesen des neuen Inhalts. Es ist wirklich wichtig, dass Sie sich bei der Entwicklung Ihrer Website für die richtige Art der Unterbrechung entscheiden. Der übermäßige Einsatz von assertive
kann dazu führen, dass Ihre Benutzer die Orientierung verlieren und verwirrt werden. Verwenden Sie daher assertive
nur, wenn es absolut notwendig ist. In unserem Fall ist es die richtige Entscheidung, weil wir den anderen Inhalt ausblenden.
Alles vorlesen lassen
Der zweite Parameter, aria-atomic=true
, weist den Screenreader an, den gesamten Inhalt des veränderten Abschnitts zu lesen. Wenn wir dafür den Wert false
angeben, weisen wir damit den Screenreader an, nur veränderte Knoten vorzulesen. Wir ersetzen den gesamten Inhalt, deshalb ist es sinnvoll, das Bildschirmlesegerät anzuweisen, auch alles vorzulesen. Wenn wir dagegen nur ein einzelnes Listenelement ersetzen oder etwas mit Ajax zu einer Tabelle hinzufügen, wäre false die richtige Entscheidung.
Bereiche ausblenden
Zum Ausblenden der Bereiche müssen wir ein bisschen JavaScript schreiben und in unsere Seite einfügen. Wir erstellen hierzu eine Datei mit dem Namen application.js und binden diese Datei ebenso wie die jQuery-Bibliothek in unsere Seite ein.
Unsere Datei application.js enthält ein einfaches Skript:
In Zeile 11 blenden wir die Abschnitte „services“, „about“ und „contact“ aus. Außerdem wenden wir darauf die Klasse hidden
an und weisen in der nächsten Zeile dem Abschnitt welcome die Klasse visible
zu. Auf diese Weise ist es wirklich einfach festzustellen, welche Abschnitte beim Umschalten aktiviert oder deaktiviert werden müssen.
In Zeile 14 fangen wir sämtliche Klicks auf die Navigationsleiste ab und ermitteln in Zeile 17, welches Element angeklickt wurde. Wenn der Benutzer auf einen Link geklickt hat, prüfen wir, ob der entsprechende Abschnitt versteckt ist. Über das href
-Attribut des geklickten Links können wir mit jQuery-Selektoren den entsprechenden Abschnitt finden, wie Sie in Zeile 19 sehen.
Ist der Abschnitt ausgeblendet, zeigen wir den gewählten Abschnitt an und blenden alle anderen aus. Das war’s. Der Screenreader sollte die Änderungen dieses Bereichs feststellen.
Ausweichlösung
Wie auch die Rollen kann diese Lösung sofort mit den neuesten Versionen von Screenreadern verwendet werden. Wenn Sie sich an einige bewährte Regeln halten, zum Beispiel an den Verzicht auf aufdringliches JavaScript, haben wir eine einfache Implementierung, die für ein durchaus breites Publikum funktioniert. Ältere Browser und Bildschirmlesegeräte ignorieren die zusätzlichen Attribute, sodass es auch keine Gefahr darstellt, wenn wir sie in unseren Markup einfügen.
Die Zukunft
HTML5 und die WIA-ARIA-Spezifikation haben den Weg für ein wesentlich barrierefreieres Web geebnet. Mit der Möglichkeit, veränderliche Bereiche der Seite zu kennzeichnen, können Entwickler umfangreichere JavaScript-Anwendungen entwickeln, ohne sich allzu viele Gedanken über Barrierefreiheit machen zu müssen.
Zu Beginn würde ich gern mit Ihnen über ein ernst zu nehmendes Problem sprechen, von dem heutzutage viele Webentwickler betroffen sind: Die Divitis greift um sich – ein chronisches Syndrom, das dazu führt, dass Webentwickler Elemente in zusätzliche div
-Tags mit IDs wie zum Beispiel banner
, sidebar
, article
und footer
verpacken. Dieses Syndrom ist in hohem Maße ansteckend. Divitis verbreitet sich extrem schnell von Entwickler zu Entwickler. Und da divs für das bloße Auge unsichtbar sind, bleiben insbesondere leichte Fälle von Divitis unter Umständen jahrelang unbemerkt.
Ein häufiges Symptom von Divitis sieht so aus:
Hier wurde eine ungeordnete Liste, die ihrerseits ja bereits ein Blockelement ist, in zwei div
-Tags eingebettet, die auch wiederum Blockelemente sind. Die id
-Attribute dieser Wrapper-Elemente erklären zwar, welche Aufgabe sie haben. Aber Sie können zumindest einen dieser Wrapper entfernen und zum gleichen Ergebnis kommen. Diese übermäßige Verwendung von Markup führt zu aufgeblähten Seiten, die schwierig zu gestalten und zu pflegen sind.
Es gibt jedoch Hoffnung: Die HTML5-Spezifikation liefert ein Heilmittel in Form von neuen semantischen Tags, die ihren Inhalt beschreiben. Weil so viele Entwickler Seitenleisten, Kopfzeilen, Fußzeilen und Abschnitte in ihren Designs verwenden, werden mit der HTML5-Spezifikation spezielle Tags eingeführt, um Seiten in solche logischen Abschnitte zu unterteilen. Machen wir uns mit diesen neuen Elementen an die Arbeit. Mit HTML5 können wir der Divitis noch in unserer Generation ein Ende setzen.
Neben den neuen strukturellen Tags werden wir auch das meter
-Element besprechen und Ihnen zeigen, wie Sie in HTML5 mit den neuen benutzerdefinierten Attributen Daten in Elemente einbetten können, ohne dafür Klassen oder existierende Attribute zu missbrauchen. Mit anderen Worten werden wir herausfinden, wie wir das richtige Tag für die richtige Aufgabe einsetzen.
In einem Blog finden Sie jede Menge Inhalte, die nach strukturiertem Markup schreien.
Sie brauchen Kopfzeilen, Fußzeilen, mehrere Navigationsmöglichkeiten (Archive, Blogrolls und interne Links) und natürlich Artikel oder Beiträge. Basteln wir mit HTML5 ein Mock-up für die Startseite des Blogs.
In Abbildung 1 sehen Sie, worum es geht. Es handelt sich um eine typische Blog-Struktur, mit einer Hauptkopfzeile und einer horizontalen Navigation darunter. Im Hauptbereich erhält jeder Artikel eine Kopf- und eine Fußzeile. Artikel können außerdem einen Zitatkasten oder Sekundärinhalte enthalten. Zum Abschluss erhält die Seite eine Fußzeile für Kontakt- und Copyright-Informationen. An der Struktur an sich ist nichts außergewöhnlich. Allerdings bauen wir sie diesmal nicht aus einer Menge div
-Tags auf, sondern verwenden spezielle Tags, um die jeweiligen Abschnitte zu beschreiben.
Wenn wir fertig sind, erhalten wir etwas, das in etwa wie Abbildung 2 aussieht.
Auf den Doctype kommt es an
Wir möchten die neuen Elemente von HTML5 verwenden. Deshalb müssen wir Browsern und Validierern mitteilen, welche Tags wir verwenden. Erstellen Sie eine neue Seite mit dem Namen index.html, und schreiben Sie die folgende einfache HTML5-Vorlage in die Datei.
Sehen Sie sich mal den Doctype in Zeile 1 an. Mehr brauchen wir für den HTML5-Doctype nicht zu schreiben. Wenn Sie regelmäßig Webseiten erstellen, sind Sie wahrscheinlich eher mit diesen langen, schwer zu merkenden Doctypes für XHTML vertraut:
Zum Vergleich noch einmal der HTML5-Doctype:
Das ist doch viel einfacher und viel leichter zu merken!
Ein Doctype hat zweierlei Aufgaben. Erstens hilft er Validierern festzustellen, welche Validierungsregeln für die Validierung des Codes herangezogen werden müssen. Außerdem zwingt der Doctype den Internet Explorer 6, 7 und 8, in den „Standards Mode“ zu wechseln, was von entscheidender Bedeutung ist, wenn Sie Webseiten erstellen, die in allen Browsern funktionieren sollen. Der HTML5-Doctype erfüllt beide Anforderungen und wird sogar vom Internet Explorer 6 erkannt.
Kopfzeilen
Kopfzeilen (nicht zu verwechseln mit Überschriften wie etwa h1
, h2
und h3
) können alle möglichen Arten von Inhalten enthalten – vom Unternehmenslogo bis hin zum Suchfeld. Die Kopfzeile unseres Blogs soll zunächst nur den Titel des Blogs enthalten.
Sie sind nicht auf eine Kopfzeile pro Seite beschränkt. Jeder einzelne Abschnitt oder Artikel kann eine eigene Kopfzeile enthalten. Da kann es hilfreich sein, Ihre Elemente mit dem id
-Attribut eindeutig zu kennzeichnen, wie ich das in Zeile 1 getan habe. Mit einer eindeutigen ID sind Elemente mit CSS einfach zu gestalten oder mit JavaScript ausfindig zu machen.
Semantisches Markup
Semantisches Markup dient dazu, Ihren Inhalt zu beschreiben. Wenn Sie bereits seit mehreren Jahren Webseiten entwickeln, teilen Sie Ihre Seiten wahrscheinlich in verschiedene Abschnitte wie header
, footer
und sidebar
auf. Dadurch können Sie die einzelnen Abschnitte der Seite leichter identifizieren, wenn Sie Stylesheets und andere Formatierungen darauf anwenden.
Semantisches Markup macht es Maschinen und Menschen gleichermaßen leicht, die Bedeutung und den Kontext des Inhalts zu verstehen. Die neuen Markup-Tags von HTML5 wie header
und nav
sollen Ihnen genau dabei helfen.
Fußzeilen
Das footer
-Element definiert Fußzeileninformationen für ein Dokument oder einen angrenzenden Abschnitt. Fußzeilen auf Websites kennen Sie bereits. Üblicherweise stehen darin Informationen wie das Copyright-Datum oder darüber, wem die Website gehört. Der Spezifikation entsprechend können wir auch mehrere Fußzeilen in einem Dokument haben. Das bedeutet also, dass wir auch innerhalb unserer Blog-Artikel Fußzeilen verwenden können.
Für den Moment definieren wir einfach eine einfache Fußzeile für unsere Seite. Da wir mehr als eine Fußzeile verwenden können, geben wir dieser genauso wie der Kopfzeile eine ID. Dadurch können wir diese bestimmte Fußzeile eindeutig identifizieren, wenn wir das Element und seine Kinder stylen möchten.
Die Fußzeile enthält lediglich ein Copyright-Datum. Jedoch können Fußzeilen genau wie Kopfzeilen auch andere Elemente enthalten, wie etwa Navigationselemente.
Navigation
Die Navigation ist für den Erfolg einer Website entscheidend. Ihre Besucher werden nicht lange bleiben, wenn sie nicht das finden, was sie suchen. Daher ist es absolut sinnvoll, dass die Navigation ein eigenes HTML-Tag erhält.
Fügen wir einen Navigationsabschnitt in die Kopfzeile unseres Dokuments ein. Wir erstellen Links auf die Homepage des Blogs, das Archiv, eine Liste mit den Verfassern von Beiträgen zum Blog und einen Link auf die Kontaktseite.
Ihre Seite kann nicht nur mehrere Kopf- und Fußzeilen, sondern auch mehrere Navigationselemente enthalten. Die Navigation befindet sich häufig in der Kopfzeile und in der Fußzeile, die Sie nun explizit kennzeichnen können. Die Fußzeile unseres Blogs braucht Links auf die Homepage von HTML-Info, eine Seite über das Unternehmen sowie Links auf die Nutzungsbedingungen und Datenschutzrichtlinien. Diese Links fügen wir in der Seite als eine weitere ungeordnete Liste im Element footer
ein.
Das Aussehen der beiden Navigationsleisten passen wir später mit CSS an. Achten Sie daher zunächst nicht so sehr auf die Optik. Die Aufgabe dieser neuen Elemente ist es, den Inhalt zu beschreiben, nicht aber, das Aussehen des Inhalt zu bestimmen.
Abschnitte und Artikel
Abschnitte sind die logischen Bereiche einer Seite. Deshalb gibt es nun das Element section
, um das oft missbrauchte div
-Tag bei der Beschreibung logischer Abschnitte einer Seite abzulösen.
Übertreiben Sie es aber nicht mit den Abschnitten. Setzen Sie sie ein, um Ihren Inhalt logisch zu gliedern. Hier haben wir einen Abschnitt erstellt, der alle Beiträge in einem Blog enthält. Jedoch soll nicht jeder Beitrag einen eigenen Abschnitt erhalten. Dafür gibt es ein besser geeignetes Tag.
Artikel
Das article
-Tag ist das perfekte Element, um den Inhalt einer Webseite zu beschreiben. Mit den vielen Elementen auf einer Seite – Kopfzeilen, Fußzeilen, Navigationselemente, Werbung, Widgets, Blogrolls und Lesezeichen für soziale Medien – könnten Sie glatt vergessen, dass die Benutzer Ihre Website wegen des von Ihnen angebotenen Inhalts besuchen. Das article
-Tag hilft Ihnen dabei, diesen Inhalt zu beschreiben.
Jeder unserer Artikel besteht aus einer Kopfzeile, dem eigentlichen Inhalt und einer Fußzeile. So definieren wir einen vollständigen Artikel:
Joe fragt …
Was ist der Unterschied zwischen Artikeln und Abschnitten?
Stellen Sie sich einen Abschnitt als logischen Teil eines Dokuments vor. Einen Artikel können Sie sich als den eigentlichen Inhalt vorstellen, wie etwa einen Artikel in einer Zeitschrift, einen Beitrag in einem Blog oder eine aktuelle Meldung.
Die neuen Tags beschreiben genau den Inhalt, den sie enthalten.
Abschnitte können mehrere Artikel enthalten, und Artikel können aus mehreren Abschnitten bestehen. Ein Abschnitt ist wie der Sportteil einer Zeitung. Der Sportteil enthält viele Artikel. Jeder dieser Artikel kann wiederum aus mehreren eigenständigen Abschnitten bestehen. Bestimmte Abschnitte wie Kopfzeilen und Fußzeilen erhalten eigene Tags. Ein Abschnitt ist ein allgemein gehalteneres Element, mit dem Sie andere Elemente logisch gruppieren können.
Bei semantischem Markup geht es darum, die Bedeutung Ihres Inhalts zu vermitteln.
In einem Artikel können wir die Elemente header
und footer
verwenden, wodurch es wesentlich einfacher wird, diese bestimmten Abschnitte zu beschreiben. Wir können unseren Artikel auch mit dem Element section
in mehrere Abschnitte unterteilen.
Das aside
-Tag und Seitenleisten
Manche Inhalte ergänzen etwas zum eigentlichen Inhalt, wie etwa Zitatkästen, Diagramme, weiterführende Gedanken oder entsprechende Links. Mit dem neuen aside
-Tag können Sie diese Elemente kenntlich machen.
Das Zitat schreiben wir in ein aside
-Element. Das aside
-Element verschachteln wir wiederum in den Artikel und platzieren es damit nahe beim zugehörigen Inhalt.
So sieht unser vollständiger Abschnitt mit dem aside
-Element aus:
Nun müssen wir nur noch den Abschnitt für die Seitenleiste einfügen.
aside
-Elemente sind keine Seitenleisten
Unser Blog enthält eine rechte Seitenleiste mit den Archiven für den Blog. Falls Sie jetzt glauben, wir könnten das aside
-Tag verwenden, um die Seitenleiste unseres Blogs zu definieren, liegen Sie leider falsch. Sie könnten das zwar so machen, aber es widerspricht dem Sinn der Spezifikation. aside
wurde entwickelt, um die Inhalte anzuzeigen, die zu einem Artikel gehören: die richtige Stelle also, um entsprechende Links, ein Glossar oder einen Zitatkasten anzuzeigen.
Als Markup für unsere Seitenleiste mit der Liste der bisherigen Archive verwenden wir ein weiteres section
-Tag sowie ein nav
-Tag:
Damit haben wir schon unsere Blog-Struktur. Jetzt können wir damit beginnen, die neuen Elemente zu stylen.
Styling
Genau wie auf div
-Tags können wir auch auf die neuen Elemente Stilregeln anwenden. Zunächst erstellen wir ein neues Stylesheet mit dem Namen style.css. Anschließend verknüpfen wir es mit unserem HTML-Dokument, indem wir im Header einen Link auf das Stylesheet einfügen:
Als Erstes zentrieren wir den Inhalt der Seite und legen einige grundlegenden Schriftarten fest:
Dann definieren wir die Breite der Kopfzeile:
Für die Navigationslinks wandeln wir die Listen mit Aufzählungszeichen in horizontale Navigationsleisten um:
Der Abschnitt posts muss gefloatet werden und eine feste Breite erhalten. Außerdem müssen wir auch den Callout im Artikel floaten. Bei der Gelegenheit vergrößern wir gleich noch die Schrift für den Callout.
Messwerte und Fortschrittsbalken
Wenn Sie ein Spendenbarometer oder einen Fortschrittsbalken für den Upload in einer Webanwendung implementieren möchten, sollten Sie einen Blick auf die Elemente meter
und progress
werfen, die mit HTML5 eingeführt wurden.
Mit dem Element meter
können wir einen festen Punkt auf einer Messskala mit einem Mindest- und einem Höchstwert semantisch beschreiben. Damit Ihr Messwert mit der Spezifikation harmoniert, sollten Sie ihn nicht für beliebige Minimal- oder Maximalwerte wie Höhe oder Gewicht verwenden, außer Sie haben einen bestimmten Grenzwert festgelegt. Wenn wir beispielsweise auf einer Website zum Sammeln von Spenden zeigen möchten, wie weit wir noch von dem Ziel $5.000 entfernt sind, lässt sich das so beschreiben:
Das Element progress
ist einem Messwert sehr ähnlich, wurde aber dafür entwickelt, einen aktiven Fortschritt darzustellen, zum Beispiel beim Hochladen einer Datei. Ein Messwert zeigt dagegen eine Messung an, die nicht mehr verändert wird, wie etwa einen Schnappschuss des Speicherplatzes, der auf einem Server einem bestimmten Benutzer noch zur Verfügung steht. Das Markup für einen Fortschrittsbalken ist dem eines meter
-Elements aber sehr ähnlich:
Die Elemente meter
und progress
lassen sich noch in keinem Browser darstellen. Sie können jedoch mit JavaScript die Werte aus dem meter
-Element auslesen und selbst abbilden, das Element meter
oder progress
also dafür verwenden, die Daten semantisch zu beschreiben.
Außerdem müssen wir die Seitenleiste floaten und eine feste Breite dafür festlegen:
Und wir müssen die Fußzeile definieren. Wir löschen die Floats in der Fußzeile, sodass sie im unteren Teil der Seite zu liegen kommt.
Das sind nur die wichtigsten Stilregeln in Kürze. Ich bin mir sicher, dass Sie das alles noch viel, viel besser aussehen lassen können.
Ausweichlösung
Das funktioniert bereits alles wunderbar in Firefox, Chrome und Safari. Allerdings werden die Leute im Management nicht allzu glücklich sein, wenn sie das Chaos sehen, das der Internet Explorer aus unserer Seite macht. Der Inhalt wird zwar wunderbar dargestellt. Aber da der IE diese Elemente nicht kennt, kann er auch keine Stilregeln darauf anwenden. Die Seite erinnert deshalb eher an die Mitte der Neunziger-Jahre.
Mit dem IE können wir diese Elemente nur stylen, wenn wir sie mit JavaScript als Teil des Dokuments definieren. Wie sich herausstellt, ist das wirklich einfach. Wir fügen den Code in den Abschnitt head
der Seite ein, sodass er ausgeführt wird, bevor der Browser irgendwelche Elemente rendert. Außerdem schreiben wir den Code in einen bedingten Kommentar – eine besondere Art von Kommentar, die nur der Internet Explorer verarbeitet.
Solche Kommentare gelten nur für Versionen des Internet Explorer, die älter als Version 9.0 sind. Wenn wir die Seite jetzt noch einmal laden, wird sie richtig dargestellt.
Allerdings machen wir uns dadurch von JavaScript abhängig. Das sollten Sie bedenken.
Für den verbesserten Aufbau und die Lesbarkeit des Dokuments lohnt sich das durchaus. Außerdem gibt es keine Probleme bezüglich der Barrierefreiheit, da der Inhalt nach wie vor angezeigt und von einem Bildschirmlesegerät vorgelesen werden kann. Die Darstellung wirkt lediglich hoffnungslos altmodisch für Benutzer, die JavaScript absichtlich deaktiviert haben.
Mit diesem Ansatz können Sie auch wunderbar zusätzliche Elemente unterstützen oder verstehen, wie Sie eine entsprechende Unterstützung schreiben können.
„Wie bitte kann ich HTML5 einsetzen, wenn ältere Browser es nicht unterstützen?“ Ist das die Frage, die Ihnen auf der Zunge liegt? Jedoch wäre die Formulierung der Frage bereits irreführend. HTML5 ist nicht eine große Sache, es ist eine Sammlung separater Funktionalitäten. Es ergäbe also überhaupt keinen Sinn, zu prüfen, ob „HTML5 insgesamt“ unterstützt wird. Dagegen können Sie aber prüfen, ob bestimmte Features wie Canvas, Video oder Geolocation unterstützt werden.
Erkennungstechniken
Wenn Ihr Browser eine Webseite darstellt, konstruiert er ein Document Object Model (DOM), eine Sammlung von Objekten, die die HTML-Elemente auf der Seite darstellen. Jedes Element – jedes <p>
, jedes <div>
, jedes <span>
– wird im DOM durch ein anderes Objekt dargestellt. (Es gibt auch globale Objekt wie window
und document
, die nicht an spezifische Elemente gebunden sind.)
Alle DOM-Objekte teilen einen Satz gemeinsamer Eigenschaften, aber einige Objekte haben zusätzliche Eigenschaften, die andere nicht haben. In Browsern, die HTML5-Funktionen unterstützen, besitzen bestimmte Objekte bestimmte eindeutige Eigenschaften. Ein kurzer Blick auf das DOM sagt Ihnen also, welche Funktionen unterstützt werden.
Es gibt vier grundlegende Techniken, um zu erkennen, ob ein Browser eine bestimmte Funktion unterstützt. In aufsteigender Komplexität sind das:
- 1. Prüfen, ob ein globales Objekt (wie
window
odernavigator
) eine bestimmte Eigenschaft unterstützt.
Ein Beispiel für eine derartige Prüfung der Geolocation-Unterstützung finden Sie im Abschnitt „Geolocation“. - 2. Ein Element erstellen und dann prüfen, ob dieses Element eine bestimmte Eigenschaft bietet.
Ein Beispiel für eine derartige Prüfung der Canvas-Unterstützung finden Sie im Abschnitt „Canvas“. - 3. Ein Element erstellen und prüfen, ob dieses Element eine bestimmte Methode bietet, und gegebenenfalls dann die Methode aufrufen, um den Wert zu prüfen, den sie liefert.
Ein Beispiel für eine derartige Erkennung der unterstützten Videoformate finden Sie im Abschnitt „Videoformate“. - 4. Ein Element erstellen, eine bestimmte Eigenschaft auf einen bestimmten Wert setzen und dann prüfen, ob die Eigenschaft den Wert bewahrt hat.
Ein Beispiel für eine derartige Prüfung der unterstützten<input>
-Typen finden Sie im Abschnitt „input-Typen“.
Modernizr: Eine Bibliothek zur HTML5-Erkennung
<script>
-Element ein:Modernizr wird automatisch ausgeführt. Es gibt keine modernizr_init()
-Funktion, die aufgerufen werden muss. Bei der Ausführung erstellt Modernizr ein globales Objekt namens Modernizr, das einen Satz Boolescher Eigenschaften für jede Funktion enthält, die erkannt wurde. Unterstützt Ihr Browser die Canvas-API, wird beispielsweise die Eigenschaft Modernizr.canvas auf true
gesetzt. Unterstützt er die Canvas-API nicht, wird Modernizr.canvas auf false
gesetzt:
Canvas
<canvas>
-Element (HTML5-Spezifikation – canvas-Element) als „auflösungsunabhängige Zeichenfläche, die zur Erstellung von Diagrammen, Grafiken für Spiele oder anderen visuellen Bildern genutzt werden kann.“ Ein Canvas ist ein Rechteck in Ihrer Seite, in das Sie mit JavaScript alles zeichnen können, was Ihnen beliebt. HTML5 definiert eine Funktionsmenge (die Canvas-API) zum Zeichnen von Figuren, Definieren von Pfaden, Erstellen von Verläufen und Anwenden von Transformationen.Die Prüfung auf Canvas-API-Unterstützung nutzt Erkennungstechnik Nummer 2 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser die Canvas-API unterstützt, hat das von ihr erstellte DOM-Objekt zur Repräsentation eines <canvas>
-Elements eine getContext()
-Methode. Unterstützt er die Canvas-API nicht, hat das für ein <canvas>
-Element erstellte DOM-Objekt nur einen Satz allgemeiner Eigenschaften, keine Canvas-spezifischen Eigenschaften. Canvas-Unterstützung können Sie mit folgender Funktion prüfen:
Diese Funktion erstellt zunächst ein ungenutztes <canvas>
-Element:
Dieses Element wird nie in Ihre Seite eingebunden und deswegen von keinem gesehen. Es geistert bloß im Speicher herum, bewegt sich nicht und tut nichts – wie ein Kanu auf einem trägen Fluss.
Nachdem Sie dieses <canvas>
-Element erstellt haben, prüfen Sie, ob es die Methode getContext()
bietet. Diese Methode gibt es nur, wenn Ihr Browser die Canvas-API unterstützt:
Schließlich nutzen Sie den Trick mit der doppelten Negation, um das Ergebnis in einen Booleschen Wert (true
oder false
) umzuwandeln:
Diese Funktion erkennt die Unterstützung für die meisten Funktionen der API, einschließlich Figuren, Pfaden, Verläufen und Mustern. Die externe explorercanvas-Bibliothek, die die Canvas-API in Microsofts Internet Explorer bereitstellt, erkennt es nicht.
Statt diese Funktion selbst zu schreiben, können Sie Modernizr nutzen, um die Unterstützung für die Canvas-API zu prüfen:
Es gibt eine eigene Prüfung für die Canvas-Text-API, die wir Ihnen gleich vorstellen werden.
Canvas-Text
Selbst wenn Ihr Browser die Canvas-API unterstützt, kann es sein, dass er die Canvas-Text-API nicht unterstützt. Die Canvas-API ist mit der Zeit gewachsen, und die Textfunktionen wurden recht spät eingeführt. Einige Browser boten Canvas-Unterstützung, bevor die Text-API vollständig war.
Die Prüfung für die Canvas-Text-API-Unterstützung nutzt erneut Erkennungstechnik Nummer 2 (siehe dazu den Abschnitt „Erkennungstechniken“. Falls Ihr Browser die Canvas-API unterstützt, bietet das DOM-Objekt, das zur Repräsentation eines <canvas>
-Elements genutzt wird, eine getContext()
-Methode. Sollte er die Canvas-API nicht unterstützen, hat das für ein <canvas>
-Element erstellte DOM-Objekt nur allgemeine Eigenschaften, keine Canvas-spezifischen. Canvas-Text-Unterstützung können Sie mit folgender Funktion prüfen:
Zunächst prüft diese Funktion mit der im vorangehenden Abschnitt vorgestellten Funktion supports_canvas()
die Canvas-Unterstützung:
Unterstützt Ihr Browser die Canvas-API nicht, unterstützt er mit Sicherheit auch die Canvas-Text-API nicht!
Anschließend wird ein ungenutztes <canvas>
-Element erstellt und sein Zeichenkontext abgerufen. Da supports_canvas()
bereits geprüft hat, ob das Canvas-Objekt die Methode getContext()
bietet, ist gesichert, dass das funktioniert:
Abschließend prüfen Sie, ob Ihr Zeichenkontext eine fillText()
-Funktion bietet. Tut er das, ist die Canvas-Text-API verfügbar:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für die Canvas-Text-API zu prüfen:
Video
HTML5 definiert ein neues Element namens <video>
, über das Videos in Ihre Webseiten eingebettet werden können. Die Einbettung von Videos war zuvor ohne externe Plug-ins wie Apple QuickTime oder Adobe Flash nicht möglich.
Das <video>
-Element wurde so entworfen, dass es ohne Erkennungsskripten genutzt werden kann. Sie können mehrere Videodateien angeben, und Browser, die HTML5-Video unterstützen, wählen auf Basis der von ihnen unterstützten Videoformate eine davon aus.
Browser, die HTML5-Video nicht unterstützen, ignorieren das <video>
-Element einfach. Das können Sie zu Ihrem Vorteil nutzen, indem Sie sie dann einfach anweisen, das Video stattdessen über ein externes Plug-in abzuspielen.
Wollen Sie Ihr Video nicht einfach nur in die Seite packen und abspielen, müssen Sie JavaScript nutzen. Die Prüfung für Videounterstützung nutzt Erkennungstechnik Nummer 2 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser HTML5-Video unterstützt, hat das zur Repräsentation von <video>
-Elementen erstellte DOM-Objekt eine canPlayType()
-Methode. Unterstützt Ihr Browser HTML5-Video nicht, besitzt das für <video>
-Elemente erstellte DOM-Objekt nur die Eigenschaften, die allen Elementen gemeinsam sind. Videounterstützung können Sie mit folgender Funktion prüfen:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für HTML5-Video zu prüfen:
Es gibt einen eigenen Test, mit dem Sie prüfen können, welche Videoformate Ihr Browser abspielen kann. Diesen werden wir Ihnen im nächsten Abschnitt vorführen.
Videoformate
Videoformate sind wie Sprachen. Auch wenn eine englische und eine spanische Zeitung die gleichen Informationen enthalten, bringt Ihnen nur eine der beiden Zeitungen etwas, solange Sie lediglich eine der beiden Sprachen beherrschen! Wenn ein Browser ein Video abspielen können soll, muss er die „Sprache“ beherrschen, in der es geschrieben wurde.
Die „Sprache“ eines Videos nennt man „Codec“ – das ist der Algorithmus, der eingesetzt wurde, um das Video zu einem Bit-Strom zu kodieren. Es gibt auf der Welt Dutzende von Codecs. Welchen davon sollen Sie also nutzen? Die unglückselige Realität von HTML5-Video ist, dass sich Browser einfach nicht auf einen einzigen Codec einigen können. Es scheint allerdings, als hätte man die Sache jetzt auf zwei eingeschränkt. Der eine Codec kostet Geld (aufgrund der Patentlizenzen) und wird von Safari und dem iPhone unterstützt. (Er funktioniert auch in Adobe Flash, falls Sie eine Lösung wie Video for Everybody! nutzen.) Der andere Codec ist frei und funktioniert in Open Source-Browsern wie Chromium und Mozilla Firefox.
Die Prüfung auf die Videoformatunterstützung nutzt Erkennungstechnik Nummer 3 (siehe dazu den Abschnitt „Erkennungstechniken“). Falls Ihr Browser HTML5-Video unterstützt, bietet das zur Repräsentation des <video>
-Elements erstellte DOM-Objekt eine canPlayType()
-Methode. Diese Methode sagt Ihnen, ob der Browser ein bestimmtes Videoformat unterstützt.
Folgende Funktion prüft auf das patentbelastete Format, das von Macs und iPhones unterstützt wird:
Zunächst prüft die Funktion die HTML5-Video-Unterstützung mit der supports_video()
-Funktion aus dem vorangegangenen Abschnitt:
Unterstützt Ihr Brower HTML5-Video nicht, unterstützt er natürlich auch keinerlei Videoformate!
Anschließend erstellt die Funktion ein ungenutztes <video>
-Element (bindet es aber nicht in die Seite ein, damit es nicht sichtbar wird) und ruft die Methode canPlayType()
auf. Diese Methode muss vorhanden sein, da wir mit supports_video()
gerade geprüft haben, ob dem so ist:
Eigentlich ist ein „Videoformat“ eine Kombination aus mehreren unterschiedlichen Dingen. Technisch ausgedrückt, fragen Sie den Browser damit, ob er H.264 Baseline-Video und AAC LC-Audio in einem MPEG-4-Container abspielen kann.
Die Funktion canPlayType()
liefert nicht einfach true
oder false
. Videoformate sind eine komplexe Angelegenheit! Deswegen liefert die Funktion einen String, der anzeigt, was der Browser vom jeweiligen Format hält:
"probably"
Falls der Browser recht sicher ist, dass er das Format abspielen kann.
"maybe"
Sollte der Browser meinen, dass er dazu in der Lage sein könnte, dieses Format abzuspielen.
""
(ein leerer String)
Wenn der Browser sicher ist, dass er dieses Format nicht abspielen kann.
Eine zweite Funktion prüft das offene Videoformat, das von Mozilla Firefox und anderen Open Source-Browsern unterstützt wird. Das Vorgehen ist genau das gleiche. Der einzige Unterschied ist der String, der der Funktion canPlayType()
übergeben wird. Hier fragen Sie den Browser technisch ausgedrückt, ob er Theora-Video und Vorbis-Audio in einem Ogg-Container abspielen kann:
Statt eigenhändig diese Funktionen zu schreiben, nutzen Sie Modernizr, um die Unterstützung verschiedener HTML5-Videoformate zu prüfen:
Local Storage
Fragen an Professor Markup
F: Ist HTML5 Storage tatsächlich Teil von HTML5? Warum steckt es dann in einer eigenständigen Spezifikation?
A: Die kurze Antwort ist: Ja. HTML5 Storage ist Teil von HTML5. Die etwas längere Antwort ist, dass lokaler Speicher ursprünglich Teil der eigentlichen HTML5-Spezifikation war, aber in eine separate Spezifikation ausgelagert wurde, weil sich einige Mitglieder der HTML5 Working Group beschwerten, dass HTML5 zu umfangreich werde. Wenn das für Sie so klingt, als wollte man die Kalorienmenge reduzieren, indem man das Törtchen in mehrere Teile zerlegt, dann willkommen in der verdrehten Welt der Standards.
Die Prüfung der Unterstützung für HTML5 Storage nutzt Erkennungstechnik Nummer 1 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser HTML5 Storage unterstützt, besitzt das globale window
-Objekt eine localStorage
-Eigenschaft. Unterstützt er HTML5 Storage nicht, ist die Eigenschaft localStorage
undefiniert. Mit folgender Funktion können Sie prüfen, ob lokaler Storage unterstützt wird:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für HTML5 Local Storage zu prüfen:
Beachten Sie, dass JavaScript Groß-/Kleinschreibung berücksichtigt. Das Modernizr-Attribut heißt localstorage
(alles Kleinbuchstaben), die DOM-Eigenschaft hingegen window.localStorage
(Groß- und Kleinbuchstaben).
Fragen an Professor Markup
F: Wie sicher ist meine HTML5 Storage-Datenbank? Können andere sie lesen?
Web Worker
Die Prüfung für Web Worker nutzt Erkennungstechnik Nummer 1 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser die Web Worker-API unterstützt, bietet das globale window
-Objekt eine Worker-Eigenschaft. Unterstützt Ihr Browser die Web Worker-API nicht, ist die Eigenschaft Worker undefiniert. Folgende Funktion prüft die Web Worker-Unterstützung:
Anstatt diese Funktion selbst zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für Web Worker zu prüfen:
Beachten Sie, dass JavaScript Groß-/Kleinschreibung berücksichtigt. Das Modernizr-Attribut heißt webworkers
(alles Kleinbuchstaben), das DOM-Objekt hingegen window.Worker
(mit dem großen „W“ von „Worker“).
Offline-Webanwendungen
Statische Webseiten offline zu lesen, ist leicht: Stellen Sie eine Verbindung mit dem Internet her, laden Sie eine Webseite, beenden Sie die Verbindung mit dem Internet, fahren Sie in ein einsames Häuschen am See und lesen Sie in aller Ruhe die Webseite. (Wenn Sie Zeit sparen wollen, können Sie den Schritt mit dem Häuschen am See weglassen.) Aber was ist mit Webanwendungen wie Gmail oder Google Docs, wenn Sie offline sind? Dank HTML5 kann jetzt jeder (nicht nur Google) Webseiten bauen, die auch offline funktionieren.
Offline-Webanwendungen beginnen als gewöhnliche Webanwendungen. Wenn Sie das erste Mal eine offlinefähige Website besuchen, sagt der Webserver Ihrem Browser, welche Dateien er für die Offlinearbeit benötigt. Diese Dateien können beliebiger Natur sein – HTML, JavaScript, Bilder, selbst Videos (siehe dazu den Abschnitt „Video“). Hat Ihr Browser einmal alle erforderlichen Dateien heruntergeladen, können Sie die Webseite wieder besuchen, selbst wenn Sie nicht mit dem Internet verbunden sind. Ihr Browser bemerkt, dass Sie offline sind, und nutzt die Dateien, die er bereits heruntergeladen hat. Sobald Sie wieder online sind, werden alle Änderungen, die Sie vorgenommen haben, zurück auf den entfernten Webserver geladen.
Offlineunterstützung prüfen Sie mit Erkennungstechnik Nummer 1 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser Offline-Webanwendungen unterstützt, bietet das globale window
-Objekt eine applicationCache
-Eigenschaft. Unterstützt Ihr Browser Offline-Webanwendungen nicht, ist die Eigenschaft applicationCache
undefiniert. Offlineunterstützung können Sie mit der folgenden Funktion prüfen:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für Offline-Webanwendungen zu prüfen:
Beachten Sie, dass JavaScript Groß-/Kleinschreibung berücksichtigt. Das Modernizr-Attribut heißt applicationcache
(alles Kleinbuchstaben), das DOM-Objekt hingegen window.applicationCache
(gemischte Groß-/Kleinschreibung).
Geolocation
Bei Geolocation geht es darum, festzustellen, wo Sie sich auf der Welt befinden, und diese Information (wenn Sie möchten) mit Menschen zu teilen, denen Sie vertrauen. Es gibt viele Möglichkeiten, herauszufinden, wo Sie stecken – Ihre IP-Adresse, Ihre drahtlose Netzwerkverbindung, die Zelle, mit der Ihr Handy verbunden ist, oder spezielle GPS-Hardware, die Längen- und Breitengradangaben von Satelliten im All empfängt.
Fragen an Professor Markup
F: Ist Geolocation ein Teil von HTML5? Warum erwähnen Sie es dann?
Die Prüfung der Geolocation-Unterstützung nutzt Erkennungstechnik Nummer 1 (siehe Abschnitt „Erkennungstechniken“). Wenn Ihr Browser die Geolocation-API unterstützt, bietet das globale navigator
-Objekt eine geolocation
-Eigenschaft. Unterstützt er die Geolocation-API nicht, ist die Eigenschaft geolocation
undefiniert. Folgendermaßen prüfen Sie die Geolocation-Unterstützung:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für die Geolocation-API zu prüfen:
navigator.geolocation-API
, aber sie erfüllt den gleichen Zweck.Außerdem gibt es gerätespezifische Geolocation-APIs auf mehreren Handyplattformen einschließlich BlackBerry, Nokia, Palm und OMTP BONDI.
input-Typen
Sie wissen alles über Webformulare, stimmt’s? Sie erstellen ein <form>
-Element, fügen ein paar <input type="text">
-Elemente und vielleicht ein <input type="password">
-Element hinzu und geben der Sache mit einem <input type="submit">
-Button den letzten Schliff.
Das, was Sie wissen, ist nicht einmal die Hälfte dessen, was es zu wissen gibt. HTML5 definiert mehr als ein Dutzend neuer Eingabetypen, die Sie in Ihren Formularen nutzen können:
<input type="search">
<input type="number">
<input type="range">
<input type="color">
<input type="tel">
<input type=ürl">
<input type="email">
<input type="date">
<input type="month">
<input type="week">
<input type="time">
<input type="datetime">
<input type="datetime-local">
Die Prüfung der Unterstützung der HTML5-Eingabetypen nutzt Erkennungstechnik Nummer 4 (siehe dazu den Abschnitt „Erkennungstechniken“). Zunächst erstellen Sie ein ungenutztes <input>
-Element im Speicher:
Der Standardeingabetyp für alle <input>
-Elemente ist "text"
. Das wird von entscheidender Bedeutung sein.
Anschließend setzen Sie das type
-Attribut dieses <input>
-Elements auf den Eingabetyp, den Sie prüfen wollen:
Wenn Ihr Browser einen bestimmten Eingabetyp unterstützt, bewahrt die Eigenschaft type
den von Ihnen gesetzten Wert. Unterstützt er diesen bestimmten Eingabetyp nicht, ignoriert er den von Ihnen gesetzten Wert, und die Eigenschaft type
behält den Wert "text"
:
Statt eigenhändig 13 separate Funktionen zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für alle in HTML5 neu definierten Eingabetypen zu prüfen. Modernizr nutzt nur ein einziges <input>
-Element, um die Unterstützung für alle 13 Eingabetypen zu prüfen. Dann baut es einen Hash namens Modernizr.inputtypes
auf, der 13 Schlüssel (die HTML5-type
-Attribute) und 13 Boolesche Werte enthält (true
, falls der Typ unterstützt wird, andernfalls false
):
Platzhaltertext
Neben den neuen Eingabetypen enthält HTML5 einige kleinere Verbesserungen für bestehende Formulare. Eine Verbesserung ist die Möglichkeit, für ein Eingabefeld Platzhaltertext zu festzulegen. Platzhaltertext wird im Eingabefeld angezeigt, solange das Feld leer und nicht im Fokus ist. Sobald Sie in das Eingabefeld klicken (oder es per Tabulator betreten), verschwindet der Platzhaltertext.
Die Prüfung der Platzhalterunterstützung nutzt Erkennungstechnik Nummer 2 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser Platzhaltertext in Eingabefeldern unterstützt, enthält das zur Repräsentation eines <input>
-Elements erzeugte DOM-Objekt eine placeholder
-Eigenschaft (auch wenn Sie in Ihr HTML kein placeholder
-Attribut einschließen). Unterstützt er Platzhaltertext nicht, hat das für ein <input>
-Element erstellte DOM-Objekt keine placeholder
-Eigenschaft. Folgendermaßen prüfen Sie die Platzhalterunterstützung:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für Platzhaltertext zu prüfen:
Formular-Autofokus
Viele Websites nutzen JavaScript, um dem ersten Eingabefeld eines Webformulars automatisch den Fokus zu geben. Beispielsweise setzt die Google-Homepage automatisch den Fokus in das Suchfeld, damit Sie sofort mit der Eingabe von Suchwörtern beginnen können, ohne zuvor manuell den Cursor im Eingabefeld positionieren zu müssen. Für die meisten Nutzer ist das äußerst angenehm, Benutzer mit besonderen Bedürfnissen kann es aber auch stören. Wenn Sie die Leertaste drücken, um die Seite zu scrollen, passiert nichts, weil das Eingabefeld bereits den Fokus hat. (Stattdessen wird ein Leerzeichen ins Eingabefeld eingegeben.) Setzen Sie den Fokus in ein anderes Eingabefeld, während die Seite noch geladen wird, kann es passieren, dass das „hilfsbereite“ Autofokusskript der Site den Fokus wieder in ein anderes Eingabefeld setzt, sobald das Laden abgeschlossen ist, und Sie damit in Ihrer Arbeit unterbricht und an der falschen Stelle etwas eintippen lässt.
Da die Fokussteuerung über JavaScript erfolgt, kann es recht kompliziert sein, all diese Sonderfälle zu berücksichtigen. Und es gibt kaum Ausweichmöglichkeiten für diejenigen, die sich nicht daran erfreuen, dass Webseiten den Fokus „kapern“.
Um dieses Problem zu lösen, führt HTML5 ein autofocus
-Attribut für alle Formularsteuerelemente ein. Das autofocus
-Attribut macht genau das, was der Name verspricht: Es verschiebt den Fokus auf ein bestimmtes Eingabefeld. Aber weil es Markup statt Skript ist, ist das Verhalten auf allen Websites gleich. Außerdem können Browserhersteller (oder Autoren von Erweiterungen) den Benutzern Möglichkeiten bieten, das Autofokusverhalten abzuschalten.
Die Prüfung der Autofokusunterstützung nutzt Erkennungstechnik Nummer 2 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser Formularsteuerelemente mit Autofokus unterstützt, bietet das zur Repräsentation eines <input>
-Elements erzeugte DOM-Objekt eine autofocus
-Eigenschaft (auch wenn Sie in Ihr HTML kein autofocus
-Attribut einschließen). Bietet er keine Unterstützung für das automatische Fokussieren von Formularsteuerelementen, bietet das für <input>
-Elemente erstellte DOM-Objekt keine autofocus
-Eigenschaft. Autofokusunterstützung können Sie mit folgender Funktion prüfen:
Statt eigenhändig diese Funktion zu schreiben, können Sie Modernizr (siehe dazu den Abschnitt „Modernizr: Eine Bibliothek zur HTML5-Erkennung“) nutzen, um die Unterstützung für automatisch fokussierte Formularfelder zu prüfen:
Microdaten
Beispielsweise können Sie Mikrodaten nutzen, um zu deklarieren, dass ein Foto unter einer bestimmten Creative Commons-Lizenz verfügbar ist. Sie können Mikrodaten dazu verwenden, eine „Info“-Seite auszuzeichnen. Browser, Browsererweiterungen und Suchmaschinen können Ihr HTML5-Mikrodaten-Markup in vCard, ein Standardformat zum Austausch von Kontaktdaten, umwandeln. Außerdem können Sie Ihre eigenen Mikrodatenvokabulare definieren.
Der HTML5-Mikrodaten-Standard enthält sowohl HTML-Markup (im Wesentlichen für Suchmaschinen) als auch einen Satz von DOM-Funktionen (hauptsächlich für Browser).
Es schadet nicht, Mikrodaten-Markup in Webseiten einzuschließen. Es sind lediglich ein paar gut platzierte Attribute, und Suchmaschinen, die die Mikrodatenattribute nicht verstehen, ignorieren sie einfach. Aber wenn Sie über das DOM auf Mikrodaten zugreifen oder Mikrodaten manipulieren wollen, müssen Sie prüfen, ob der Browser die DOM-API für Mikrodaten unterstützt.
Die Prüfung auf Unterstützung der HTML5-Mikrodaten-API nutzt Erkennungstechnik Nummer 1 (siehe dazu den Abschnitt „Erkennungstechniken“). Wenn Ihr Browser die HTML5-Mikrodaten-API unterstützt, bietet das globale document
-Objekt eine getItems()
-Funktion. Wenn er Mikrodaten nicht unterstützt, ist diese getItems()
-Funktion undefiniert. Die Mikrodatenunterstützung können Sie folgendermaßen prüfen:
Modernizr bietet aktuell noch keine Unterstützung für die Prüfung der Mikrodaten-API. Sie müssen also eine Funktion wie die obige nutzen.
Dieser Artikel wird sich eine HTML-Seite vornehmen, die vollkommen in Ordnung ist, und sie verbessern. Einige Teile werden kürzer werden, andere länger. Und alles wird so viel semantischer werden. Es wird Sie umwerfen.
Die Doctype-Deklaration
Beginnen wir mit der ersten Zeile:
Dieses Ding nennt man die Doctype-Deklaration. Dahinter verbirgt sich eine lange Geschichte und etwas schwarze Magie. Während der Entwicklung des Internet Explorer 5 für den Mac stand Microsoft vor einem überraschenden Problem. Die anstehende Version des Browsers hatte ihre Standardunterstützung so stark verbessert, dass ältere Seiten nicht mehr ordentlich dargestellt wurden. Besser gesagt, sie wurden korrekt (d.h. der Spezifikation gemäß) dargestellt, aber man erwartete, dass sie inkorrekt dargestellt würden. Die Seiten selbst wurden unter Berücksichtigung der Macken der vorherrschenden Browser der Zeit, allen voran Netscape 4 und Internet Explorer 4, geschrieben. IE5/Mac war so fortgeschritten, dass er das Web zerstörte.
Microsoft fand eine innovative Lösung. Bevor der IE5/Mac die Seite darstellte, warf er einen Blick auf die Doctype-Deklaration, die üblicherweise die erste Zeile in einer HTML-Quelle bildet (und sogar vor dem <html>
-Element selbst steht). Ältere Seiten (die sich auf die Mängel älterer Browser stützten) enthielten üblicherweise überhaupt keine Doctype-Deklaration. Diese Seiten stellte der IE5/Mac wie ältere Browser dar. Um die neue Standardunterstützung zu „aktivieren“, mussten die Autoren von Webseiten vor dem <html>
-Element den richtigen Doctype deklarieren.
Dieses Konzept verbreitete sich wie ein Lauffeuer, und schon bald boten alle wichtigeren Browser
zwei Modi an: den „Quirks-Modus“ (der auf den Macken basierte) und den „Standards-Modus“. Wie Sie sich denken können – schließlich reden wir hier ja über das Internet -, geriet die Sache schnell außer Kontrolle. Als Mozilla versuchte, Version 1.1 des eigenen Browsers zu veröffentlichen, stellte man fest, dass sich einige im Standards-Modus dargestellte Seiten eigentlich auf bestimmte Macken verließen. Um diese Macken zu beheben, reparierte Mozilla seine Rendering-Engine und zerstörte damit auf einen Schlag Tausende von Seiten. Und dies führte – ungelogen – zum „Almost Standards-Modus“.
- Quirks-Modus: Im Quirks-Modus verletzen Browser die zeitgenössischen Webformatspezifikationen, um zu vermeiden, dass Seiten „beschädigt“ werden, die gemäß den Verfahren geschrieben waren, die Ende der 1990er-Jahre vorherrschend waren.
- Standards-Modus: Im Standards-Modus versuchen Browser, den Spezifikationen entsprechende Dokumente spezifikationsgemäß zu behandeln, sofern der Browser diese unterstützt. HTML5 nennt diesen Modus den „No Quirks-Modus“.
- Almost Standards-Modus: Firefox, Safari, Chrome, Opera (seit 7.5) und IE8 bieten einen „Almost Standards-Modus“ genannten Modus, der die vertikale Größenermittlung für Tabellenzellen traditionell und nicht streng gemäß der CSS2-Spezifikation durchführte. Diesen Modus bezeichnet HTML5 als den „eingeschränkten Quirks-Modus“.
Sie sollten den Rest von Henris Artikel lesen, weil ich hier erheblich vereinfache. Selbst im IE5/Mac gab es einige ältere Doctype-Deklarationen, die nicht reichten, um die Standardunterstützung zu aktivieren. Mit der Zeit wuchs die Liste der Macken und ebenso die Liste der Doctypes, die den Quirks-Modus aktivierten.
Also … Wo waren wir noch? Stimmt, beim Doctype:
Das ist einer der 15 Doctypes, die in allen modernen Browsern den Standards-Modus aktivieren. Er ist vollkommen in Ordnung. Wenn Sie möchten, können Sie ihn lassen, wie er ist. Oder ändern Sie ihn in den HTML5-Doctype, der kürzer und süßer ist und ebenfalls in allen modernen Browsern den Standards-Modus aktiviert.
Das ist der HTML5-Doctype:
Das ist alles. Nur 15 Zeichen. Das ist so einfach, dass man es mit der Hand tippen kann, ohne es zu verbocken.
Professor Markup sagt
Die Doctype-Deklaration muss die erste Zeile Ihrer HTML-Datei sein. Steht irgendetwas davor – selbst eine einzige leere Zeile -, behandeln manche Browser sie so, als enthielte sie überhaupt keine Doctype-Deklaration. Gibt es sie nicht, stellt der Browser Ihre Seite im Quirks-Modus dar. Dieser Fehler kann sehr schwer zu entdecken sein. Zusätzlicher Withespace spielt in HTML üblicherweise keine Rolle. Man neigt also dazu, ihn einfach zu übersehen. Aber an dieser Stelle ist er äußerst wichtig!
Das Wurzelelement
Eine HTML-Seite ist eine Folge geschachtelter Elemente. Die vollständige Struktur der Seite ist wie ein Baum. Einige Elemente sind „Geschwister“, wie Zweige, die aus dem gleichen Baumstamm wachsen. Einige Elemente sind „Kinder“ anderer Elemente, vergleichbar mit einem dünneren Zweig, der aus einem dickeren Zweig wächst. (Umgekehrt funktioniert das auch: Ein Element, das andere Elemente enthält, wird als „Elternknoten“ seines unmittelbaren Kindelements und als „Vorfahr“ seiner Enkel bezeichnet.) Elemente, die keine Kinder haben, bezeichnet man als „Blattknoten“. Das äußerste Element, das Vorfahr aller anderen Elemente auf der Seite ist, bezeichnet man als das „Wurzelelement“ oder den „Wurzelknoten“. Das Wurzelelement einer HTML-Seite ist immer <html>
.
In unserer Beispielseite sieht das Wurzelelement so aus:
Dieses Markup ist vollständig in Ordnung. Wieder können Sie es lassen, wie es ist – wenn es Ihnen gefällt. Es ist gültiges HTML5. Aber Teile davon sind in HTML5 nicht mehr erforderlich. Sie können also ein paar Bytes sparen, indem Sie sie entfernen.
Das Erste, was wir besprechen müssen, ist das
xmlns
-Attribut. Es ist ein Überbleibsel aus XHTML 1.0. Es sagt, dass sich die Elemente in dieser Seite im XHTML-Namensraum befinden. Aber Elemente in HTML5 sind immer in diesem Namensraum. Sie müssen ihn also nicht mehr explizit deklarieren. Ihre HTML5-Seite funktioniert in allen modernen Browsern, ob dieses Attribut vorhanden ist oder nicht.Lassen wir das xmlns
-Attribut weg, bleibt uns folgendes Wurzelelement: <html lang="en" xml:lang="en">
Die Attribute lang
und xml:lang
definieren beide die Sprache dieser HTML-Seite. en steht für „Englisch“. Warum aber zwei Attribute für die gleiche Sache? Auch das ist ein XHTML-Erbe. In HTML5 hat nur das lang
-Attribut Auswirkungen. Sie können das xml:lang
-Attribut stehen lassen, wenn Sie wollen. Doch wenn Sie das tun, müssen Sie sicherstellen, dass es den gleichen Wert enthält wie das lang
-Attribut.
Zur Erleichterung des Umstiegs von und nach XHTML können Autoren ein Attribut in keinem Namensraum, ohne Präfix und mit dem literalen lokalen Namen xml:lang
auf HTML-Elementen in HTML-Dokumenten definieren. Ein solches Attribut darf allerdings nur definiert sein, wenn ebenfalls ein lang
-Attribut ohne Namensraum angegeben wird. Beide Argumente müssen den gleichen Wert enthalten, wenn sie ohne Berücksichtigung von Groß-/Kleinschreibung im ASCII-Bereich verglichen werden. Das namensraum- und präfixlose Attribut mit dem literalen lokalen Namen xml:lang
hat keine Auswirkungen auf die Sprachverarbeitung.
Sind Sie bereit, es fallen zu lassen? Das ist in Ordnung, lassen Sie es langsam aus Ihrem Leben verschwinden … und weg! Wir verbleiben bei diesem Wurzelelement:
Und das ist alles, was ich dazu zu sagen habe.
Das <head>
-Element
Das erste Kind des Wurzelelements ist üblicherweise das <head>
-Element. Das <head>
-Element enthält Metadateninformationen über die Seite, nicht den Inhalt der Seite selbst. (Der steht im darauf folgenden <body>
-Element.) Das <head>
-Element selbst ist ziemlich langweilig und hat sich in HTML5 kaum auf interessante Weise geändert. Das Gute ist das, was im <head>
-Element steht. Und dazu wenden wir uns wieder unserer Beispielseite zu:
Erster Schritt: das <meta>
-Element.
Zeichenkodierung
Wenn Sie an „Text“ denken, denken Sie wahrscheinlich „Zeichen und Symbole, die ich auf meinem Bildschirm sehe“. Aber Computer befassen sich nicht mit Zeichen und Symbolen. Computer befassen sich mit Bits und Bytes. Jedes bisschen Text, das Ihnen je auf einem Bildschirm vor Augen trat, wird eigentlich in einer bestimmten Zeichenkodierung gespeichert. Es gibt unzählige unterschiedliche Zeichenkodierungen. Einige von ihnen sind für bestimmte Sprachen wie Russisch oder Chinesisch oder Englisch gedacht, andere können für viele Sprachen verwendet werden. Grob formuliert, könnte man sagen, dass die Zeichenkodierung die Zuordnung zwischen dem ist, was Sie auf dem Bildschirm sehen, und dem, was Ihr Computer im Speicher und auf der Festplatte speichert.
In Wirklichkeit ist das natürlich etwas komplizierter.
Viele Zeichen tauchen in verschiedenen Kodierungen auf, aber jede dieser Kodierungen kann eine andere Folge von Bytes nutzen, um diese Zeichen tatsächlich im Speicher oder auf der Festplatte zu speichern. Sie können sich eine Zeichenkodierung also als eine Art Entschlüsselungsmechanismus für Text vorstellen. Gibt Ihnen jemand eine Folge von Bytes und behauptet, es sei „Text“, müssen Sie wissen, welche Zeichenkodierung genutzt wurde, damit Sie die Bytes wieder in Zeichen umrechnen und anzeigen (oder irgendwie verarbeiten) können.
Wie also ermitteln Browser tatsächlich die Zeichenkodierung der Byte-Streams, die der Server sendet? Ich bin froh, dass Sie das fragen. Wenn Sie mit HTTP-Headern vertraut sind, haben Sie vielleicht schon einmal einen Header dieser Form gesehen:
Kurz und knapp sagt diese Zeile, dass der Server denkt, er sende Ihnen ein HTML-Dokument, das die Zeichenkodierung UTF-8 nutzt. Unglücklicherweise haben im großen Brei des World Wide Web nur äußerst wenige Autoren die Kontrolle über ihre HTTP-Server.
Dieses sagt, dass der Webautor meint, er habe ein HTML-Dokument unter Verwendung der Zeichenkodierung UTF-8 geschrieben.
Beide Techniken funktionieren auch in HTML5 noch. Der HTTP-Header ist die bevorzugte Methode und überschreibt ein eventuell vorhandenes <meta>
-Tag. Aber da nicht jeder HTTP-Header setzen kann, gibt es das <meta>
-Tag immer noch. Und es ist in HTML5 sogar noch etwas einfacher geworden. Es sieht jetzt folgendermaßen aus:
Der Grund für die <meta charset="">
-Attributkombination ist, dass UAs sie bereits implementieren, weil viele dazu neigen, die Dinge folgendermaßen ohne Anführungszeichen anzugeben:
<meta charset>
–Testfälle, wenn Sie sich nicht vorstellen können, dass Browser das bereits tun.Fragen an Professor Markup
F: Ich habe noch nie seltsame Zeichen verwendet. Muss ich trotzdem eine Zeichenkodierung angeben?
A: Ja! Sie sollten immer eine Zeichenkodierung für alle HTML-Seiten angeben, die Sie ausliefern. Geben Sie keine Kodierung an, kann das zu Sicherheitslücken führen.
Zusammengefasst: Die Sache mit der Zeichenkodierung ist eine komplizierte Angelegenheit, die durch Jahrzehnte schlecht geschriebener Software, die von Autoren verwendet wird, deren bevorzugte Arbeitstechnik immer noch Copy-and-Paste ist, nicht gerade vereinfacht wird. Sie sollten bei jedem HTML-Dokument grundsätzlich eine Zeichenkodierung angeben, da es andernfalls zu üblen Folgen kommen kann. Sie können das über den HTTP-Content-Type-Header, die <meta http-equiv>
-Deklaration oder die kürzere <meta charset>
-Deklaration tun. Aber vergessen Sie sie nicht. Das Web wird es Ihnen danken.
Link-Relationen
Gewöhnliche Links (<a href>
) zeigen einfach auf eine andere Seite. Link-Relationen sind ein Mittel, zu erläutern, warum Sie auf eine andere Seite verweisen. Sie beenden den Satz: Ich verweise auf diese andere Seite, weil …
- …sie ein Stylesheet mit CSS-Regeln ist, die Ihr Browser auf dieses Dokument anwenden soll.
- …sie ein Feed ist, der den gleichen Inhalt wie diese Seite in einem standardkonformen, abonnierbaren Format enthält.
- …sie eine Übersetzung dieser Seite in eine andere Sprache ist.
- …sie das Gleiche beinhaltet wie diese Seite, aber im PDF-Format.
- …weil sie das nächste Kapitel eines Onlinebuchs ist, zu dem auch diese Seite gehört.
Und so weiter. HTML5 teilt Link-Relationen in zwei Kategorien ein:
Zwei Kategorien von Links können mit dem Linkelement erstellt werden. Links auf externe Ressourcen sind Links auf Ressourcen, die genutzt werden, um das aktuelle Dokument zu bereichern, und Hyperlink-Links sind Links auf andere Dokumente. […]
Das genaue Verhalten von Links auf externe Ressourcen hängt von der spezifischen Beziehung ab,
die für den entsprechenden Linktyp definiert ist.
Von den Beispielen, die ich Ihnen gegeben hatte, ist nur das erste (rel="stylesheet"
) ein Link auf eine externe Ressource. Der Rest sind Hyperlinks auf andere Dokumente. Wenn Sie wollen, können Sie diesen Links folgen oder auch nicht. Aber sie sind nicht erforderlich, um die aktuelle Seite zu betrachten.
Link-Relationen tauchen am häufigsten in <link>
-Elementen im <head>
-Element einer Seite auf. Einige Link-Relationen können auch auf <a>
-Elementen genutzt werden, aber selbst wenn das erlaubt ist, ist es nicht sehr üblich. HTML5 gestattet ebenfalls einige Relationen auf <area>
-Elementen, aber das ist sogar noch weniger üblich. (HTML 4 gestattete keine rel
-Attribute auf <area>
-Elementen).
Fragen an Professor Markup
F: Kann ich eigene Link-Relationen definieren?
rel
-Werte und definiert den Akzeptierungsprozess (Akzeptierungsprozess).rel = stylesheet
Schauen wir uns die erste Link-Relation in unserer Beispielseite an:
Das ist die am häufigsten genutzte Link-Relation auf der Welt (buchstäblich). <link rel="stylesheet">
dient dazu, auf CSS-Regeln zu verweisen, die in einer eigenen Datei gespeichert sind. Eine kleine Optimierung, die Sie in HTML5 vornehmen können, ist, das type
-Attribut wegzulassen. Es gibt nur eine Stylesheet-Sprache für das Web, CSS, das ist deswegen der Standardwert für das type
-Attribut:
Das funktioniert in allen Browsern. (Vielleicht erfindet jemand irgendwann eine neuen Stylesheet-Sprache. Aber wenn das passiert, können Sie das type
-Attribut einfach wieder einfügen.)
rel = alternate
Fahren wir mit unserer Beispielseite fort:
Auch diese Link-Relation ist recht häufig. <link rel="alternate">
in Kombination mit dem RSS- oder Atom-Medientyp im type
-Attribut aktiviert die sogenannte „automatische Feederkennung“. Sie ermöglicht Feedreadern wie dem Google Reader, zu erkennen, dass die Site einen Newsfeed mit den neuesten Artikeln hat. Die meisten Browser unterstützen die automatische Feederkennung zusätzlich damit, dass sie ein spezielles Symbol neben der URL anzeigen. (Anders als bei rel="stylesheet"
ist das type
-Attribut hier relevant. Lassen Sie es nicht weg!)
Die Link-Relation rel="alternate"
war schon in HTML 4 ein seltsamer Zwitter von Anwendungsfällen. In HTML5 wurde ihre Definition klarer gemacht und erweitert, um bestehende Webinhalte präziser zu beschreiben. Wie Sie gerade sahen, zeigt die Verbindung von rel="alternate"
und type=application/atom+xml
einen Atom-Feed für die aktuelle Seite an. Aber Sie können rel="alternate"
gemeinsam mit anderen type
-Attributen nutzen, um Inhalte in anderen Formaten wie PDF anzuzeigen.
HTML5 legt auch eine weitere lang währende Verwirrung über Links auf Übersetzungen von Dokumenten bei. HTML 4 sagt, dass das lang
-Attribut gemeinsam mit rel="alternate"
angegeben werden muss, um die Sprache des verlinkten Dokuments anzugeben, aber das ist falsch. Die HTML 4-Errata-Liste führt unumwunden vier Fehler in der HTML 4-Spezifikation (mit anderen editorischen Kleinigkeiten) auf. Einer dieser Fehler betrifft die Angabe der Sprache eines mit rel="alternate"
eingebundenen Dokuments. (Das richtige Verfahren, das im HTML 4-Errata-Dokument und jetzt in HTML5 beschrieben wird, ist die Verwendung des Attributs hreflang
.) Unglücklicherweise wurden diese Errata nie rückwirkend in die HTML 4-Spezifikation eingebaut, weil niemand in der W3C HTML Working Group mehr an HTML arbeitete.
Andere Link-Relationen in HTML5
rel="archives"
zeigt an, dass das angegebene Dokument eine Sammlung von Datensätzen, Dokumenten oder anderem Material von historischem Interesse ist. Die Indexseite eines Blogs könnte auf einen Index der vergangenen Blog-Einträge mit rel="archives"
verweisen.
rel="author"
wird genutzt, um Informationen zum Autor der Seite anzugeben. Das kann eine mailto
:-Adresse sein, muss es aber nicht. Es könnte auch einfach auf ein Kontaktformular oder eine „Über mich“-Seite verweisen.
rel="external"
zeigt an, dass der Link auf ein Dokument zeigt, das nicht Teil der Site ist, zu der das aktuelle Dokument gehört. Ich glaube, es wurde zuerst durch WordPress populär gemacht, das es für Links verwendet, die in Kommentaren hinterlassen werden.
rel="start"
, rel="prev"
und rel="next"
werden genutzt, um Relationen zwischen Seiten zu definieren, die Teil einer Serie sind (wie die Kapitel eines Buchs oder auch die Einträge eines Blogs). Das Einzige davon, das je korrekt genutzt wurde, ist rel="next"
. Man nutzte rel="previous"
statt rel="prev"
; man nutzte rel="begin"
und rel="first"
statt rel="start"
; man nutzte rel="end"
statt rel="last"
. Und dann dachte man sich auch noch rel="up"
aus, um auf eine „Elternseite“ zu verweisen.
HTML5 schließt rel="first"
ein, das am häufigsten verwendet wurde, um auf die „erste Seite einer Reihe“ zu verweisen (rel="start"
ist ein nicht standardkonformes Synonym, das die Rückwärtskompatibilität gewährleisten soll). Es schließt wie HTML 4 rel="prev"
und rel="next"
ein (und unterstützt rel="previous"
im Dienste der Rückwärtskompatibilität) und auch rel="last"
(das Letzte in einer Reihe und passende Gegenstück zu rel="first"
) und rel="up"
.
Am einfachsten machen Sie sich die Bedeutung von rel="up"
klar, wenn Sie sich die Wegweiser in Ihrer Site-Navigation vor Augen führen (oder zumindest vorstellen). Ihre Homepage ist wahrscheinlich die erste Seite unter Ihren Wegweisern und die aktuelle Seite die letzte. rel="up"
weist auf die vorletzte Seite unter Ihren Wegweisern.
rel="icon"
ist die zweitbeliebteste Link-Relation nach rel="stylesheet"
. Üblicherweise wird sie mit shortcut
kombiniert, wie hier:
Alle wichtigeren Browser unterstützen diese Verwendung, um ein kleines Symbol mit der Seite zu verbinden. Üblicherweise wird es in der Adressleiste des Browsers neben der URL angezeigt oder im Browser-Tab oder in beidem.
Ebenfalls neu in HTML5: Das sizes
-Attribut kann gemeinsam mit der icon
-Relation genutzt werden, um die Größe des referenzierten Symbols anzuzeigen.
rel="license"
wurde von der Mikroformat-Gemeinschaft erfunden. Es „zeigt an, dass das referenzierte Dokument die Copyright-Lizenzbedingungen angibt, unter der das aktuelle Dokument bereitgestellt wird“.
rel="nofollow"
„zeigt an, dass der Link vom ursprünglichen Autor oder Herausgeber der Seite nicht befürwortet wird oder dass der Link auf das referenzierte Dokument im Wesentlichen auf einer kommerziellen Beziehung zwischen den Urhebern der beiden Seiten beruht.“ Sie wurde von Google erfunden und in der Mikroformat-Gemeinschaft standardisiert. Die Überlegung war, dass Spammer es aufgeben würden, Spam-Kommentare in Blogs zu hinterlassen, wenn nofollow
-Links keinen Einfluss auf das Seiten-Ranking haben. Das geschah nicht. Aber rel="nofollow"
blieb. Viele beliebte Blogging-Systeme hängen an Links in Kommentaren standardmäßig rel="nofollow"
an.
rel="noreferrer"
„zeigt an, dass keine Referrer-Daten weitergegeben werden sollen, wenn dem Link gefolgt wird.“ Das wird von keinem der aktuell veröffentlichten Browser unterstützt, wurde aber in aktuelle WebKit-Nightlies eingebaut und wird irgendwann in Safari, Google Chrome und anderen WebKit-basierten Browsern auftauchen.
rel="pingback"
gibt die Adresse eines „Pingback-Servers“ an. Wie in der Pingback-Spezifikation steht: „Das Pingback-System ist ein Mittel, Blogs automatisch zu benachrichtigen, wenn andere Websites auf es verlinken […] Es ermöglicht umgekehrtes Linking – eine Möglichkeit, in einer Kette von Links wieder nach oben zu steigen, statt einfach in ihr abzusteigen.“ Blogging-Systeme, insbesondere WordPress, implementieren den Pingback-Mechanismus, um Autoren zu benachrichtigen, dass Sie auf sie verlinkt haben, als Sie einen neuen Blog-Eintrag erstellten.
rel="prefetch"
„zeigt an, dass es vorteilhaft sein kann, die angegebene Ressource vorab abzurufen und zu cachen, da es äußerst wahrscheinlich ist, dass der Benutzer sie benötigt.“ Suchmaschinen fügen Suchergebnissen <link rel="prefetch" href="<emphasis>URL OF TOP SEARCH RESULT</emphasis>">
hinzu, wenn sie meinen, dass das Topergebnis erheblich populärer ist als alle anderen. Ein Beispiel: Nehmen Sie Firefox, suchen Sie mit Google nach CNN, schauen Sie sich den Quelltext der Seite an und suchen Sie nach dem Schlüsselwort prefetch. Mozilla Firefox ist aktuell der einzige Browser, der rel="prefetch"
unterstützt.
rel="search"
„zeigt an, dass das referenzierte Dokument eine Schnittstelle bietet, die speziell dem Durchsuchen des Dokuments und verwandter Ressourcen dient.“ Genauer gesagt, wenn rel="search"
etwas Nützliches tun soll, sollte es auf ein OpenSearch-Dokument verweisen, das beschreibt, wie ein Browser eine URL aufbauen muss, um die aktuelle Site nach einem bestimmten Suchwort zu durchsuchen. OpenSearch (und rel="search"
-Links, die auf OpenSearch-Beschreibungsdokumente verweisen) wird von Microsoft Internet Explorer seit Version 7 und von Mozilla Firefox seit Version 2 unterstützt.
rel="sidebar"
„zeigt an, dass das referenzierte Dokument, wenn es abgerufen wird, in einem sekundären Browsing-Kontext angezeigt werden soll (wenn möglich) statt im aktuellen Browsing-Kontext.“ Was bedeutet das? In Opera und Mozilla Firefox bedeutet es: „Wenn ich auf diesen Link klicke, werde ich aufgefordert, ein Lesezeichen anzulegen, das, wenn es im Lesezeichen-Menü angewählt wird, das verlinkte Dokument in einer Browser-Sidebar anzeigt.“ (Opera nennt es „Panel“ statt „Sidebar“.) Internet Explorer, Safari und Chrome ignorieren rel="sidebar"
und behandeln es als gewöhnlichen Link.
rel="tag"
„zeigt an, dass das Tag, das das referenzierte Dokument repräsentiert, für das aktuelle Dokument gilt.“ Die Auszeichnung von „Tags“ (Kategorieschlüsselwörtern) mit dem rel
-Attribut wurde von Technorati erfunden, um die Kategorisierung von Blog-Einträgen zu vereinfachen. Frühe Blogs und Tutorials bezeichneten sie deswegen als „Technorati-Tags“. (Das haben Sie richtig verstanden: Ein kommerzielles Unternehmen hat die gesamte Welt davon überzeugt, Metadaten zu verwenden, die ihm das Leben erleichtern. Gute Arbeit, wenn man so etwas schafft!) Die Syntax wurde später in der Mikroformat-Gemeinschaft standardisiert, wo sie einfach zu rel="tag"
wurde. Die meisten Blog-Systeme, die es ermöglichen, Kategorien, Schlüsselwörter oder Tags mit individuellen Einträgen zu versehen, zeichnen sie mit rel="tag"
-Links aus. Browser machen nichts Spezielles damit; eigentlich sollen sie Suchmaschinen signalisieren, worum es in der Seite geht.
Neue semantische Elemente in HTML5
HTML5 dreht sich nicht nur darum, bestehendes Markup zu verkürzen (obwohl es in dieser Beziehung einiges leistet). Es definiert außerdem einige neue semantische Elemente. Das sind die Elemente, die die HTML5-Spezifikation definiert:
<section>
Das section
-Element repräsentiert einen allgemeinen Abschnitt in einem Dokument oder einer Anwendung. Ein Abschnitt ist in diesem Kontext eine thematische Gruppierung von Inhalten, die üblicherweise unter einer Überschrift stehen. Beispiele für Abschnitte wären Kapitel, die verschiedenen Tabs in einem Dialog mit Tabs oder die nummerierten Abschnitte einer wissenschaftlichen Arbeit. Die Homepage einer Website könnte mehrere Abschnitte für eine Einführung, die eigentlichen Nachrichten sowie die Kontaktdaten enthalten.
<nav>
Das nav
-Element repräsentiert einen Abschnitt einer Seite, der auf andere Seiten oder Teile in der Seite verlinkt: ein Abschnitt mit Navigationslinks. Nicht alle Linkgruppen auf einer Seite müssen in einem nav
-Element stehen – nur die Abschnitte, die aus wichtigen Navigationsblöcken bestehen, sind für das nav
-Element gedacht. Die häufig anzutreffenden kurzen Linklisten in den Fußleisten von Webseiten, die auf verschiedene Seiten innerhalb der Site verweisen, wie Geschäftsbedingungen, Homepage oder Copyright, zählen beispielsweise nicht dazu. Das footer
-Element allein, ohne eingebettetes nav
-Element, reicht in solchen Fällen aus.
<article>
Das article
-Element repräsentiert eine abgeschlossene Einheit in einem Dokument, einer Anwendung oder einer Site, die unabhängig verbreitet oder wiederverwendet werden kann, z.B. in RSS-Feeds. Es könnte beispielsweise ein Forenbeitrag, ein Zeitschriften- oder Zeitungsartikel, ein Blog-Eintrag, ein Benutzerkommentar, ein interaktives Widget oder Gadget oder ein Element mit unabhängigem Inhalt enthalten.
<aside>
Das aside
-Element repräsentiert einen Abschnitt einer Seite, der Inhalte enthält, die sich zwar auf den das aside
-Element umgebenden eigentlichen Inhalt der Seite beziehen, aber als von ihm unabhängig betrachtet werden können. In Druckwerken werden derartige Abschnitte häufig als Seitenleisten dargestellt. Das Element kann für typografische Effekte wie herausgehobene Zitate oder Seitenleisten, für Werbung, für Gruppen von nav
-Elementen und andere Inhalte verwendet werden, die als vom eigentlichen Inhalt der Seite getrennt betrachtet werden können.
<hgroup>
Das hgroup
-Element repräsentiert die Überschrift eines Abschnitts. Das Element wird genutzt, um einen Satz von h1
–h6
-Elementen zu gruppieren, wenn die Überschrift mehrere Ebenen wie Untertitel, alternative Titel oder Schlagzeigen enthält.
<header>
Das header
-Element repräsentiert eine Gruppe von Einführungselementen oder Navigationshilfen. Ein header
-Element soll üblicherweise die Überschrift eines Abschnitts (ein h1
–h6
-Element oder ein hgroup
-Element) enthalten, aber erforderlich ist das nicht. Das header
-Element kann auch verwendet werden, um das Inhaltsverzeichnis eines Abschnitts, ein Suchformular oder eventuelle Logos einzuschließen.
<footer>
Das footer
-Element repräsentiert die Fußleiste für das unmittelbar vorausgehende abschnittsartige Element oder abschnittsartige Wurzelelement. Eine Fußleiste enthält üblicherweise Informationen zu einem Abschnitt, beispielsweise wer ihn geschrieben hat, verweist auf verwandte Dokumente, bietet Copyright-Informationen und Ähnliches. Fußleisten müssen nicht notwendigerweise am Ende eines Abschnitts erscheinen, tun das aber meist. Wenn das footer
-Element vollständige Abschnitte enthält, repräsentieren diese Anhänge, Indizes, lange Kolophone, umfangreiche Lizenzbedingungen und andere derartige Inhalte.
<time>
Das time
-Element repräsentiert eine Zeit auf einer 24-Stunden-Uhr oder ein genaues Datum auf dem proleptischen gregorianischen Kalender, optional mit einer Zeit und einer Zeitzonenverschiebung.
<mark>
Das mark
-Element repräsentiert einen Textverlauf in einem Dokument, der zu Verweiszwecken markiert oder vorgehoben ist.
Ich weiß, Sie hungern danach, gleich mit der Verwendung dieser neuen Elemente loszulegen. Sonst würden Sie diesen Artikel sicher nicht lesen. Aber erst müssen wir einen kleinen Umweg nehmen.
Wie Browser mit unbekannten Elementen umgehen
Jeder Browser besitzt eine Stammliste der HTML-Elemente, die er unterstützt. Bei Mozilla Firefox ist diese beispielsweise in nsElementTable.cpp
gespeichert. Elemente, die sich nicht in dieser Liste befinden, werden als „unbekannte Elemente“ behandelt. Es gibt zwei grundlegende Fragen in Bezug auf unbekannte Elemente:
- Wie soll das Element dargestellt werden?
Standardmäßig erhält <p>
oberhalb und unterhalb Freiraum, <blockquote>
wird mit einem linken Außenabstand eingerückt, und <h1>
wird in einer größeren Schrift dargestellt.
- Wie sollte das DOM des Elements aussehen?
Mozillas nsElementTable.cpp
enthält Informationen dazu, welche Arten anderer Elemente die einzelnen Elemente enthalten können. Wenn Sie Markup wie dieses einschließen, <p><p>
, schließt das zweite Paragraf-Element implizit das erste. Die Elemente werden also zu Geschwistern, nicht zu Eltern und Kindern. Aber wenn Sie <p><span>
schreiben, schließt das span den Absatz nicht, weil Firefox weiß, dass <p>
ein Blockelement ist, das das Inline-Element <span>
enthalten kann. Das <span>
wird im DOM also zu einem Kind des <p>
-Elements.
Die verschiedenen Browser beantworten diese Fragen auf unterschiedliche Weise.
(Erschreckend, ich weiß.) Unter den wichtigsten Browsern beantwortet Microsofts Internet Explorer diese Frage am problematischsten.
Die erste Frage lässt sich recht leicht beantworten: Unbekannte Elemente erhalten keine spezielle Darstellung. Sie erben einfach die CSS-Eigenschaften, die an der Stelle in Kraft sind, an der sie in der Seite erscheinen. Es bleibt dem Seitenautor überlassen, alle Darstellungsstyles mit CSS anzugeben. Unglücklicherweise erlaubt der Internet Explorer (vor Version 9) kein Styling für unbekannte Elemente. Nehmen wir an, Sie hätten eine Seite mit folgendem Markup:
Der Internet Explorer (bis einschließlich IE8) würde diesen Artikel nicht mit einem roten Rand darstellen. Während ich dies schrieb, befand der Internet Explorer 9 immer noch im Betastadium, aber Microsoft hat gesagt (und Entwickler haben das bestätigt), dass der Internet Explorer 9 dieses Problem nicht mehr beinhalten wird.
Das zweite Problem ist das DOM, das der Browser erstellt, wenn er ein unbekanntes Element antrifft. Auch hier weist der Internet Explorer die größten Probleme auf. Wenn der IE einen Elementnamen nicht explizit erkennt, fügt er das Element als leeren Knoten ohne Kinder in das DOM ein. Alle Elemente, die Sie als unmittelbare Kinder des unbekannten Elements erwarten würden, werden tatsächlich also als seine Geschwister eingefügt.
Hier ist etwas ASCII-Kunst, die den Unterschied vorführt. Das ist das DOM, das HTML5 vorschreibt:
Aber das ist das DOM, das der Internet Explorer tatsächlich erstellt:
Es gibt einen wunderlichen Workaround für dieses Problem. Wenn Sie ein leeres <article>
-Element mit JavaScript erstellen, bevor Sie es in der Seite verwenden, erkennt der Internet Explorer auf magische Weise das <article>
-Element und ermöglicht Ihnen, es mit CSS zu stylen. Sie müssen das Scheinelement nie in das DOM einfügen. Sie müssen das Element nur einmal pro Seite erstellen, um dem IE beizubringen, das Styling für ein Element zuzulassen, das er nicht kennt, zum Beispiel so:
Das funktioniert in allen Versionen des Internet Explorer bis zurück zum IE6! Wir können diese Technik nutzen, um Scheinelemente für alle neuen HTML5-Elemente auf einmal zu erstellen – wieder ohne sie in das DOM einzufügen. Sie werden diese Scheinelemente nie sehen und können sie dann einfach verwenden, ohne sich über Nicht-HTML5-fähige Browser Gedanken machen zu müssen.
Die <!--[if lt IE 9]>
– und <![endif]-->
-Teile sind bedingte Kommentare. Der Internet Explorer interpretiert sie als if
-Anweisung: „Ist der aktuelle Browser eine Version des Internet Explorers vor Version 9 führe diesen Block aus.“ Alle anderen Browser behandeln den gesamten Block als HTML-Kommentar. Die Folge ist, dass der Internet Explorer (bis einschließlich Version 8) das Skript ausführt, andere Browser es aber vollständig ignorieren. Das sorgt dafür, dass die Seite in Browsern, die diesen Hack nicht benötigen, schneller geladen wird.
Der JavaScript-Code selbst ist recht gradlinig. Die Variable e
wird zu einem Array mit Strings wie „abbr
„, „article
„, „aside
“ und so weiter. Dann durchlaufen wir dieses Array und erstellen jedes der angeführten Elemente, indem document.createElement()
aufgerufen wird. Aber da wir den Rückgabewert ignorieren, werden die Elemente nie in das DOM eingefügt. Es reicht jedoch, um den Internet Explorer dazu zu bringen, diese Elemente so zu behandeln, wie wir es wollen, wenn wir sie später in der Seite verwenden.
Dieser „später“-Aspekt ist wichtig. Das Skript muss am Anfang Ihrer Seite stehen,
vorzugsweise im <head>
-Element -, nicht am Ende. Das sorgt dafür, dass der Internet Explorer das Skript ausführt, bevor er Ihre Tags und Attribute parst. Wenn Sie dieses Skript am Ende Ihrer Seite angeben, ist es zu spät. Der Internet Explorer hat Ihr Markup bereits falsch interpretiert und das falsche DOM aufgebaut, und er wird das wegen Ihres Skripts nicht rückgängig machen.
Jetzt sind wir bereit, die neuen semantischen Elemente in HTML5 auch zu benutzen.
Kopfleisten und Überschriften
Kehren wir zu unserer Beispielseite zurück und schauen wir uns zunächst die Überschriften an:
Dieses Markup ist vollständig in Ordnung. Wenn es Ihnen gefällt, können Sie es lassen, wie es ist. Es ist gültiges HTML5. Aber HTML5 bietet einige zusätzliche semantische Elemente für Überschriften und Abschnitte.
Befreien wir uns zunächst von diesem <div id="header">
. Das ist ein sehr verbreitetes Muster. Das div
-Element hat keine eigene Semantik, ebenso das id
-Attribut. (Clients dürfen aus dem Wert des id
-Attributs keine Bedeutung ableiten.) Sie könnten stattdessen <div id="shazbot">
verwenden, und es hätte den gleichen semantischen Wert, also gar keinen.
HTML5 definiert für diesen Zweck ein <header>
-Element. Die HTML5-Spezifikation bietet eine Reihe lebensnaher Beispiele für die Verwendung des <header>
-Elements. So würde das bei unserer Beispielseite aussehen:
Das ist gut. Es sagt allen, die es wissen wollen, dass das eine Kopfleiste (ein Header) ist. Aber was ist mit diesem Slogan? Ein weiteres verbreitetes Muster, für das es bislang kein Standard-Markup gab. So etwas ist schwer auszuzeichnen. Ein Slogan ist so etwas wie ein Untertitel, der mit der eigentlichen Überschrift verknüpft ist. Es ist also eine Zwischenüberschrift, die keinen eigenen Abschnitt öffnet.
Überschriftenelemente wie <h1>
und <h2>
geben Ihrer Seite eine Struktur. Gemeinsam bilden sie eine Gliederung, die Sie nutzen können, um sich die Seite vorzustellen (oder durch die Seite zu navigieren). Bildschirmleser nutzen Dokumentgliederungen, um blinden Nutzern die Navigation der Seite zu erleichtern.
In HTML 4 waren die Elemente <h1>-<h6>
die einzige Möglichkeit, eine Dokumentgliederung zu erstellen. Die Gliederung unserer Beispielseite sieht so aus:
Das ist in Ordnung, bedeutet aber, dass es keine Möglichkeit gibt, den Slogan „A lot of effort went into making this effortless.“ auszuzeichnen. Versuchen wir, ihn als <h2>
-Element zu markieren, würden wir damit einen Phantomknoten in die Dokumentgliederung einfügen:
Das entspricht jedoch nicht der Struktur des Dokuments. Der Slogan entspricht keinem Abschnitt. Er ist lediglich ein Untertitel.
Vielleicht könnten wir den Slogan als <h2>
auszeichnen und alle Artikeltitel als <h3>?
Aber das ist sogar noch schlimmer:
Der Phantomknoten befindet sich immer noch in unserer Dokumentgliederung, hat aber Kinder „gestohlen“, die eigentlich unter den Wurzelknoten gehören. Und hier liegt das Problem: HTML 4 bietet keine Möglichkeit, einen Untertitel auszuzeichnen, ohne ihn der Dokumentgliederung hinzuzufügen. Ganz gleich, wie wir die Dinge herumschieben, „A lot of effort went into making this effortless“ landet in unserer Gliederung. Und deswegen landeten wir bei semantisch bedeutungslosem Markup wie <p class="tagline">
.
HTML5 bietet jetzt eine Lösung dafür: das <hgroup>
-Element. Das <hgroup>
-Element fungiert als Verpackung für zwei oder mehr aufeinander bezogene Überschriftenelemente. Was „aufeinander bezogen“ heißt? Es bedeutet, dass sie gemeinsam einen einzigen Knoten in der Dokumentgliederung bilden.
In HTML5 nutzen Sie folgendes Markup:
So sieht die Dokumentgliederung aus, die erstellt wird:
Artikel
Fahren wir mit unserer Beispielseite fort und schauen wir uns an, was wir mit diesem Markup machen können:
Auch das ist gültiges HTML5. Aber HTML5 bietet ein spezifischeres Element für den häufigen Fall der Auszeichnung eines Artikels auf einer Seite – das entsprechend benannte <article>
-Element:
Aber ganz so einfach ist das nicht. Sie sollten noch eine weitere Änderung vornehmen. Ich werde es Ihnen erst zeigen und dann erläutern:
Haben Sie das verstanden? Ich habe das <h2>
-Element in ein <h1>
umgewandelt und in ein <header>
-Element gepackt. Sie haben bereits gesehen, wie das <header>
-Element verwendet wird. Es soll alle Elemente zusammenfassen, die die Überschrift des Artikels bilden (in diesem Fall sind das das Veröffentlichungsdatum und der Titel). Aber … sollte man nicht nur ein <h1>
pro Dokument haben? Beschädigt das nicht die Dokumentstruktur? Nein. Um das zu verstehen, müssen wir einen Schritt zurückgehen.
In HTML 4 konnte man die Dokumentgliederung nur mit den Elementen <h1>
–<h6>
gestalten. Sollte Ihre Gliederung lediglich einen Wurzelknoten enthalten, mussten Sie sich in Ihrem Markup auf ein einziges <h1>
-Element beschränken. Aber die HTML5-Spezifikation definiert einen Algorithmus zur Erstellung einer Dokumentgliederung, die die neuen semantischen Elemente von HTML5 einschließt. Der HTML5-Algorithmus sagt, dass ein <article>
-Element einen neuen Abschnitt erzeugt, d.h. einen neuen Knoten in der Dokumentgliederung. Und in HTML5 kann jeder Abschnitt sein eigenes <h1>
-Element haben.
Das ist eine erhebliche Änderung im Vergleich zu HTML 4, und hier sind die Gründe dafür,
dass das so gut ist. Viele Webseiten werden eigentlich auf Basis von Schablonen erstellt. Etwas Inhalt wird von dieser Quelle bezogen und oben in die Seite hineingepfropft. Etwas Inhalt wird von jener Quelle abgerufen und unten in die Seite hineingepfropft. Viele Einführungen sind auf vollkommen gleiche Weise strukturiert. „Hier ist etwas HTML-Markup. Kopieren Sie es und fügen Sie es in Ihre Seite ein.“ Bei kleinen Datenmengen ist das vollkommen ausreichend, aber was ist, wenn das Markup, das so eingefügt wird, einen ganzen Abschnitt umfasst? Dann würde jene Einführung Folgendes sagen: „Hier ist etwas HTML-Markup. Kopieren Sie es, fügen Sie es in einem Texteditor in ein Dokument ein und passen Sie die Tags für die Überschriften so an, dass sie den Schachtelungsebenen der entsprechenden Überschriften-Tags in der Seite entsprechen, in die das Markup eingefügt wird.“
Formulieren wir die Sache anders. HTML 4 bietet kein allgemeines Überschriftenelement. Es bietet sechs streng nummerierte Überschriftenelemente, <h1>
–<h6>
, die genau in dieser Reihenfolge geschachtelt werden müssen. Das ist recht nervig, vor allem wenn Ihre Seite eher „zusammengestückelt“ als „geschrieben“ wird. Und das ist das Problem, das HTML5 mit den neuen abschnittsbildenden Elementen und den neuen Regeln für die vorhandenen Überschriftenelemente löst. Wenn Sie die neuen abschnittsbildenden Elemente nutzen, können Sie Markup wie dieses nutzen:
Sie können es einfach kopieren und an völlig beliebiger Stelle in Ihre Seite einfügen, ohne daran Änderungen vornehmen zu müssen. Dass es ein <h1>
-Element ist, ist kein Problem, weil das Ganze in ein <article>
-Element eingeschlossen ist. Das <article>
-Element definiert einen in sich abgeschlossenen Knoten in der Dokumentgliederung, das <h1>
-Element bietet einen Titel für diesen Gliederungsknoten, und alle anderen abschnittsbildenden Elemente in der Seite bleiben genau auf der Schachtelungsebene, auf der sie sich auch vorher befanden.
Professor Markup sagt
Wie im Web üblich, ist die Realität wieder einmal komplizierter, als ich hier durchblicken lasse. Die neuen „explizit“ abschnittsbildenden Elemente (wie <h1>
in einem <article>
) können auf unerwartete Weise mit den alten „implizit“ abschnittsbildenden Elemente (<h1>
–<h6>
allein) kollidieren. Sie machen sich das Leben erheblich leichter, wenn Sie nur eines von beidem nutzen, nicht beides gleichzeitig. Müssen Sie in einer Seite beides nutzen müssen, sollten Sie sich das Ergebnis unbedingt im HTML5 Outliner anschauen, um zu prüfen, ob Ihre Dokumentgliederung vernünftig ist.
Datum und Uhrzeit
Aufregend, nicht wahr? Nicht „Nacht vor der Hochzeit“- oder „Abend vor Weihnachten“-aufregend natürlich, aber schon spannend, was semantisches Markup betrifft. Fahren wir mit unserer Beispielseite fort. Als Nächstes wollen wir uns folgende Zeile vornehmen:
Wieder die gleiche alte Geschichte? Ein verbreitetes Muster – das Veröffentlichungsdatum eines Artikels -, für das es kein semantisches Markup gibt. Also griffen Autoren auf generisches Markup zurück und nutzen selbst definierte class
-Attribute. Auch das ist gültiges HTML5. Sie müssen es nicht ändern. Aber HTML5 bietet eine spezifische Lösung für diesen Fall – das <time>
-Element:
Das <time>
-Element hat drei Bestandteile:
- einen maschinenlesbaren Zeitstempel,
- einen für Menschen lesbaren Textinhalt sowie
- ein optionales pubdate-Flag.
In diesem Beispiel gibt das datetime
-Attribut nur ein Datum, keine Uhrzeit an. Das Format ist eine vierstellige Jahresangabe sowie zweistellige Monats- und Tagesangaben, jeweils getrennt durch Bindestriche:
Wenn Sie auch eine Zeit einfügen wollen, können Sie nach dem Datum den Buchstaben T, auf ihn folgend die Zeit (24-Stunden-Format) und eine Zeitzonenverschiebung angeben:
Das Format für die Datum/Zeit-Angabe ist ziemlich flexibel. Die HTML5-Spezifikation enthält eine Reihe von Beispielen für gültige Datum/Zeit-Strings.
Beachten Sie, dass ich den Textinhalt zwischen <time>
und </time>
so geändert habe, dass er dem Zeitstempel entspricht. Das ist nicht vorgeschrieben. Der Textinhalt kann beliebige Form haben, solange dass datetime
-Attribut eine gültige Datum/Zeit-Angabe enthält. (Die zulässigen Formate für den datetime
-Wert sind eingeschränkt, was im Tag steht, das bleibt natürlich Ihnen überlassen. Dass oben eine Zeitangabe in dem im angelsächsischen Raum üblichen Format steht, liegt daran, dass die Webseite selbst aus diesem Sprachraum kommt.) Auch das ist also gültiges HTML5:
Und das ebenfalls:
Das letzte Puzzleteil ist das pubdate
-Attribut. Es ist ein Boolesches Attribut. Sie müssen es also nur einfügen, wenn Sie es benötigen:
Sollte Ihnen dieses „nackte“ Attribut nicht gefallen, können Sie folgendes Äquivalent nutzen:
Was bedeutet dieses pubdate
-Attribut? Zwei Dinge: Wenn das <time>
-Element in einem <article>
-Element steht, bedeutet es, dass das der Zeitstempel des Publikationsdatums des Artikels ist. Steht das <time>
-Element nicht in einem <article>
-Element, bedeutet es, dass der Zeitstempel das Publikationsdatum des vollständigen Dokuments ist.
Hier ist der vollständige Artikel, und zwar so umformuliert, dass er alle Vorteile von HTML5 nutzt:
Navigation
Einer der wichtigsten Teile jeder Website ist die Navigationsleiste. spiegel.de hat Tabs – Register – oben auf jeder Seite, die auf die verschiedenen Nachrichtenbereiche verweisen – „Politik“, „Wirtschaft“, „Sport“ usw. Google-Suchergebnisse bieten oben eine ähnliche Leiste, über die Sie Ihre Suche auf den verschiedenen Google-Diensten durchführen können – „Bilder“, „Videos“, „Maps“ usw. Und unsere Beispielseite hat eine Navigationsleiste in der Kopfleiste, die Links auf die verschiedenen Bereiche unserer hypothetischen Site enthält – „home“, „blog“, „gallery“ und „about“.
So wurde diese Navigationsleiste ursprünglich ausgezeichnet:
Auch das ist gültiges HTML5. Aber an dieser Liste aus vier Elementen ist nichts, das Ihnen mitteilt, dass sie Teil der Navigation der Site ist. Visuell können Sie das erraten, weil sie Teil der Kopfleiste ist und der Text der Links darauf hindeutet. Aber semantisch gibt es nichts, was diese Liste von jeder anderen Liste in der Seite unterscheidet.
Wen die Semantik der Site-Navigation interessiert? Leute mit Behinderungen beispielsweise. Warum das? Betrachten Sie folgendes Szenario: Sie können sich nur eingeschränkt bewegen. Eine Maus zu verwenden, fällt Ihnen schwer oder ist vollkommen unmöglich. Um das zu kompensieren, nutzen Sie eventuell eine Browsererweiterung, die es Ihnen ermöglicht, zu den wichtigen Navigationslinks zu springen (oder über sie hinaus). Oder überlegen Sie das: Ihr Sehvermögen ist eingeschränkt, und Sie nutzen ein spezielles Programm, einen sogenannten „Screenreader“, der Text in Sprache umwandelt und Webseiten zusammenfasst. Wenn Sie den Seitentitel hinter sich haben, sind die nächsten wichtigen Informationen die Navigationslinks. Wenn Sie schnell navigieren wollen, sagen Sie dem Bildschirmleser, dass er zur Navigationsleiste springen und mit dem Lesen beginnen soll. Wenn Sie schnell lesen wollen, sagen Sie Ihrem Bildschirmleser vielleicht, dass er die Navigationsleiste überspringen und damit beginnen soll, den eigentlichen Inhalt der Seite vorzulesen. In beiden Fällen ist es äußerst wichtig, dass man die Navigationslinks programmtechnisch ermitteln kann.
Die Auszeichnung der Site-Navigation mit <div id="nav">
ist also nicht falsch, allerdings auch nicht sonderlich richtig. Dieser Mangel wirkt sich auf die Handhabung aus. HTML5 bietet ein semantisches Mittel, Navigationsabschnitte auszuzeichnen – das <nav>
-Element:
Fragen an Professor Markup
F: Sind Sprunglinks mit dem <nav>
-Element kompatibel? Brauche ich Sprunglinks in HTML5 noch?
Wenn Screenreader gelernt haben, das <nav>
-Element zu erkennen, werden Sprunglinks überflüssig werden, da Screenreader dann automatisch anbieten können, einen Navigationsabschnitt zu überspringen, der mit dem <nav>
-Element ausgezeichnet ist. Es wird allerdings eine Weile dauern, bis alle behinderten Benutzer auf HTML5-fähige Screenreader-Software umgestiegen sind. Sie sollten also weiterhin Sprunglinks anbieten, um Ihre <nav>
-Abschnitte zu überspringen.
Fußleisten
Endlich sind wir am Ende unserer Beispielseite angelangt. Das Letzte, worüber wir sprechen wollen, ist auch das letzte Element der Seite: die Fußleiste. Das entsprechende Markup sah ursprünglich so aus:
Das ist gültiges HTML5. Wenn es Ihnen gefällt, können Sie es so lassen. Aber HTML5 bietet ein spezifischeres Element dafür – das <footer>
-Element:
Was man in ein <footer>
-Element stecken sollte? Wahrscheinlich alles, was Sie bislang in ein <div id="footer">
gepackt haben. Gut, diese Antwort enthält vermutlich wenig Neues für Sie, aber genau das ist die Antwort. Die HTML5-Spezifikation sagt: „Eine Fußleiste enthält üblicherweise Informationen über den Abschnitt, dem sie zugeordnet ist, beispielsweise wer ihn geschrieben hat, Links auf darauf bezogene Dokumente, Copyright-Angaben und Ähnliches.“ Genau das steht auch in der Fußleiste der Beispielseite: ein kurzer Copyright-Hinweis und ein Link auf die Informationsseite zum Seitenautor. Wenn ich mich auf anderen populären Sites umschaue, sehe ich eine Menge Möglichkeiten für das footer
-Element:
- Der Spiegel hat eine Fußleiste, die Links zu Onlinepartnern und Links auf weitere Site-Inhalte, unter anderem zur Hilfe-Seite, zur Kontakt-Seite oder zum Impressum enthält. Alles höchst passendes
<footer>
-Material. - Google hat die berühmte spartanische Homepage, aber unten finden sich Links auf „Werben mit Google“, „Unternehmensprogramm“, „Über Google“ sowie ein Copyright-Hinweis und die Datenschutzerklärung. All das könnte in ein
<footer>
-Element gepackt werden.
Um das in semantisches HTML5 umzuwandeln, würde ich die folgenden Änderungen vornehmen:
- Ich würde das äußere
<div id="w3c_footer">
in ein<footer>
-Element umwandeln. - Ich würde die ersten beiden
<div class="w3c_footer-nav">
in<nav>
-Elemente umwandeln und das dritte in ein<section>
-Element. - Ich würde die
<h3>
-Überschriften in<h1>
-Überschriften umwandeln, da sie dann alle in abschnittsbildenden Elementen stünden. Das<nav>
-Element erstellt einen Abschnitt in der Dokumentgliederung, genau wie das<article>
-Element.
Das endgültige Markup könnte folgendermaßen aussehen:
Mit CSS können Sie nicht nur vorhandene Elemente stylen, sondern auch Inhalte in ein Dokument einfügen. Es gibt einige Fälle, in denen die Erzeugung von Inhalten mit CSS sinnvoll ist. So wäre es zum Beispiel naheliegend, beim Ausdruck einer Seite die URLs von Hyperlinks neben dem jeweiligen Text mit auszugeben. Wenn Sie ein Dokument auf dem Bildschirm ansehen, können Sie einfach den Mauszeiger über den Link bewegen und sehen in der Statusleiste, wohin der Link führt. Auf dem Ausdruck einer Seite haben Sie dagegen keine Ahnung, wohin die Links verweisen.
Das Geschäft arbeitet an einer neuen Seite für Formulare und Richtlinien. Ein Mitglied des Gremiums für die Neugestaltung besteht darauf, bei jedem Treffen ein Exemplar der Website auszudrucken. Dieser Kollege möchte genau wissen, wohin die Links auf der Seite führen, damit er feststellen kann, ob sie verändert werden müssen. Mit ein bisschen CSS können wir diese Funktionalität in IE 8, Firefox, Safari und Chrome hinbekommen. Und mit proprietärem JavaScript funktioniert es auch im IE 6 und 7.
Die Seite selbst enthält derzeit nichts außer einer Liste von Links. Bei Gelegenheit wird sie in eine entsprechende Vorlage eingesetzt.
Auf einem Ausdruck dieser Seite können Sie bisher nicht erkennen, wohin diese Links führen. Das werden wir ändern.
Das CSS
Wenn wir Stylesheets in eine Seite einbinden, können wir den Medientyp angeben, für den die Stilregeln gelten sollen. Meistens verwenden wir den Typ screen
. Wir können aber auch den Typ print
angeben, um ein Stylesheet zu definieren, das nur für den Ausdruck der Seite geladen wird (oder wenn der Benutzer die Druckvorschau aufruft).
Anschließend erstellen wir das Stylesheet print.css mit dieser einfachen Regel:
Dadurch wird hinter den Text jedes Links in Klammern der Wert des Attributs href
hinzugefügt. Wenn Sie die Seite in einem modernen Browser ausdrucken, sieht das so aus:
Wenn Sie das Ganze in Aktion sehen möchten, ohne Papier zu verbrauchen, können Sie die Druckvorschau des Browsers verwenden, die ebenfalls dieses Stylesheet aufruft.
Damit haben wir alles im Griff, außer den Internet Explorer 6 und 7. Sollen wir uns darum als Nächstes kümmern?
Ausweichlösung
Der Internet Explorer verfügt über einige JavaScript-Events, von denen ich mir wünsche, dass alle Browser sie übernehmen: onbeforeprint
und onafterprint
. Mithilfe dieser Events können wir den Text des Hyperlinks ändern, wenn der Ausdruck angestoßen wird, und anschließend die Änderung wieder rückgängig machen, wenn der Druck abgeschlossen ist. Unsere Benutzer werden den Unterschied nicht bemerken.
Wir müssen lediglich eine Datei mit dem Namen print.js anlegen und diesen Code einfügen:
Anschließend binden wir die Datei in unsere Seite ein. Wir brauchen diese Lösung lediglich für den IE 6 und 7, deshalb können wir einen bedingten Kommentar verwenden. Der Code setzt jQuery voraus, also müssen wir unbedingt auch die jQuery-Bibliothek einbinden.
Sobald das JavaScript eingebunden ist, werden die URLs von Links auf allen Browsern ausgedruckt. Sie können dieses Stylesheet für den Druck als Grundlage für ein umfassenderes verwenden und zum Beispiel dieses Verhalten nur auf bestimmte Links anwenden.
Im Printbereich werden seit Langem Spalten verwendet – und von vielen Webdesignern neidisch beäugt. Schmale Spalten erleichtern das Lesen, und seitdem Bildschirme immer breiter werden, suchen Entwickler nach Möglichkeiten, die Spalten auf eine angenehme Breite zu begrenzen. Schließlich möchte niemand Textzeilen quer über den ganzen Monitor lesen – genauso wie niemand Textzeilen über die gesamte Breite einer Zeitung lesen möchte. In den vergangenen zehn Jahren gab es einige ziemlich smarte Lösungen. Aber keine ist so einfach wie die Methode der CSS3-Spezifikation.
Spalten spalten
Das Unternehmen verwendet zufällig ein beliebtes webbasiertes E-Mail-System für Newsletter. E-Maibasierte Newsletter sehen bei Weitem nicht so gut aus und sind schwer zu pflegen. Es wurde daher beschlossen, den Newsletter auf die Intranetseite zu stellen und den Mitarbeitern nur eine E-Mail mit einem Link zu schicken, damit Sie den Newsletter im Browser ansehen können. Ein Gerüst dieses Newsletters sehen Sie in Abbildung 1.
Die neue Kommunikationdirektorin, die Erfahrung im Printbereich hat, möchte, dass der Newsletter mehr wie ein echter Newsletter aussieht – mit zwei Spalten statt einer.
Falls Sie jemals versucht haben, Text mit divs und floats in mehrere Spalten aufzuteilen, wissen Sie, wie schwer das sein kann. Die erste große Hürde besteht darin, dass Sie den Text von Hand aufteilen müssen. In DTP-Software wie zum Beispiel InDesign können Sie die Textfelder so miteinander verknüpfen, dass der Text vom ersten Feld automatisch in das nächste fließt, wenn das erste Textfeld vollständig mit Text gefüllt ist. Exakt so etwas gibt es für das Web noch nicht. Aber wir haben etwas, das wirklich sehr gut und einfach funktioniert. Wir können den Inhalt eines Elements auf mehrere gleich breite Spalten aufteilen.
Wir beginnen mit dem Markup für den Newsletter. Das HTML ist ziemlich einfach. Da sich der Inhalt ohnehin ändern wird, sobald wir es geschrieben haben, verwenden wir einfach Blindtext. Falls Sie sich fragen, warum wir nicht die neuen HTML5-Markup-Elemente wie zum Beispiel section
für den Newsletter verwenden: Das liegt daran, dass unsere Ausweichlösung mit diesen Elementen nicht mit dem Internet Explorer kompatibel ist.
Um den Newsletter auf zwei Spalten aufzuteilen, müssen wir lediglich Folgendes in unser Stylesheet einfügen:
Wie Sie in Abbildung 2 erkennen, sieht unser Newsletter schon viel besser aus. Wenn wir zusätzlichen Inhalt einfügen, verteilt der Browser den Inhalt automatisch gleichmäßig auf die Spalten. Beachten Sie auch, wie die gefloateten Elemente in die Spalten fließen, in die sie gehören.
Joe fragt …
Kann ich für jede Spalte eine andere Breite angeben?
Nein, alle Spalten müssen dieselbe Breite haben. Ich war zunächst auch etwas überrascht, also habe ich die Spezifikation gründlich überprüft. Als ich dieses Buch geschrieben habe, war keine Möglichkeit vorgesehen, verschiedene Spaltenbreiten anzugeben.
Wenn man aber darüber nachdenkt, wie Spalten herkömmlicherweise verwendet werden, ist das auch sinnvoll. Spalten sind genauso wenig wie Tabellen als Trick gedacht, um schnell eine Seitenleiste zu basteln. Spalten sollen das Lesen von langen Textpassagen erleichtern, und gleich breite Spalten sind nun mal perfekt dafür.
Ausweichlösung
Menschen ohne JavaScript müssen mit einer Textspalte auskommen, können den Inhalt aber trotzdem lesen, weil wir ihn linear aufgebaut haben. Auch sie sind also versorgt. Allerdings können wir mit Java-Script die Unterstützung bestimmter Elemente durch den Browser ermitteln: Wenn wir eine existierende CSS-Eigenschaft abfragen, erhalten wir einen Leerstring. Erhalten wir dagegen einen Nullwert, ist die Eigenschaft nicht verfügbar.
Wir prüfen also zunächst, ob Spalten unterstützt werden. Falls nicht, wenden wir unser Plugin an.
Laden Sie die Seite im Internet Explorer neu, und der Newsletter wird zweispaltig angezeigt. Das Ergebnis ist zwar nicht perfekt (siehe Abbildung 3). Aber mit ein bisschen CSS oder JavaScript können Sie die Elemente zurechtbiegen, die noch nicht optimal aussehen. Diese Übung überlasse ich Ihnen.
Die Aufteilung von Inhalten auf mehrere Spalten kann diese besser lesbar machen. Sollte Ihre Seite aber länger sein, finden es Ihre Benutzer unter Umständen nervig, wieder ganz an den Anfang scrollen zu müssen. Setzen Sie diese Funktion daher mit Vorsicht ein.
Viele der neuen Elemente in HTML5 helfen Ihnen dabei, Inhalte genauer zu beschreiben. Dies wird umso entscheidender, wenn andere Programme Ihren Code übersetzen. Manche Menschen verwenden zum Beispiel Bildschirmlesegeräte oder Screenreader, um sich die grafischen Inhalte des Bildschirms in Text übersetzen und laut vorlesen zu lassen. Screenreader interpretieren den Text auf dem Bildschirm und das entsprechende Markup, um Links, Bilder und andere Elemente zu erkennen. Diese Geräte haben erstaunliche Fortschritte gemacht, hinken aber immer ein bisschen hinter den neuesten Trends her. Live-Bereiche auf Seiten, in denen durch Polling oder Ajax-Anfragen der Inhalt verändert wird, sind schwierig zu erkennen. Aufwendige Seiten sind teilweise schwierig zu navigieren, da der Screenreader eine Menge Text vorlesen muss.
In diesem Artikel sehen wir uns an, wie HTML5 die Erfahrung jener Besucher verbessern kann, die unterstützende Geräte verwenden.
Das Beste daran ist:
Für die in diesem Artikel beschriebenen Techniken benötigen wir keine Ausweichlösung, da viele Screenreader jetzt schon diese Technologien nutzen können.
Dazu gehören:
- Das role-Attribut
[<div role="document">]
Gibt die Aufgabe eines Elements für den Screenreader an. [C3, F3.6, S4, IE8, O9.6] - aria-live
[<div aria-live="polite">]
Kennzeichnet einen Bereich, der automatisch aktualisiert wird, möglicherweise durch Ajax. [F3.6 (Windows), S4, IE8] - aria-atomic
[<div aria-live="polite" aria-atomic="true">]
Gibt an, ob der gesamte Inhalt eines Live-Bereichs oder nur die veränderten Elemente vorgelesen werden sollen. [F3.6 (Windows), S4, IE8]
Die meisten Websites haben eine typische Struktur gemeinsam: Es gibt einen Kopfbereich, einen Navigationsbereich, den Hauptinhalt und eine Fußzeile. Genauso ist auch der Code der meisten Websites aufgebaut: linear
. Leider bedeutet das, dass ein Bildschirmlesegerät unter Umständen die gesamte Website auch in dieser Reihenfolge vorlesen muss. Da sich auf den meisten Websites derselbe Kopfbereich und dieselbe Navigation auf jeder Seite wiederholen, müssen die Benutzer sich diese Elemente jedes Mal wieder anhören, um von einer Seite zur nächsten zu navigieren.
Die bevorzugte Abhilfe besteht in einem versteckten Link „Navigation überspringen“,
den Screenreader laut vorlesen und der auf einen Anker im Hauptinhalt verweist. Allerdings ist diese Funktionalität nicht von Haus aus integriert – und nicht jeder weiß, wie das funktioniert, oder denkt überhaupt daran.
Mit dem neuen HTML5-Attribut role
können wir jedem Element auf Ihrer Seite eine „Zuständigkeit“ zuweisen. Bildschirmlesegeräte können dann ganz einfach die Seite einlesen und diese Zuständigkeiten kategorisieren und einen einfachen Index für die Seite erstellen. So werden beispielsweise alle Elemente mit der Rolle navigation
auf der Seite gesucht und den Benutzern erklärt, damit sie schnell durch die Anwendung navigieren können.
Diese Rollen stammen aus der WIA-ARIA-Spezifikation und wurden in die HTML5-Spezifikation integriert. Es gibt zwei bestimmte Rollenklassifizierungen, die Sie bereits jetzt einsetzen können: Landmark- und Dokument-Rollen.
Landmark-Rollen
Landmark-Rollen kennzeichnen „interessante Punkte“ auf Ihrer Website, wie etwa Banner, den Suchbereich oder die Navigation, die Screenreader schnell erkennen können.
Rolle | Verwendung |
---|---|
banner |
Kennzeichnet den Bannerbereich Ihrer Seite. |
search |
Kennzeichnet den Suchbereich Ihrer Seite. |
navigation |
Kennzeichnet Navigationselemente auf Ihrer Seite. |
main |
Kennzeichnet die Stelle, an der der Hauptinhalt Ihrer Seite beginnt. |
contentinfo |
Gibt an, wo Informationen über den Inhalt vorhanden sind, zum Beispiel Copyright-Informationen und das Veröffentlichungsdatum. |
complementary |
Kennzeichnet Inhalte auf einer Seite, die den Hauptinhalt ergänzen, aber auch eine eigenständige Bedeutung haben. |
application |
Kennzeichnet einen Seitenbereich, der statt einem Webdokument eine Anwendung enthält. |
Für den Kopfbereich vergeben wir die Rolle banner
:
Wir müssen lediglich role="banner"
zu dem vorhandenen header
-Tag hinzufügen.
Unsere Navigation können wir auf dieselbe Weise kennzeichnen:
Die HTML5-Spezifikation besagt, dass manche Elemente standardmäßige Rollen haben, die nicht überschrieben werden können. Das Element nav
muss die Rolle navigation
haben und daher technisch gesehen nicht damit gekennzeichnet werden. Nicht alle Bildschirmlesegeräte akzeptieren diesen Standard, aber viele verstehen die ARIA-Rollen.
Unsere Haupt- und Seitenleistenbereiche lassen sich wie folgt kennzeichnen:
Die Informationen zur Veröffentlichung und zum Copyright in unserem Fußbereich kennzeichnen wir mit der Rolle contentinfo
:
Wenn es in unserem Blog eine Suchfunktion gäbe, könnten wir diesen Bereich ebenso kennzeichnen. Nachdem wir jetzt die Orientierungspunkte gekennzeichnet haben, gehen wir einen Schritt weiter und identifizieren einige der Dokumentelemente.
Rollen für die Dokumentstruktur
Die Rollen für die Dokumentstruktur helfen Screenreadern dabei, die Teile mit statischen Inhalten leichter zu erkennen, wodurch sich der Inhalt für die Navigation leichter strukturieren lässt.
Rolle | Verwendung |
---|---|
document |
Kennzeichnet einen Bereich mit Dokumentinhalt, im Gegensatz zu Anwendungsinhalten. |
article |
Kennzeichnet eine Zusammenstellung, die einen unabhängigen Dokumentteil darstellt. |
definition |
Kennzeichnet die Definition eines Begriffs oder Themas. |
directory |
Kennzeichnet eine Liste von Referenzen auf eine Gruppe, wie etwa ein Inhaltsverzeichnis. Für statischen Inhalt. |
heading |
Kennzeichnet eine Überschrift für einen Abschnitt einer Seite. |
img |
Kennzeichnet einen Abschnitt, der Bildelemente enthält. Dabei kann es sich ebenso um Bildelemente wie um Beschriftungen und beschreibenden Text handeln. |
list |
Kennzeichnet eine Gruppe nicht interaktiver Listenelemente. |
listitem |
Kennzeichnet ein einzelnes Mitglied einer Gruppe nicht interaktiver Listenelemente. |
math |
Kennzeichnet einen mathematischen Ausdruck. |
note |
Kennzeichnet Inhalt, der ausgeklammert ist oder den Hauptinhalt der Ressource ergänzt. |
presentation |
Kennzeichnet Inhalt, der der Darstellung dient und daher von unterstützenden Technologien ignoriert werden kann. |
row |
Kennzeichnet eine Zeile von Zellen in einem Gitter. |
rowheader |
Kennzeichnet eine Zelle mit Header-Informationen für eine Zeile in einem Gitter. |
Viele Dokumentrollen sind implizit durch HTML-Tags definiert, wie etwa article
und header
. Allerdings ist die Rolle document
nicht implizit definiert. Und das ist eine sehr wichtige Rolle, insbesondere für Anwendungen, in denen dynamischer und statischer Inhalt gemischt werden. So kann zum Beispiel in einem webbasierten E-Mail-Client dem Element, das den Body der E-Mail enthält, die Rolle document
zugewiesen werden. Das ist nützlich, weil Bildschirmlesegeräte oft verschiedene Methoden für die Navigation mit der Tastatur anbieten. Wenn der Fokus des Screenreaders auf einem Anwendungselement liegt, müssen Tastenanschläge unter Umständen an die Webanwendung durchgereicht werden. Befindet sich der Fokus dagegen auf statischem Inhalt, können die Tastenbelegungen des Screenreaders andere Aufgaben übernehmen.
In unserem Blog können wir die Rolle document
auf das body
-Element anwenden:
Joe fragt …
Brauchen wir diese Landmark-Rollen für Elemente wie nav
und header
?
Die Landmark-Rollen erscheinen im ersten Moment vielleicht redundant, aber sie bieten die Flexibilität, die Sie dann benötigen, wenn Sie die neuen Elemente nicht verwenden können.
Mit der Rolle search
können Sie Ihre Benutzer nicht nur zu dem Seitenbereich führen, der das Suchfeld enthält, sondern auch zu Links auf eine Sitemap, zu einer Dropdown-Liste mit Quick-Links oder zu anderen Elementen, die Ihren Benutzern helfen können, Informationen schnell zu finden. Sie sind also nicht darauf beschränkt, Ihre Benutzer lediglich zum Suchfeld selbst zu führen.
Mit der Spezifikation werden eine Menge mehr Rollen eingeführt als neue Elemente und Formularsteuerelemente.
Dies hilft, dafür zu sorgen, dass Bildschirmlesegeräte Ihre Seite als statischen Inhalt behandeln.
Ausweichlösung
Diese Rollen können bereits mit den neuesten Browsern und den neuesten Screenreadern verwendet werden. Sie können also ab sofort damit arbeiten. Browser, die die Rollen nicht unterstützen, ignorieren sie einfach. Sie können damit also auch nur den Menschen helfen, die sie verwenden können.
Weblinks
Was ist HTML5 einfach erklärt?
HTML5 ist eine neue Version des HTML-Standards. Er enthält neue Funktionen wie Videowiedergabe und Drag-and-Drop und soll die Webentwicklung vereinfachen.
Eines der Hauptziele von HTML5 war es, den Entwicklern die Erstellung von Websites zu erleichtern, die auf einer Vielzahl von Geräten, einschließlich Smartphones und Tablets, funktionieren. Die neuen Funktionen in HTML5 ermöglichen es Entwicklern, Websites zu erstellen, die reaktionsschneller und benutzerfreundlicher sind.
Was ist der Unterschied zwischen HTML und HTML5?
HTML 5 ist eine aktualisierte Version von HTML, die entwickelt wurde, um die Benutzung zu erleichtern und mehr Multimedia-Inhalte auf Webseiten zu ermöglichen. Sie bietet auch neue Tags, die zur Definition der Struktur eines Dokuments verwendet werden können.
HTML, oder HyperText Markup Language, ist die Sprache, die zur Erstellung von Webseiten verwendet wird. Sie enthält Codes, die den Browsern mitteilen, wie Text, Bilder und andere Elemente auf einer Seite angezeigt werden sollen. HTML5 ist die neueste Version von HTML und enthält Aktualisierungen, die die Verwendung erleichtern und weniger restriktiv sind, was die Arten von Multimedia-Inhalten angeht, die in eine Seite aufgenommen werden können.
Ist HTML5 eine Programmiersprache?
HTML5 ist keine Programmiersprache. Es ist eine Auszeichnungssprache.
Eine Auszeichnungssprache ist eine spezielle Art von Computersprache, die Tags verwendet, um Text und Daten für die Anzeige auf einer Webseite zu formatieren. HTML5 ist die fünfte Überarbeitung des HTML-Standards und enthält neue Tags und Attribute, mit denen Sie anspruchsvollere Webseiten erstellen können als mit früheren Versionen von HTML.
Im Gegensatz zu Programmiersprachen dienen Auszeichnungssprachen in erster Linie dazu, die Struktur und das Layout eines Dokuments zu definieren, und nicht dazu, Anweisungen oder Berechnungen auszuführen. Sie können jedoch JavaScript-Code in Verbindung mit HTML5-Tags verwenden, um Interaktivität und dynamische Inhalte zu Ihren Webseiten hinzuzufügen.
Ist HTML5 noch aktuell?
HTML5 ist immer noch relevant. Es ist ein neuerer Standard, der viele Vorteile gegenüber älteren HTML-Versionen bietet, beispielsweise verbesserte semantische Auszeichnungsmöglichkeiten, mehr Multimedia-Funktionen und größere Geräteunabhängigkeit. Außerdem ist er mit älteren Browsern abwärtskompatibel, so dass Sie sich keine Sorgen machen müssen, dass Ihre Website nicht von allen angezeigt wird.
Warum funktioniert HTML5 nicht?
HTML5 funktioniert in manchen Fällen nicht, weil es nicht von allen Browsern unterstützt wird. Einige Browser unterstützen HTML5, andere nicht, was zu Kompatibilitätsproblemen führen kann. Außerdem sind einige Funktionen von HTML5 noch nicht vollständig implementiert, so dass die Verwendung dieser Funktionen zu Fehlern führen kann. Bis alle Browser HTML5 unterstützen und die Funktionen vollständig implementiert sind, ist es am besten, bei HTML4 zu bleiben.
Damm 17,
38100 Braunschweig