Sie sind hier : sebastian1012.bplaced.net/ homepage-neu / kreuz-und-quer / tutorials-info-neuigkeiten-css / css-sprites-einsparung-an-http-requests-durch-kombination-von-hintergrund-bildern.php

CSS Sprites – Einsparung an HTTP-Requests durch Kombination von Hintergrund-Bildern

Wie sich ja gewünscht wurde, möchte ich von nun an auch etwas mehr über die Performance von Client-Systemen in Bezug auf Webanwendungen schreiben. Genauer: Wie müssen Webseiten ausgeliefert werden, damit Sie möglichst schnell im Browser des Besuchers dargestellt werden können?
In diesem Beitrag soll es dabei um die CSS Sprites bzw. Image Sprites gehen. Die Yahoo-Präsentation hat ja bereits darauf hingewiesen und hier soll nun erklärt werden, wie es funktioniert.

Problemstellung
Ein Problem, weshalb Seiten oft lange Ladezeiten haben, ist, dass recht viele HTTP-Requests gemacht werden müssen. Da HTTP ein zustandsloses Protokoll ist, muss für jedes Bild, jede JS-Datei und allgemain jede externe Ressource eine neue HTTP-Verbindung zwischen Browser und Server eröffnet werden. Das wäre ja noch nicht problematisch (lediglich umständlich). Das Problem ist aber, dass je nach Browser nur eine bestimmte Anzahl an parallelen HTTP-Requests ausgeführt werden können (oft 2-4). Im Firefox kann diese Einstellung über network.http.max-persistent-connections-per-server auf der about:config-Seite verändert werden.
Dadurch entsteht ein Treppen-Diagramm, das die Gesamtladezeit verdeutlicht: Treppeneffekt beim Laden mehrerer externer Ressourcen
Es wird deutlich, dass einige Beschleunigung des Ladens erreicht werden kann, wenn entweder die Anzahl paralleler Requests erhöht wird oder die Anzahl an Requests verringert wird. Ersteres kann beispielsweise durch unterschiedliche Subdomains gemacht werden (eine für Bilder, eine für JS usw.). Genaueres dazu findet sich beim Beitrag, wenn man auf den Link des Diagramms klickt.
Wir wollen uns jetzt aber mal mit dem anderen Punkt beschäftigen: der Verringerung der HTTP-Requests.

HTTP-Requests verringern könnte man auf 2 Arten: Mehrere Komponenten innerhalb einer Datei konsolidieren (ich hasse dieses Wort, seitdem es in jeder IT-Zeitung steht) oder die Komponenten inline in das HTML-Dokument einbinden. Letzteres geht beispielsweise gut bei CSS (wird z.B. auf Yahoo gemacht) aber auch bei Bildern (aber dann wird der Quellcode richtig hässlich).
Hier soll es um die Konsolidierung mehrerer Komponenten gehen, genauer um das Verringern von Anfragen für Hintergrundbilder.

Jedes mit CSS über url() referenzierte Hintergrundbild benötigt natürlich auch einen HTTP-Request. Aber habt ihr euch mal alte Spiele angesehen (bei aktuellen weiß ichs nicht, dafür hab ich leider kaum noch Zeit)? Dort sind meistens verschiedene GUI-Elemente in einer einzigen Bilddatei zusammengefügt. Hinterher schneidet sich dann das Programm den gewünschten Teil raus. Das spart Speicherplatz und Ladezeit.

Übertragen auf die Webentwicklung ist es ganz ähnlich: Mehrere Hintergrund-Bilder können in eine Grafik-Datei gepackt werden und später trotzdem an der richtigen Position angezeigt werden.
Der erste Schritt dafür ist das Zusammenpacken in eine Datei. Das geht ganz gut mit einem CSS-Sprite-Generator. Dabei sollte allerdings dringend darauf geachtet werden, dass man entweder nur Bilder nimmt, die später horizontal wiederholt werden (repeat-x) oder nur Bilder, die vertikal wiederholt werden (repeat-y) (bei mir traf ersteres dazu, deshalb kann ich die Tauglichkeit des verlinkten Generators für Letzteres nicht einschätzen). Zu den Gründen unten mehr.
Jedenfalls spuckt der Generator letztlich eine Bilddatei aus, in der alle eure Hintergründe enthalten sind. Dazu gibts noch einen CSS-Code.

Allein mit den Anweisungen dort beim Generator habe ich es allerdings nicht hinbekommen, die Sprites zum Laufen zu bekommen, sondern mir war noch dieser Beitrag behilflich. Zuerst muss die Sprite-Grafikdatei nämlich einmal eingebunden werden. Und zwar am besten für alle CSS Regeln, bei denen eines der im Sprite enthaltenen Bilder als Hintergrundbild genutzt wird. (Beispiel siehe unten)

Anschließend kann man sich um das Ersetzen der alten Hintergrundregeln kümmern, denn die werden nun nicht mehr benötigt. Je nachdem, welche CSS-Regeln obiger Generator ausspuckt, ersetzt ihr background-image nun nur noch durch background-position (also inklusive den vom Generator ausgepuckten Werten). Dadurch wird das Sprite an die für das bestimmte Element korrekte Position verschoben.

Nun aber zum repeat. Wenn ihr bisher die Multiformat-Eigenschaft background benutzt habt, müsst ihr das jetzt aufdröseln, denn es wird ja nur noch background-position und background-repeat benötigt. Jedenfalls werdet ihr eventuell schnell merken, dass wenn ihr repeat-x verwendet, es kein hübsches Hintergrundbild wie früher gibt. Der Grund dafür sind unterschiedlich breite Hintergrundbilder. Dadurch entstehen mitunter neben dem Bild weiße (oder transparente; je nachdem, was man einstellt) Ränder. Für ein repeat-x werden aber durchgängige Bilder benötigt.
Deshalb gibt es 2 Wege dieses Problem zu beheben: Entweder alle Bilder nachträglich auf gleiche Breite ziehen. Oder (von mir präferiert) einfach nur den ersten Pixel in der Breite des Sprite-Bildes verwenden (bzw. gleich nur 1px breite Bilder dem Sprite-Generator geben). Kann man ganz einfach per Irfan View oder auch irgendeinem anderen Grafikprogramm auswählen, freistellen / kopieren und entsprechend abspeichern. Es ist sowieso nur ein 1px breites Bild erforderlich, da der Rest ja per repeat-x generiert wird.

Und jetzt kommt noch das zweite Problem, auf das ich gestoßen bin: Ich habe einen Seiten-Hintergrund, der 500px hoch ist (Verlauf). Wenn die Seite länger ist, wird einfach die angegebene Hintergrundfarbe angezeigt. Wenn nun aber unterhalb dieses Bildes im Sprite noch ein völlig anderer Hintergrund kommt, weiß CSS natürlich nicht, wo es das Bild beenden soll (insbesondere da man per CSS zwar das Hintergrundbild verschieben nicht aber einen bestimmten Bereich ausschneiden kann). Entsprechend wird dann einfach der darunterliegende Hintergrund noch mit angezeigt. Entweder man wählt zur Behebung dieses Problems sehr große vertikale Abstände zwischen den einzelnen Hintergründen (diese Lösung ist allerdings nicht sehr flexibel, denn eventuell wird in der Zukunft eine noch längere Seite erstellt, wo der Abstand dann nicht mehr ausreicht) oder man wählt für das Sprite nur Hintergrundbilder von Elementen, deren Höhe feststeht (per CSS-Attribut height; teilweise auch bei Inline-Elementen, jedoch muss da eventuell auf die Schriftgröße geachtet werden – ist diese nämlich relativ angegeben, ist auch die Höhe des Elements variabel).

Nun noch ein kleines Beispiel zur Verdeutlichung:
Alte Regeln:

#header {background: url(bild1.jpg) top left repeat-x; height:80px;} ul.menu li {background: url(bild2.jpg) top left repeat-x; height:15px;}

bild1.jpg und bild2.jpg sind jeweils 1px breit. Durch den Sprite-Generator jagen, Sprite-Datei runterladen bzw. auf Webserver hochladen.

Sprite-Bild einmalig einbinden für alle Elemente, die einen im Sprite enthaltenen Hintergrund haben:

#header, ul.menu li {background-image:url(bg_sprite.png);}

Mit Hilfe der im Generator angegebenen Regeln die alten Regeln ersetzen:
Generator spuckt zum Beispiel aus:

.sprite-bild1 { background-position: 0 -30px; }  .sprite-bild2 { background-position: 0 -110px; }

Einbau in vorhandenes CSS:

#header {background-position: 0 -30px; background-repeat: repeat-x;} ul.menu li {background-position: 0 -110px; background-repeat: repeat-x;}

Und das wars. Mit 2 Bildern macht das noch nicht so viel Sinn, aber ich denke das reicht zum Verdeutlichen.

Falls ihr noch Fragen habt zu diesem Thema, freue ich mich über Kommentare. Euren Erfolg durch diese Maßnahme prüfen könnt ihr übrigens mit Firebug (Tab Net) sowie YSlow (Tab Performance).