Dear screenreader-user, please let me know, if I can make any improvements for your convinience. Mail me to "n" dot "company" at "mac" dot "com". Thank you.
Skip to the navigation. / Zur Navigation springen. Newer Entries / Neuere Einträge Older Entries / Ältere Einträge Skip to the search. / Zur Suche springen. Skip to the content. / Zum Inhalt springen. Skip to the bottom. / Zum Ende springen.
Fullscreen-Modus einschalten um die Artikel besser lesen zu können.
Ich bin seit einiger Zeit ein grosser Fan von Custom Events. Heute musste ich eine Funktion erweitern, bei der nach allen Elementen mit der Klasse insertmsgafter ein eingefügt wird, in den später eine Nachricht eingeblendet wird. Bisher war der Code für diese Funktions sehr einfach:
$('.insertmsgafter', c).after('<span class="msg"> </span>');
Nun aber gab es plötzlich eine neue Grösse der Nachricht und ausserdem musste der Text durch das CMS pflegbar gemacht werden – bisher kam der Text aus einem JavaScript File. Das bedeutet, es ist nicht nur einfach ein mit einer statischen Klasse, je nach dem braucht es eine zusätzliche Klasse für die neue Dimension und eine URL für den Ajax Aufruf.
Der normale Ansatz wäre, alle Elemente mit der $.each() Funktion zu durchlaufen und darin zu entscheiden, welche Klassen gesetzt werden müssen und wo HTML nachgeladen werden muss:
$('.insertmsgafter', c).each(function() {
var $t = $(this);
var size = $t.attr('data-msg-size');
var src = $t.attr('data-msg-src');
var $msg = $('<span class="msg' + (size ? ' msgSize' + size: '') + '"> </span>');
if (src) {
$msg.load(src + ' #ajax');
}
$t.after($msg);
});
Nun hatte ich folgende Idee: Statt alle Elemente mit $.each() zu durchlaufen, binde ich auf allen einen Custom-Event, welcher prüft ob das data Attribut für die Grösse und eines für die den Ajax Aufruf vorhanden ist. Danach rufe ich die Funktion direkt auf und unbinde sie wieder:
$('.insertmsgafter', c)
.bind('setupMsg.sgc', function() {
var $t = $(this);
var size = $t.attr('data-msg-size');
var src = $t.attr('data-msg-src');
var $msg = $('<span class="msg' + (size ? ' msgSize' + size: '') + '"> </span>');
if (src) {
$msg.load(src + ' #ajax');
}
$t.after($msg);
})
.trigger('setupMsg.sgc')
.unbind('setupMsg.sgc');
Ich habe vermutet, dass dieses Vorgehen schneller ist als jedes Element mit $.each() zu durchlaufen. Um das zu testen habe ich einen kleinen Test geschrieben welcher auf 1000 Div’s eine Funktion in verschiedenen Varianten ausführt:
Getestet wurde folgende Funktion:
var calc = function() {
var $msg = $(' ');
$(this).after($msg);
$msg.remove();
};
Für den nativen Loop wurde folgende Funktion verwendet:
for (var i = times; i > 0; i--) {
var $msg = $(' ');
$divs.eq(i - 1).after($msg);
$msg.remove();
}
Leider stimmt meine Vermutung nur teilweise. Das Binding ist zwar fast 3 mal schneller als alle Elemente zu durchlaufen, das Aufrufen der Funktion jedoch dauert fast 1.5 mal so lange – rechnet man das Unbinding dazu, dauert es doppelt so lange wie ein einfaches Loopen mit each.
Das Binden eines Custom-Events ist um einiges schneller als das Durchlaufen aller Elemente mit each – solange die Funktion dann zu einem späteren Zeitpunkt einzeln aufgerufen wird. Um Elemente einmaling aufzubereiten, lohnt sich der Einsatz von Custom-Events aus Performance-Sicht jedoch nicht. Den browser-nativen Loop zu verwenden lohnt sich ebenfalls nicht; Wenn man darin auf ein DOM-Element selektieren muss, dauert es sogar etwas länger.
Ausserdem ganz interessant; Die Ergebnisse in Safari und Firefox sind zwar praktisch identisch, Safari5 ist aber insgesammt etwa 6 mal schneller wie Firefox 3.6.6.
Was sind Eure Ergebnisse in anderen Browsern?
Testsystem:
Inline-Elemente sind für viele Frontend Entwickler mehr oder minder ”unbekannte” Elemente, welche ein Dasein im Schatten des nur allzu bekannten und dominanten Block-Elements führt. Dabei bringen Inline-Elemente eine Reihe von Eigenschaften mit, welche jeder Entwickler kennen sollte – denn ohne sind gewisse Effekte beim Erstellen eines Layouts nicht zu erklären. Das Inlin-Elemente keinen Umbruch erzeugen ist allgemein bekannt. Darüber hinaus, haben Inline-Elemente aber viele weitere Eigenarten. Alle Inline-Elemente folgen einander in einer Linie. Daher werden sie auch «in»line genannt. Genaugenommen sind auch einzelne Buchstaben nichts anderes als eine Aneinanderreihung von Inline-Elementen.
Eine solche “line” hat eine Höhe – diese wird immer durch die Schriftgrösse bestimmt, ist aber um etwa 15% grösser als die Schriftgrösse. Die Elemente liegen jedoch nicht wie im ersten Moment vielleicht anzunehmen ganz “unten” oder “oben” in dieser Linie, sondern ruhen auf einer imaginären Linie dazwischen – ich nenne diese unsichtbare Linie «Basis-Linie»1. Diese Linie dient dazu, dass “überhängende” Buchstaben wie etwa ein «g» nicht aus der “line” fallen.
Der Abstand zur Oberkannte der nächsten Line entspricht der CSS Eigenschaft line-height – ebenso ist dies der Abstand zwischen zwei Basis-Linien. Inline-Elemente ruhen grundsätzlich auf dieser Basis-Linie und können den Abstand der Linen selber nicht verändern – selbst wenn das Element höher ist. Dies äussert sich darin, dass man einem solchen Element zwar einen vertikalen Innenabstand2 geben kann – und dieser auch angenommen wird3 – dadurch jedoch der Abstand zur nächsten Linie nicht verändert wird. Ein Rahmen um ein Inline-Element umschliesst jedoch nicht die Linien-Höhe sondern die Höhe der “line” – also Schriftgrösse plus etwa 15%4.
Auch Bilder sind bekanntlich Inline-Elemente. Sonst könnten wir keine Icons in einer Textzeile platzieren, ohne dass dieses einen Umbruch erzeugt. Wie alle anderen Inline-Elemente folgen auch Bilder den Gesetzen der «Inline-Graviation» – das heisst, sie ruhen auf der imaginären «Basis Linie». In manchen Layouts werden jedoch Bilder verwendet, welche nicht direkt von Text umschlossen wird. Das führt dazu, dass besagte Inline-Eigenschaften schnell vergessen werden können. Ist das Bild von einem Block-Element umschlossen und gibt man diesem Element einen Rand, liegt diese erstaunlicherweise nicht bündig mit der Bild Unterkante. Dies ist natürlich nicht wirklich «erstaunlich» sondern kommt von der etwa 7.5% höherligenden Basis-Linie, auf welcher das Bild ruht.
Diesem Umstand sind wir jedoch nicht hilflos ausgeliefert. Mit der CSS Eigenschaft vertical-align können wir bestimmen, wo ein Element innerhalb der Linie zu liegen kommt. Ob es also auf der unsichtbaren Basis Linie liegt oder nicht, ist beeinflussbar. Der Standard dieser Eigenschaft ist «baseline». Wenn der Rahmen um das Bild also auf der Unterkante anliegend sein soll, darf das Bild nicht mehr auf der «baseline» liegen, sondern muss sich ganz unten in der Line befinden. Folgende Beispiele zeigen ein Bild mit dem Standard-Wert «baseline» und dem Wert «bottom» welcher das Problem löst: