Verwendung von CSS-Expressions (IE)

Autor
Gunnar Bittersmann
Letzte Änderung
2009-05-10

Anwendung von CSS-Expressions

CSS-Expressions sind eine Eigenheit des Internet Explorers, Werte von CSS-Eigenschaften mit JavaScript berechnen zu können. Andere Browser kennen dies nicht und ignorieren solche Deklarationen. CSS-Expressions lassen sich einsetzen, um Schwachstellen der CSS-Interpretation des IE auszugleichen.

Betrachten wir als Beispiel Vorschaubilder (thumbnails) unterschiedlicher Größe, die in Diarahmen (100 × 100 Pixel) horizontal und vertikal zentriert dargestellt werden sollen. Wir zeichnen sie als Liste aus:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Verwendung von CSS-Expressions (IE) – Beispiel 1</title> <link rel="stylesheet" type="text/css" href="example1.css"/> </head> <body> <ul> <li><img src="FernsehturmWeltzeituhr.64.jpg" alt="Fernsehturm und Weltzeituhr"/></li> <li><img src="BrandenburgerTor.64.jpg" alt="Brandenburger Tor"/></li> </ul> </body> </html>

und geben im Stylesheet an:

ul { list-style: none; margin: 0; padding: 0; } li { border-radius: 8px; -moz-border-radius: 8px; -webkit-border-radius: 8px; border: 1px solid black; float: left; height: 100px; line-height: 98px; margin: 4px; padding: 0; text-align: center; width: 100px; } img { vertical-align: middle; }

So sieht es aus: Beispiel 1 | im Iframe:

Damit erreichen wir die gewünschte Darstellung – außer im IE 6, dieser stellt die Bilder nicht vertikal zentriert dar. Deshalb berechnen wir den oberen Abstand der Bilder: Höhe des Bilderrahmens minus Höhe des Bildes ergibt den Freiraum, den wir gleichmäßig über und unter das Bild verteilen wollen. Durch 2 geteilt erhalten wir also den oberen Abstand:

img { vertical-align: middle; margin-top: expression( (100 - this.height) / 2 + "px" ); }

Damit die Beispiele auch im IE 7 nachvollziehbar sind, lassen wir diesen auch die Expressions auswerten, obwohl es nicht erforderlich wäre. Die Darstellung im IE 7 erfordert eine Korrektur:

*:first-child+html img { display: block; }

Die Veränderungen in den Beispielen lassen sich natürlich nur im Internet Explorer nachvollziehen, deshalb sind sie auch nur im IE verlinkt.

Probleme mit CSS-Expressions

Im Abschnitt Avoid CSS Expressions heißt es (Übersetzung des Autors): „Das Problem mit Expressions ist, dass diese viel öfter ausgewertet werden als man denkt. Sie werden nicht nur dann ausgewertet, wenn die Seite gerendert oder ihre Größe geändert wird, sondern auch, wenn gescrollt wird und sogar dann, wenn der Nutzer die Maus auf der Seite bewegt.“ [SPEEDING-UP] Dies verlangsamt die ohnehin schon langsame Darstellung von Webseiten im Internet Explorer noch mehr.

„Das Einfügen eines Zählers in die CSS-Expression ermöglicht uns zu erkennen, wann und wie oft die CSS-Expression ausgewertet wird.“ [ibid.] Folgen wir Steve Soulders’ Beispielen [Souders] und fügen einen Zähler am Ende des body ein:

</ul> <p>CSS-Expressions wurden <span id="counter">0</span> Mal ausgewertet.</p> <script type="text/javascript src="counter.js"></script> </body> </html>

In counter.js:

function count() { var counter = document.getElementById("counter"); counter.firstChild.nodeValue = parseInt(counter.firstChild.nodeValue) + 1; return true; }

Wir rufen den Zähler mit Conditional Operator auf, denn in der CSS-Expression wird keine JavaScript-Anweisung erwartet, sondern ein JavaScript-Ausdruck [Schäfer]:

img { vertical-align: middle; margin-top: expression( count() ? (100 - this.height) / 2 + "px" : "0" ); }

In der Tat, die Expression wird wieder und immer wieder ausgewertet. Es muss also ein Weg gefunden werden, dass sie nur dann ausgewertet wird, wenn es nötig ist.

Lösung: Einmalige Auswertung der CSS-Expressions

Im Abschnitt Avoid CSS Expressions findet sich auch die Lösung des Problems: „einmal ausgewertete Expressions, wobei das erste Mal, wenn eine Expression ausgewertet wird, diese die CSS-Eigenschaft auf einen expliziten Wert setzt, welcher die CSS-Expression ersetzt.“ [SPEEDING-UP]

Im Quelltext des dritten Beispiels [Souders] finden wir, wie es geht: Vor(!) dem Stylesheet fügen wir ein Script ein:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Verwendung von CSS-Expressions (IE) – Beispiel 4</title> <script type="text/javascript" src="setMarginTop.js"></script> <link rel="stylesheet" type="text/css" href="example4.css"/>

In setMarginTop.js definieren wir die Funktion:

function setMarginTop(element) { element.style.marginTop = (parseInt(element.parentNode.currentStyle.height) - element.height) / 2 + "px"; }

die wir bei der Auswertung der CSS-Expression aufrufen:

img { vertical-align: middle; margin-top: expression( count() ? setMarginTop(this) : "0" ); }

Die Anzahl der Auswertungen der CSS-Expressions entspricht nun der Anzahl der Bilder; die Expressions werden nun also genauso oft ausgewertet wie nötig.

Unschön daran ist, dass der Quelltext dafür jetzt über 2 Stellen – das Stylesheet und das Script – verteilt ist. Es wäre schöner, so wie vorher sämtlichen Code im Stylesheet zu haben. Auch dafür gibt es eine Lösung: eine anonyme Funktion, die sofort ausgefüht wird [Schäfer]:

img { vertical-align: middle; margin-top: expression( count() ? (function(element) { element.style.marginTop = (100 - element.height) / 2 + "px"; })(this) : "0" ); }

Die Zeile <script type="text/javascript" src="setMarginTop.js"></script> wird wieder entfernt.

Zu guter Letzt

Der Zähler hat nun seine Schuldigkeit getan und wird wieder entfernt (p und script im Markup, Conditional Operator in der CSS-Expression). Damit entspricht das Markup wieder dem ursprünglichem aus Beispiel 1.

Wie eingangs erwähnt, ist die Auswertung der CSS-Expressions im IE 7 nicht nötig; sie werden nur bis IE 6 benötigt. Deshalb geben wir die Expression nun mit * html-Hack an und entfernen auch den IE-7-Hack wieder:

img { vertical-align: middle; } * html img { margin-top: expression( (function(element) { element.style.marginTop = (100 - element.height) / 2 + "px"; })(this) ); }

Problematisch daran sind die geschweiften Klammern: Parser von Webkit-Browsern (Safari, Chrome) steigen an der Stelle aus und beachten nachfolgende Regeln nicht mehr. Um CSS-Expression dennoch nicht ans Ende des Stylesheets oder gar in ein gesondertes Stylesheet verbannen zu müssen, bietet sich die Notation mit Function-Objekt [SELFHTML] an:

* html img { margin-top: expression( (new Function('elem', 'elem.style.marginTop = (parseInt(elem.parentNode.currentStyle.height) - elem.height) / 2 + "px";'))(this) ); }

Referenzen

Schäfer
Mathias Schäfer, Geheimnisse der JavaScript-Syntax, SELFHTML aktuell Weblog, http://aktuell.de.selfhtml.org/weblog/javascript-syntax
SELFHTML
SELFHTML, http://de.selfhtml.org
Souders
Steve Souders, Avoid CSS Expressions, http://stevesouders.com/hpws/rule-expr.php
SPEEDING-UP
Best Practices for Speeding Up Your Web Site, Yahoo! Developer Network, http://developer.yahoo.com/performance/rules.html