Schlagwort: Snipplet

Ordnung ist das halbe Leben…

06. Juli 2011

Ordnung ist das halbe Leben
Geschachtelte geordnete Listen mit einer fortlaufenden Nummerierung in Form von 1., 1.1, 1.1.1 usw.? Seit CSS 2 lassen sich dank der neuen CSS-Counter auch solche Arten der Aufzählung realisieren. Für ältere Browser wie dem Internet Explorer 7 müssen dabei allerdings ein paar Hürden genommen werden. Dabei sind die CSS-Counter nicht auf Listenelemente beschränkt, da gäbe es eine ganze Reihe schöner Einsatzszenarien. Wahrlich kein brandneues Thema mehr, aber ein Blick lohnt trotzdem.

Geordnete Listen

CSS-Counter erlauben das Zählen von Instanzen bestimmter Elemente bzw. Selektoren. In Kombination mit den Pseudo-Elementen :before und :after lassen sich dann ganze neue Formen der Nummerierung erstellen. Die CSS-Counter sind dabei deutlich leistungsstärker als das alte HTML start Attribut, das ohnehin als deprecated gilt, und ausschließlich die Nummerierung einer Ebene bzw. eines Listenelementes beeinflusst.
Die Nummerierung mit Hilfe von CSS basiert setzt sich aus mehreren Eigenschaften und Funktionen zusammen:

  • counter-reset: Setzt einen oder mehrere Zähler zurück, ein Zähler ist dabei eine frei wählbare Variable. Optional lässt sich für jeden Zähler noch ein Anfangswert festlegen, der Standardwert ist 1.
  • counter-increment: Zählt einen oder mehrere Zähler hoch, auch hier kann man über einen zweiten optionalen integeren Wert die Zählweise beeinflussen (selbst negative Werte sind erlaubt).
  • content: Gibt mit Hilfe der Funktionen counter() oder counters() die Nummerierung aus.

Ein Beispiel

OL {
  counter-reset: item;
}
OL LI {
  display: block;
}
 
OL LI:before {
  content: counter(item) ". ";
  counter-increment: item;
}

Betrachtet man dieses erste Beispiel im Browser, so sieht das Ergebnis noch nicht anders aus, als eine ganz normale geordnete Liste (OL), der einzige Vorteil liegt darin, dass sich die Nummerierung nun erheblich besser gestalten lässt, ohne dass man weitere Tag Verschachtelungen vornehmen muss. Auf einen Nachteil sei an dieser Stelle auch gleich hingewiesen. In Sachen “Accessability” sind diese Lösungen negativ zu bewerten, denn über CSS generierter Content manipuliert die DOM nicht (siehe hier), so dass aktuelle Screen-Reader diese Informationen schlicht nicht ausgeben, die Liste hätte also keine Nummerierung mehr.
Für eine fortlaufende Nummerierung, die die übergeordneten Nummern übernimmt, kann man den CSS Code wie folgt verändern:

OL {
  counter-reset: item;
}
OL LI {
  display: block;
}
 
OL LI:before {
  content: counters(item, ".") ".";
  counter-increment: item;
}

Nun werden die Nummern der übergeordneten Listenelemente übernommen. Die Funktion counters() liefert im Gegensatz zu counter alle Instanzen eines Zählers gleichzeitig. Der zweite Parameter ist das Trennzeichen, mit dem diese Instanzen verkettet werden. Doch Vorsicht mit dieser Syntax. Sie führt im Internet Explorer 7 dazu, dass alle folgenden CSS Notationen nicht mehr interpretiert werden, selbst wenn diese in separaten Stylesheet Dateien abgelegt sind. Anscheinend bringt die Notation der Anfürhrungszeichen innerhalb der Klammern den CSS Interpreter völlig durcheinander. Bedeutet, man kann wahlweise sicher stellen, dass der entsprechende Code projektweit wirklich immer an letzter Stelle kommt, oder man nutzt die Funktion counter anstatt counters – wobei diese Lösung den CSS-Code abhängig von der möglichen Verschachtelungstiefe deutlich vergrößern kann:

/* 1. Ebene */
BODY OL {
  counter-reset: level1;
  list-style-type: none;
}
 
OL LI:before {
  content: counter(level1) ". ";
  counter-increment: level1;
}
 
/* 2. Ebene */
OL LI OL {
  counter-reset: level2;
}
 
OL LI OL LI:before {
  content: counter(level1) "." counter(level2) " ";
  counter-increment: level2;
}
 
/* 3. Ebene */
OL LI OL LI OL {
  counter-reset: level3;
}
 
OL LI OL LI OL LI:before {
  content: counter(level1) "." counter(level2) "." counter(level3) " ";
  counter-increment: level3;
}
...

Text begrenzen, aber richtig!

16. März 2010

Text begrenzen, aber richtig!
Texte bei der Ausgabe in ihrer Länge zu begrenzen ist nun wirklich eine immer wiederkehrende Aufgabe. Daher ist es nicht verwunderlich, dass es zahlreiche Funktionen gibt, die ein solches Abschneiden gewährleisten sollen. Aber was leisten diese Truncate Funktionen wirklich?

Die allgemeine Verfahrensweise solcher Funktionen ist denkbar einfach: Es muss zunächst getestet werden, ob ein String überhaupt länger ist, als die vorgegebene maximale Länge. Ist dieses der Fall, so wäre der String an entsprechender Stelle abzuschneiden, und diese Kürzung ggf. durch eine Zeichenfolge wie zu kennzeichen. Nur was heisst an entprechender Stelle?

Basis vieler Truncate Funktionen in PHP ist die substr Funktion, über die sich ein beliebig langer Text an einer definierten Stelle abschneiden lässt. Berücksichtigt wird bei substr jedes Zeichen, ganz gleich ob es sich um Satzzeichen, Buchstaben oder Leerzeichen handelt.
Aus Gründen der Lesbarkeit kann es aber sinnvoller sein nicht exakt auf eine bestimmte Zahl an Einzelzeichen zu kürzen, sondern stattdessen das nächste Wort- oder Satzende zu finden. Ein Artikel bei the art of web liefert gleich eine ganze Reihe solcher Funktionen. Bei dieser Art von truncate Funktionen wird wahlweise mit einem regulären Ausdruck oder einer Kombination aus substr und strrpos gearbeitet.

Und was passiert, wenn es sich bei dem Text nicht um einfachen Plain-Text, sondern um eine HTML Struktur handelt? Schneidet man solche Texte an der falschen Stelle ab, kann es einem im schlimmsten Fall das gesamte Layout der Seite zerstören, man muss nur das Pech haben einen geöffneten Tag innerhalb der gekürzten Version zu haben, der nicht wieder geschlossen wird. Und selbst wenn das nicht der Fall sein sollte, so werden bei HTML Code die nicht dargestellten Tags als Zeichen mitgezählt, die ausgebene Länge entspricht also gar nicht der gewünschten maximalen Ausprägung. Auch ein Aufteilen eines solchen HTML-Textes an Leerzeichen hilft nicht – viele Tags enthalten Leerzeichen – die Konsequenz wäre damit die Gleiche.

Selbst wenn man vorab alle HTML Tags durch strip_tags entfernt, bleibt ggf. noch das Problem von vorhandenen Entities: Wie schaut es aus wenn ein ä nach &au abgeschnitten wird? Vom dadurch enstehenden Wegfall an Informationen wie Bildern, Verweisen oder anderen Formatierungen durch den HTML Code ganz zu schweigen.

Sobald man es mit HTML zu tun hat, und dieser Code in seiner Struktur auch nach der Kürzung erhalten bleiben soll, wird die Aufgabe deutlich komplexer. Weder substr oder ein regulärer Ausdruck sind ausreichend, denn alle geöffneten Tags müssen (in umgekehrter Reihenfolge) wieder sauber geschlossen werden. Alle Tags dürften keinen Einfluss auf die maximale Anzeigelänge haben und HTML Entities dürften nur als ein Zeichen gewertet werden.

Der “geöffnete Tags”-Problematik nehmen sich noch einige Funktionen an. Bei DZone gibt es ein entsprechendes Snipplet (die ausgereifteren Lösungen findet man in den Kommentaren zum Snipplet) und für die Template-Engine SMARTY gibt es auch einen entsprechenden Modifier. Die Problematik der “wirklichen maximalen Länge”, oder die Entities werden in den meisten Fällen aber nicht mit berücksichtigt.

Die meines Erachtens ausgereifteste Lösung findet man bei CakePHP, dieses Snipplet gewährleistet wirklich eine reibungslose Kürzung von HTML, inklusive Entities und korrekter Längenberechnung.