Bild mittels CSS austauschen

in einem Projekt hatten wir an einem bestimmten Punkt die Notwendigkeit, Bilder möglichst einfach austauschen zu können. „Normale“ Bilder sollten einfach durch hochauflösende Bilder ersetzt werden können. Ich wollte gerne auf JavaScript verzichten, denn da es sich nicht um redaktionell eingepflegte Bilder handelte – sondern um Logo, Icons u.ä. -, war eine reine CSS-Lösung möglich.

Einer spontanen Eingebung folgend nutzte ich die Eigenschaft „content“ und löschte mit dieser die Bildquelle, um dann dem Bild ein Hintergrundbild zu geben. Dafür musste das Bild natürlich konkrete Dimensionen besitzen. Deshalb muss es entweder als Blockelement oder als Inline-Blockelement formatiert sein.

Die endgültige Lösung bei jsFiddle arbeitet mit Media-Queries. Sie zeigt deshalb auf einem iPad 3 ein anderes Ergebnis, als auf einem normalen Rechner.

Das grundsätzliche Prinzip ist einfach:

img {
content: ""; 
background-image: url("http://ein-tolle-url"); 
width: 300px; /* konkrete Breite des Bildes */ 
height: 200px; /* konkrete Höhe des Bildes */ 
display: inline-block;
}

Ist doch interessant, dass man einem Bild ein Hintergrundbild mitgeben kann, oder?

Nachtrag: Fritz Weisshart wies mich in einem Kommentar darauf hin, dass die Technik im Firefox nicht funktioniert. Das ist richtig. Ich habe vor lauter Begeisterung nur im Chrome und dann auf dem iPad 3 getestet. Denn für dieses war die Technik ja gemacht. Für und wegen dessen Retina-Display. Die Technik ist also nur halb gut, wenn überhaupt. Und sie ist nicht ganz im Sinne der Specs, denn eigentlich ist „content“ nur im Zusammenhang mit „:before“ und „:after“ gedacht. Aber: es funktioniert zumindest in einigen Browsern. 😉

13 Responses to “Bild mittels CSS austauschen”

  1. Thomas sagt:

    Nett, aber wird dadurch nicht auch jedesmal die ursprüngliche Ressource geladen?

    • Jens Grochtdreis sagt:

      Ja, die Originalressource wird geladen. Es gibt aktuell keine tolle Lösung, die ohne JS funktionieren würde.

  2. Ich löse dieses Problem derzeit so, dass ich alle Bilder nur als Background-Image einbinde. Für Retina-Screens wird dann ein zweites CSS geladen, das alle Bild URLs durch die Retina URLs ersetzt.
    Geladen wir hier laut Browser Inspector nur die jeweils richtige Version, nicht beide.

    Die CSS Dateien sind synchron, da sie auf einer einzigen CSS Datei mit Variablen basiert, aus der mittels Compass zwei CSS Dateien ohne Variablen erzeugt werden.

    Was hältst du von dieser Möglichkeit?

    • Jens Grochtdreis sagt:

      @Daniel: Diese Vorgehensweise finde ich auch besser, als meine beschriebene. Ich hatte nur das Problem, dass der CMS-Dienstleister wohl keine Möglichkeit sah, bei einigen der betroffenen Icons über Hintergrundbilder zu arbeiten. Der Grund ist mir unbekannt und auch recht egal. Deshalb mussten wir mit Vordergrundbildern arbeiten.

      In einem weiteren Schritt mussten wir die vorgestellte Lösung fallen lassen und auf JavaScript umsteigen, weil die Agentur sich nicht in der Lage sah, den Bildern auch noch Klassen oder IDs mitzugeben. Da kann man zwar nur den Kopf schütteln, aber es ist die Realität in sogenannten Enterprise Systemen.

      Nichtsdestotrotz fand ich die Technik ganz interessant und wollte sie weitergeben.

      Deine Compass-Vorgehensweise interessiert mich allerdings. Wie kannst Du denn aus einem SCSS mittels Compass zwei Dateien erstellen? Oder habe ich Dich da falsch verstanden? Wenn das nämlich ginge, dann könnte man ja alle IE-Sonderbehandlungen in einem CSS schreiben und Compass würde sie separieren.
      Das wünsche ich mir für die Zukunft. Es war auch schonmal eine Idee bei Sass, aber ich fürchte, es dauert noch. Falls es überhaupt je kommt.

  3. lab sagt:

    Gute Idee. Aber warum dann nicht gleich das zu ersetzende Bild in den content laden?


    img#replace {
    content: url(http://placedog.com/600/400);
    ...
    }

    Hast Du getestet, wo das überall (nicht) funktioniert (aka IE)?

    Gruß, Kai

    • Jens Grochtdreis sagt:

      @Kai: Auf die naheliegende Idee komme ich natürlich wieder nicht. Werde das mit dem direkten src-Austausch mal ausprobieren.

      Ob das im IE funktioniert ist mir egal. Denn das Ziel war ja, auf Geräten mit high-density Displays (also vor allem iPad3) ein anderes Bild ausliefern zu können. Und die modernen Geräte haben alle eine prima Eigenschaft: es läuft kein IE auf ihnen 🙂

      Und wenn es dann mal Geräte mit dem IE10 geben wird, dann wird der das können.

  4. Meine CSS Kenntnisse sollten auch mal wieder auf den aktuellen Stand gebracht werden.
    Ich war bisher der Meinung, “ content: can only be used with the pseudo elements :after and :before.“

    • Jens Grochtdreis sagt:

      Fritz, so ging es mir auch. Aber ich habe es einfach mal ausprobiert. Und es genügt ja vollkommen, dass es funktioniert, auch wenn es nicht so gedacht war. 🙂 Und da ich ja nur die Bildquelle neu schreibe bliebe ein eventueller Alt-Text auch erhalten. Im konkreten Falle war das egal, weil es sich eh nur um dekorative Bilder (meist Logos) handelte.

  5. Zu meiner beschriebenen Compass-Arbeitsweise:

    Ich habe 3 Dateien:
    _image-sources.scss
    images-default.scss
    images-retina.scss

    Alle Background-Image-URLs sind in _image-sources.scss mit einer Variable angegeben, z.B.
    background-image: url($base+$res+'/ipad.png');
    $res ist dabei eine bisher noch nicht definierte Variable. Aufgrund dessen, dass der Dateiname mit einem Unterstrich beginnt wir diese nicht compiliert.

    Die anderen beiden .scss Dateien beinhalten jetzt jeweils nur die Variablendefinition (z.B. als $res: "2x";) und importieren dann die _image-sources.scss.

    Somit erhalte ich im Endeffekt 2 CSS Dateien (images-default.css und images-retina.css) die jeweils die Bild URLs enthalten. Natürlich setzt dies eine sinnvolle Ordnerstruktur voraus.

  6. @Daniel: Vielen Dank für diesen Denkanstoss. Den habe ich offenbar gebraucht. Denn ich suche schon seit Monaten nach einer einfachen Variante, um für einzelne CSS3-Eigenschaften die IE-Filter-Äquivalente in eine separate IE-Datei ausgeben zu können. Die Lösung lag eigentlich nahe, doch ich habe sie erst durch Deinen Kommentar gesehen. Danke!

    Darüber blogge ich dann demnächst und stelle meine ersten Mixins auf github.

  7. Hallo Jens,

    > Aber ich habe es einfach mal ausprobiert. Und es genügt ja vollkommen, dass es funktioniert,

    Nochmal nachgefragt:
    Ist die Technik nur für iOS / Safari / webkit gedacht?
    Ich hab’s natürlich auch einfach mal ausprobiert. Mit dem Firefox. Und hab’s nicht geschafft.
    Hast du eventuell einen Testcase online, den ich mir mit verschiedenen Browsern / BS anschauen könnte.

    • Jens Grochtdreis sagt:

      @Fritz: Die Technik war im konkreten Fall tatsächlich nur für den iOS-Webkit gedacht, weil wir für das high-density Display des iPad 3 Alternativen einbinden wollten.

      Die Testcases sind im Text verlinkt. Und Du hast Recht: der FF ersetzt das Bild seltsamerweise nicht. Bzw.: er verhält sich wie gewünscht laut Specs und reserviert die content-Eigenschaft nur für die Pseudoelemente :before und :after.

  8. Mathias sagt:

    Das man Bildern ein background-image mitgeben kann, lässt sich auch an anderen Stellen gut einsetzen: Ich verwende das gerne für freigestellte Produktbilder (also 24-Bit PNGʼs), denen so einfach ein beliebiger Hintergrund (oder Farbe, CSS-Verlauf, …) mitgegeben werden kann – ganz ohne zusätzliches HTML-Markup. Funktioniert nach meinen Tests ab IE7 wunderbar.