Tobi's Blog

Archiv für die 'Programmierung' Kategorie

Die CSS-Alternative zu Float – display:inline-block

Erstellt von Tobi am 22. Oktober 2009

Als ich mal seit langem mal wieder ein Layout per CSS gestalten musste, kam die Frage auf, ob es nicht eine Alternative für die float-Angabe im CSS gibt, um Elemente dynamisch nebeneinander platzieren zu können. Denn mit folgenden Problemen hab ich bisher jedes mal zu kämpfen, wenn float im Spiel ist:

  • Ein ein mal gesetztes Float kann nur durch ein Clear aufgehoben werden (oder mit hässlichen Hacks, die aber in verschiedenen Browsern verschiedene Probleme machen)
  • Man kann immer nur ALLE floats (einer Richtung) aufheben. Also nicht nur das eine bestmmte, sondern im Zweiffel auch alle anderen, die man nicht erwischen wollte.
  • In so ziemlich jeder unterschiedlichen IE-Version gibt es damit unterschiedliche Fehler.
  • In allen Browsern brechen die “gefloeteten” Elemente aus ihrem umgebenden Container aus. Das muss man dann auch wieder mit Hacks irgendwie umgehen (und das ist, finde ich, das schlimmste Problem, weil am nervigsten)

Und wenn man es sich mal genau überlegt, wofür float eigentlich gedacht ist, ist es eigentlich klar, dass es so viele Probleme damit gibt. Ich denke es ist eben für Dinge wie ein Bild, was von Textblöckem umflossen wird und nicht um z.B. Navigationspunkte horizontal in einer Reihe anzuordnen oder ein 3-Spalten-Layout zu erstellen.

Und genau die zwei letztgenannten Anforderungen trieben mich dazu, mal in der Liste der CSS-Angaben zu suchen. Gefunden hab ich dort display: inline-block, was zwar auch nicht ohne Probleme ist, aber die sind örtlich begrenzt und eher handhabbar als ein außer Kontrolle geratenes Float.

Man definert mit dieser Angabe, dass sich das aktuelle Element nach “außen” wie ein inline-Element verhält, also keinen Umbruch hat und die Breite ggf. automatisch wählt, “innen” aber alles wie beim Block möglich ist. Also fixe Breitenangaben, wenn man denn will, margin und padding etc. Außerdem ist es extrem stressfrei, da es eben nur für genau dieses eine Element gilt und in dem umgebenden Block quasi eingesperrt bleibt. Also keine Nebenwirkungen außerhalb haben kann. Und will man den Elementfluss unterbrechen, um z.B. mehrere Zeilen zu haben, reicht ein einfaches <br/>.

Da das aber zu schön ist, gibts dann doch noch ein paar Probleme, nämlich folgende:

Whitespaces
Gibt es Zeichen zwischen den Elementen, z.B. Whitespaces, dann werden die, wie bei Fließtext auch, zwischen den Elementen dargestellt und erzeugen einen Abstand. Das kann man verhindern, in dem umgebeenden Block die Schriftgröße 0 zuteilt. Dazu muss es aber eben immer einen Umgebenden Block geben. Wenn man dann nicht für jedes enthaltene Element wieder definieren will, wie groß die Schrift dort wieder sein soll, muss man global eine absolute Schriftgröße angeben (* { font-size; 10pt; }). Alternativ kann man auch einfach keine Zeichen zwischen die Elemente setzen, auch keinen Zeilenumbruch, und braucht dann nicht mit den Schriftgrößen hantieren (der Quellcode ist dann aber unleserlich).
FireFox vor Version 2
Alle Versionen des FireFox vor Version 3 kennen die Angabe nicht. Allerdings gibt es dafür dort eine properitäre Angabe die genau das gleiche macht. Und die inline-block Angabe wird komplett ignoriert. Also kann man einfach VOR display:inline-block; die Angabe display:-moz-inline-box; setzen und alle FF sind glücklich
IE vor Version 8
Alle IE vor der Version 8 kennen die Angabe nicht. Allerdings ist display:inline; dort so fehlerhaft umgesetzt dass es sich exakt so wie display:inline-block verhält :) Dumm ist nur, dass man nicht so einfach vorgehen kann wie beim FF kleiner v3 da die unbekannten Angaben trotzdem verwendet werden (was auch immer er dann macht, aber es ist falsch). Hier kann man entweder ConditionalComments benutzen, worurch die Angaben aber voneinander getrennt abgelegt werden, oder folgende Hacks:

Für IE 6
* html MEINELEMENT { display: inline; }
Für IE 7
*+ html MEINELEMENT { display: inline; }

Leider muss man BEIDE Angaben einzeln machen, da der eine Hack für den IE7 den für den IE6 unbrauchbar macht.

Unterschiedliche Höhen
Die Elemente, die man nebeneinander platzieren möchte, sind ja nicht immer alle gleich groß. Der Trick, das am einfachsten und effektivsten ist, ist einfach alle per vertical-align:top an der oberen Kante ausrichten.

Damit lassen sich für folgende Browser alle Unzulänglichkeiten ausbügeln, die mir bisher aufgefallen sind und es sieht dort überall gleich aus:

  • Opera seit mindestens v9.5
  • IE seit v6
  • FF seit mindestens v0.9

Hier mal ein Beispiel und der Code für eine Liste, deren Elemente nebeneinander stehen sollen und nach deren dritten Element eine zweite Zeile beginnen soll. Natürlich ist das für so ein einfaches beispiel viel einfacher per float umsetzbar, aber ich will gar nicht wissen, wie viele Zeilen CSS-Hacks ich schon geschrieben habe, um genau so einfache Sachen in größeren Layouts für den IE wieder gangbar zu machen.

Bsp: Display:inline-block in Aktion.


<style type="text/css">
/* solve whitespace-problem, part 1 */
* {
font-size: 12pt;
}
div#codeexdib {
/* solve whitespace-problem, part 2 */
font-size: 0;
/* only to show... */
border: solid 2px blue;
margin: 0;
padding: 0;
list-style: none;
}
div#codeexdib div {
/* FF before v3 solved */
display: -moz-inline-box;
display: inline-block;
/* force alignment */
vertical-align: top;
/* only to show... */
border: solid 2px red;
margin: 0;
padding: 1em;
}
/* IE6 solved */
* html div#codeexdib div {
display: inline;
}
/* IE7 solved */
*+ html div#codeexdib div {
display: inline;
}
</style>
<div id="codeexdib">
<div style="height:8em">Eins:1</div>
<div>Zwei:1</div>
<div style="width:4em">Drei:1</div>
<br/>
<div>Eins:2</div>
<div>Zwei:2</div>
</div>

Nachtrag: Ich hatte im Beispiel vormals UL und LI verwenden und darin ein BR, das funktioniert natürlich nicht weils nicht valide ist. Interessanterweise “mekern” nur alle IE (6 bis 8) und verschieben das BR in das davor liegende LI um wieder validen Code zu haben.

Abgelegt unter Programmierung | 3 Kommentare »

WordPress-Mythen: Beschleunigung mit Themenoptimierung

Erstellt von Tobi am 9. Februar 2009

Bei meinen Recherchen wie ich meinem etwas leistungsschwachen Server noch etwas von der Last mit WordPress abnehmen kann, bin ich über diesen Artikel bzw. dessen englische Quelle gestolpert, die sich merkwürdig anhörten. Die Artikel sind zwar von Anfang 2008, womit das Verhalten ggf. nur bei alten WP-Versionen autritt, die Kommentare reichen aber bis Dezember 2008 wo schon die aktuelle Version im Umlauf war.

Es geht darum, dass man WordPress bzw. die Themen damit schneller machen kann wenn man die Config-Angaben in den Templates statisch hinterlegt. Es wird behauptet dass damit [...]you can remove 11 queries to the database, and this can highly speed up your theme[...].

Ein kurzer Test am eigenen Blog belegt, dass das nicht stimmt. Die Config-Werte werden, so wie sich das auch gehört, am Stück eingelesen und Programmintern gecacht. Das macht den Optimierungseffekt der vorgeschlagenen Maßnahmen zunichte. Außer man gehört der Erbsenzähler-PHP-Echo-Statt-Print-Benutzen Fraktion an, dann sollte man das trotzdem unbedingt machen ( sorry Daniel, aber in dieser Disskusion ist noch nicht das letzte Wort gefallen ;)

Und so kann man’s selber testen:

MySQL Query-Log einschalten
vi /etc/mysql/my.cnf die Zeile #log = /var/log/mysql/mysql.log auskommentieren
MySQL neu starten (reload reicht nicht!)
sudo /etc/init.d/mysql restart
Abfrage starten
Den Blog ein mal im Browser aufrufen

Die Abfragen werden in der Datei /var/log/mysql/mysql.log geloggt. Danach sollte man, wenn der Blog live im Netz steht, das Logging schleunigst wieder abschalten. Erstens bremst es MySQL stark aus und zweitens wird die Datei sonst schnell sehr groß.

Bei mir ergeben sich ganze 23 Abfragen plus Connect und Disconnect. Die Config-Tabelle selbst wird nur zwei mal abgefragt und keines der angeblich wegoptimierten Abfragen ist dabei (siehe Liste weiter unten). Und in meinem Thema stehen die Config-Angaben nicht statisch drin. Alle anderen Abfragen betreffen den Inhalt. Alles in allem wenig Optimierungsbedürftig.

Fazit

Auch wenn man oft den Glauben schon verloren hat, manchmal arbeiten Programme auch mal so wie es sich gehört. Selbst in PHP geschriebene *duckUndWegrenn*

Hier mal meine komplette Liste das SQL-Abfragen beim Aufruf der Startseite (die IDs sind anonymisiert). Bei Unterseiten ist das ähnlich, nur kommen da noch Abfragen über die Kategorien dazu:

Connect wp@localhost on
Init DB wp
SELECT option_value FROM wp_options WHERE option_name = 'siteurl'
SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'
SELECT t.*, tt.*, tr.object_id FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN wp_term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ('category', 'post_tag') AND tr.object_id IN (301, 302, 303, 304, 305) ORDER BY t.name ASC
SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE post_id IN (301, 302, 303, 304, 305)
SELECT t.*, tt.* FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ('category') ORDER BY t.name ASC
SELECT object_id, term_taxonomy_id FROM wp_term_relationships INNER JOIN wp_posts ON object_id = ID WHERE term_taxonomy_id IN (1,2,3,4,5,6,7,8,9) AND post_type = 'post' AND post_status = 'publish'
SELECT t.*, tt.* FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ('link_category') AND tt.count > 0 ORDER BY t.name ASC
SELECT * , IF (DATE_ADD(link_updated, INTERVAL 120 MINUTE) >= NOW(), 1,0) as recently_updated FROM wp_links INNER JOIN wp_term_relationships AS tr ON (wp_links.link_id = tr.object_id) INNER JOIN wp_term_taxonomy as tt ON tt.term_taxonomy_id = tr.term_taxonomy_id WHERE 1=1 AND link_visible = 'Y' AND ( tt.term_id = 16 ) AND taxonomy = 'link_category' ORDER BY link_name ASC
SELECT * , IF (DATE_ADD(link_updated, INTERVAL 120 MINUTE) >= NOW(), 1,0) as recently_updated FROM wp_links INNER JOIN wp_term_relationships AS tr ON (wp_links.link_id = tr.object_id) INNER JOIN wp_term_taxonomy as tt ON tt.term_taxonomy_id = tr.term_taxonomy_id WHERE 1=1 AND link_visible = 'Y' AND ( tt.term_id = 15 ) AND taxonomy = 'link_category' ORDER BY link_name ASC
SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM wp_posts WHERE post_type = 'post' AND post_status = 'publish' GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC
SELECT * FROM wp_posts WHERE (post_type = 'page' AND post_status = 'publish') ORDER BY post_title ASC
SELECT * FROM wp_posts WHERE post_type = 'post' AND post_status = 'publish' ORDER BY post_date DESC LIMIT 10
SELECT * FROM wp_comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 5
SELECT COUNT(comment_ID) FROM wp_comments WHERE comment_post_ID = 11 AND comment_parent = 0 AND comment_date_gmt < '2009-02-05 22:13:29'
SELECT * FROM wp_posts WHERE ID = 11 LIMIT 1
SELECT COUNT(comment_ID) FROM wp_comments WHERE comment_post_ID = 12 AND comment_parent = 0 AND comment_date_gmt < '2009-01-19 23:15:53'
SELECT COUNT(comment_ID) FROM wp_comments WHERE comment_post_ID = 13 AND comment_parent = 0 AND comment_date_gmt < '2009-01-19 23:09:05'
SELECT * FROM wp_posts WHERE ID = 13 LIMIT 1
SELECT COUNT(comment_ID) FROM wp_comments WHERE comment_post_ID = 14 AND comment_parent = 0 AND comment_date_gmt < '2009-01-19 14:35:26'
SELECT * FROM wp_posts WHERE ID = 14 LIMIT 1
SELECT COUNT(comment_ID) FROM wp_comments WHERE comment_post_ID = 15 AND comment_parent = 0 AND comment_date_gmt < '2009-01-05 10:24:01'
SELECT * FROM wp_users WHERE ID = 7 LIMIT 1
SELECT meta_key, meta_value FROM wp_usermeta WHERE user_id = 7
Quit

Abgelegt unter Programmierung, Technik | 3 Kommentare »

RFC konforme E-Mail-Header

Erstellt von Tobi am 13. November 2008

Wie schon bei den Problemen mit UTF-8 E-Mails berichtet, gibt es eine Spezifikation, wie ein Mail-Header zu kodieren ist. Und als Header zählt auch der Betreff.

Laut dieser Vorgabe (RFC 1522) dürfen in E-Mail-Headern nur die ersten 127 Zeichen des ASCII-Zeichensatzes benutzt werden. Quasi nur alphanumerische Zeichen und eine Hand voll Sonderzeichen. Also auf keinen Fall Umlaute oder ähnliches.

Wenn man zum Versenden z.B. das Perl-CPAN-Modul MIME::Lite benutzt, werden die nicht erlaubten Zeichen nicht automatisch konvertiert. Da muss man sich selber kümmern.

Am leichtesten geht es mit dem Perl-CPAN-Modul MIME::EncWords. Die Funktion MIME::EncWords::encode_mimewords(STRING) : QUOTED_STRING macht eben genau diese Konvertierung, wobei es interessanter weise den aktuellen Zeichensatz erraten kann. Diesen Teil werde ich noch mal genauer unter die Lupe nehmen, da es schon länger auf meiner Liste steht.

Wie oben schon erwähnt, kann und sollte man alle E-Mail-Header quoten. Die Funktion ändert im übrigen den übergebenen String nur, wenn es wirklich nötig ist. Reine Ascii-Inhalte, die unterhalb der 127 Zeichens liegen, werden so belassen wie sie sind. Den E-Mail-Inhalt sollte man aber nicht damit übersetzen lassen. Das funktioniert zwar auch soweit, aber die Zeilenumbrüche sind bei mir dabei verloren gegangen.

Abgelegt unter Perl, Programmierung | Keine Kommentare »

Probleme beim Dokumentendownload im IE

Erstellt von Tobi am 5. Juli 2008

Für ein altbekanntes Problem habe ich jetzt die offizielle Fehlermeldung mit einem interessanten Kommentar gefunden. Um folgendes Problem geht es:

Wenn im IE ein z.B. PDF-Dokument geöffnet wird, z.B. mit dem InlineViewer, und der Server das cachen per Header verbietet, kommt beim ersten Aufruf des Dokumentes ein “Datei ist ungültig/nicht vorhanden”.

Um das Problem zu umgehen muss die no-cache Angabe entfernt werden. Herausgefunden wurde das nur in mühsamer Kleinarbeit.

Bei Microsoft selbst ist das Verhalten ebenfalls dokumentiert unter http://support.microsoft.com/?id=316431. Dort kann man folgendes lesen:

Status: Es handelt sich hierbei um ein beabsichtigtes Verhalten.

Ich will ja nix sagen, aber wenigstens ein wenig einfallsreicher hätte die Ausrede schon sein können. Das schnöde “It’s not a bug, it’s a feature” ist schon so dermaßen abgelatscht…

Abgelegt unter Programmierung | 1 Kommentar »

Schlechte CPAN-Module

Erstellt von Tobi am 12. März 2008

Heute ist mir mal wieder (schmerzhaft) klar gemacht worden, dass es viel Mist im CPAN gibt. Man tendiert halt auch sehr leicht dazu, ein fertiges Modul einfach zu benutzen. Wenig Arbeit, schnelle Ergebnisse. Aber das geht eigentlich immer schief. Die Frage ist nur, wie schlimm die Fehler sind.

Mein konkretes Beispiel: das Modul HTML::Tidy, was auch in anderen Modulen öfter verwendet wird, ist ein wenig Bug-verseucht. Das ist mir leider erst NACH dem Produktiveinsatz aufgefallen. Das Encoding wird von ISO nach UTF oder anders herum durchgeführt, ohne dass man das mitgeteilt bekommt. Am Ende hatte ich hunderte schadhafte Datenbankeintragungen :(

Fazit: wenn man ein CPAN-Modul benutzen will, sollte man die Bugs, die Anzahl der durchgefallenen Tests, die Bewertung und den Quellcode ganz genau betrachten. Dumm ist nur, dass einige Module so viele Abhängigkeiten haben. Da ist eine komplette Prüfung ‘ne ziemliche Sysiphos-Arbeit. Aber man sonnte sich die Arbeit machen. Immer!

Abgelegt unter Perl, Programmierung | 1 Kommentar »