Dienstag, 27. Januar 2009

jQuery Tipp 1 – bind() verbessern

Jeder, der mit jQuery z.B. eigenen HTML Code erzeugt, und dazu irgendwelche Events wie onclick, onmouseover etc. einsetzt, kommt um die Methoden $.bind() und $.unbind() nicht herum. Diese Methoden werden auf einen jQuery Selector angewendet:

$("#testid div").bind("click", function(ev) {
    alert("Hello World!")
});

In dem oben gezeigten Beispiel geben wird nach dem Anklicken eines DIV Elementes unterhalb des HTML Elements mit der ID testid der Text Hello World! angezeigt.

Nehmen wir an, dass es 100 DIV Elemente unter dem Elternelement testid gibt, dann könnte man das obige Beispiel auch so schreiben (und so wird das auch intern abgearbeitet):

$("#testid div").each(function() {
    $(this).bind("click", function(ev) {
        alert("Hello World!")
    });
});

Um das noch etwas aufzublähen könnte man auch noch das hier schreiben:

$("#testid").find("div").each(function() {
    $(this).bind("click", function(ev) {
        alert("Hello World!")
    });
});

In einem Beispiel mit 100 Elementen komme ich auf einem Testrechner auf 10-20 Millisekunden, das ist nicht besonders viel. Baut man den Beispielcode weiter aus, und verwendet mehrere Stylesheetangaben, verschachtelt die Elemente in eine große HTML Seite von ca. 100 kB, dann werden aus den 10-20 Millisekunden schnell auch mal 100 oder auch mehr.

Wer jetzt weiter den HTML Code in kurzen Abständen aktualisieren möchte, muss zudem noch die .unbind() Methode aufrufen, da sonst der Speicher im Internet Explorer wächst.

Eine andere Lösung ist, dass man anstatt jedem einzelnen DIV Element ein onclick zuweist, und dann das Event nur noch am Elternelement abfängt. Das könnte dann so aussehen:

$("#testid").bind("click", function(ev) {
    alert("Hello World!")
});

Misst man das Zuweisen des Events kommt man auf 0-1 Millisekunde (so genau wie das eben mit dem Date Objekt in JavaScript geht).

Da wir in unserem Beispiel noch gar nicht auf das eigentliche Element zugreifen, ist hier erst einmal alles in Ordnung. Wollen wir auf das eigentliche Element zugreifen, können wir jetzt die Eigenschaft .srcElement des Ereignis ev verwenden:

$("#testid").bind("click", function(ev) {
    var ele = $(ev.srcElement);
    alert(ele.html());
});

Als kleinen Test zeigen wir mal den HTML Source Code des angeklickten Elements an. Soweit so gut.

Doch was ist ev.srcElement eigentlich wirklich? Ist es das erwartete DIV Element? Das kommt ganz auf den HTML Source Code an:

  1. Enthält das DIV Element nur Text, und ist mindestens so breit wie das Elternelement testid, dann erhalten wir das DIV Element.
  2. Ist das DIV Element schmäler, und wir haben daneben geklickt, bekommen wir den HTML Source Code von testid.
  3. Ist in dem DIV Element selber HTML enthalten, also z.B. verschiedene Links oder SPAN, B, … Tags, und wir haben gezielt auf diesen geklickt, bekommen wir dessen Inhalt angezeigt.

Beheben kann man das nur, indem man dafür sorgt, dass die DIV Elemente den leeren Bereich von testid komplett ausfüllen. Das ist besonders bei Tabellen als auch DIV Elementen sehr leicht.

Für den Fall, dass das DIV Element selber viele HTML Tags enthält, habe ich mir eine findParent Methode gebastelt. Mit dieser Methode ist es dann möglich, mit Hilfe eines jQuery Selectors das oberste DIV Element (direkt unter testid) zu finden.

function findParent(ele, selector, maxr) {

    if(ele.is(selector))
        return ele;

    var c = 0;
    while(++c < maxr && !ele.is("body") && !ele.is(selector)) {
        ele = ele.parent();
    }

    if(ele.is(selector))
        return ele;

    return null;
}

Der .bind() Aufruf sieht dann wie folgt aus:

$("#testid").bind("click", function(ev) {
    var ele = $(ev.srcElement);
    var div = findParent(ele, "div[:parent=#testidx]", 10);

    alert(div.html());
});

Und hier nochmal eine ganze HTML Seite mit dem Beispiel:

<html><body>

<div id="testid" 
style="background-color:red;width:240px;"></div> <script type="text/javascript"
src="http://jqueryjs.googlecode.com/files/jquery-1.3.1.min.js"></script>
<script type="text/javascript">

function findParent(ele, selector, maxr) {

    if(ele.is(selector))
        return ele;

    var c = 0;
    while(++c < maxr && !ele.is("body") && !ele.is(selector)) {
        ele = ele.parent();
    }

    if(ele.is(selector))
        return ele;

    return null;
}

$(document).ready(function() {

    // Beispiel HTML
    var h = [];
    for(var i=0; i<10; i++) {
        h.push("<div style=\"background-color:yellow;width:200px;cursor:hand;\">" + i + "</div>");
    }
    h.push("<div style=\"background-color:yellow;width:240px;cursor:hand;\">10</div>");
    h.push("<div style=\"background-color:green;width:240px;cursor:hand;\">11 <b>fett</b> <i>kursiv</i></div>");
    $("#testid").html(h.join(""));

    $("#testid").bind("click", function(ev) {
        var ele = $(ev.srcElement);
        var div = findParent(ele, "div[:parent=#testidx]", 10);

        alert(div.html());
    });

});

</script>

</body></html>

image Klickt man nun in den roten Bereich (das ist der Bereich, bei dem die DIV Elemente nicht bis an den Rand des Elternelements reichen, dann erhält man den kompletten HTML Source Code des Elternelements testid. Im gelben Bereich wird der richtige Source Code angezeigt, also die Zahlen 0..10.

Bei der grünen Zeile kann man jetzt auf die 11, auf fett oder kursiv klicken, und erhält immer den kompletten Source Code der Zeile. Das würde mit ele.html() nicht funktionieren.

1 Kommentar:

  1. Eine gute Lösung, die aber auch am Ende viel Disziplin abverlangt - das sollte man nicht vergessen.

    Die Sache mit dem unbind() zur rechten Zeit ist im IE in der Tat ein nicht so schönes Problem.

    AntwortenLöschen