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.
Conclusion:
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:
- Firefox 3.6.
- Safari Version 5.0 (6533.16)
- Mac OS X 10.6.4
Ich sehe, es läuft..