Archive for the ‘javascript’ Category

Javascript: Klickst du schneller

Ganz vergessen vorm Urlaub zu veröffentlichen:

Jaja…immer noch die onclick – Geschichte. Dies wird dann aber der letzte Post dazu, versprochen. Ich hab da aber nach langem Rumdiskutieren und Recherchieren mal ein wenig getestet und will euch die Ergebnisse nicht vorenthalten, falls ihr mal vor dem selben Problem steht.

Die Sache war nämlich die, dass mein CTO und ich nicht auf einen Nenner gekommen sind, ob wir trotz der großartigen Erkenntnis mit dem data-attr nicht doch onclick benutzen sollen. Argumente für und gegen Beides gab es ausreichend. Es mußten also Fakten her und die sollten sich daraus generieren, was nun beim initialen Laden letztendlich schneller ist.

Um dies rauszufinden bin ich zunächst hergegangen und habe eine Seite gebaut, die mir 1000 links erzeugt hat und mir einen Timer je ans Anfang und Ende des Body-Elements gesetzt:

<body>
  <script>console.time('timerName');</script>
  <?php for(i=0, i<2000, i++) { ?>
    <a href="/">dumdidum</a>
  <?php } ?>
  <script type="text/javascript" src="meinescripte.min.js"></script>
  <script>console.timeEnd('timerName');</script>
</body>

Die Seite habe ich jetzt 30 mal geladen und die Zeit gemessen um einen Referenzwert zu bekommen.
Um den Durchschnitt zu bilden habe ich von den gesammelten Zeiten je die schnellste und langsamste Zeit rausgestrichen, also folgende Rechnung durchgeführt:

Durchschnitt = ∑(time(1…29))/28 (->ich bin nicht so gut in Formeln aufstellen wie man merkt, aber ich denke, ihr wißt, was ich meine)

Dann habe ich meinen Link mit einem onclick bestückt:

<body>
  <script>console.time('timerName');</script>
  <?php for(i=0, i<2000, i++) { ?>
    <a href="/" onclick="myfunction({'id':'<?php echo $i ?>'})">dumdidum</a>
  <?php } ?>
  <script type="text/javascript" src="meinescripte.min.js"></script>
  <script>console.timeEnd('timerName');</script>
</body>

Selbiges noch einmal in der bind-click-unobstrusive-data-attr Variante:

<body>
  <script>console.time('timerName');</script>
  <div id="parentid">
    <?php for(i=0, i<2000, i++) { ?>
      <a href="/" class="my-link" data-obj='{"id":"<?php echo $i; ?>"}'>dumdidum</a>
    <?php } ?>
  </div>
  <script type="text/javascript" src="meinescripte.min.js"></script>

  <script>
    jQuery(document).ready( function() {
      jQuery('#parentid .my-link').live('click', function(){
        return false;
      });
    });
    console.timeEnd('timerName');
  </script>
</body>

Nach wiederum je 30 Reloads und obiger Rechnung ergaben sich zum allgemeinen Erstaunen folgende Ergebnisse:

Ohne Klick: 1577,50 Sekunden
Late Binding: 1726,03 Sekunden
onclick: 1885,07 Sekunden

Irgendwie hatte ich nicht damit gerechnet, dass das late-binding wirklich schneller ist, als das gute alte onclick. Warum das so ist, kann ich mir auch nicht ganz erklären. Ich schätze mal, dass es daran liegen könnte, dass bei jedem onclick ein Event-Objekt instanziiert wird und das wohl seine Zeit dauert, wenn das bei jedem onclick extra gemacht wird, als eben gesammelt nachher. Aber wie gesagt, im Detail hab ich keinen Plan.

Ich hoffe mal, dass das Ganze jetzt nicht zu sehr nach Milchmädchenrechnung aussieht, denn natürlich könnte man meinen, dass nach 30 Reloads und bei der Art der Messung wahrhaftig keine validen Ergebnisse zustande kommen. Aber meiner Ansicht nach, ist eine klare Tendenz zu erkennen, dass das late-binding etwas performanter abläuft.

JS: onclick war die Frage, HTML5 die Antwort?

Gestern hatte ich ja die Frage aufgeworfen, ob das gute alte onclick eigentlich noch verwendet werden soll und/oder ob man auf Biegen und Brechen auf unobstrusive bestehen sollte. Mein größtes Problem bei der ganzen unobstrusive Geschichte war dabei, dass ich mir Information wie, an welche Action und mit welchen Parametern, irgendwo im HTML merken muss, um das ganze möglichst generisch zu behandeln.

Nach einigen Diskussionen und Recherche ist mir eine Lösung untergekommen, die ich zu diesem Zeitpunkt zwar warscheinlich nicht implementieren werde, ich aber trotzdem irgendwie großartig finde (zugegeben, ich habe mich mit HTML 5 bisher noch nicht sonderlich auseinandergesetzt, weil “Never trust a draft”). Deswegen hier dann mal der Ansatz für alle, die wie ich vielleicht noch ein wenig hinterher sind:

1. Man definiere ein Element und füge die in HTML5 vorgesehen data-attribute hinzu:

  <ul>
    <li class="listeelement" data-action="login" data-id="1" data-params="a=b">
      <div>contentzeug</div>
    </li>
  </ul>

2. Man hole sich die gesetzten data-attribute im Javascript
2.1 Einmal für die doofen Browser statisch:

jQuery('.listeelement').live( "click", function() {
  var action = this.getAttribute('data-action');
  var id = this.getAttribute('data-id');
  var params = this.getAttribute('data-params');
}

Statisch deswegen, weil ich hier beim im Javascript auch immer genau wissen muss, welche data-attributes gesetzt sind

2.2 Für die doofen Browser mit jQuery-Plugin und nicht ganz so statisch, da es eine Methode gibt, die alle data-attribute holt

jQuery('.listeelement').live( "click", function() {
    var mydataset =  jQuery(this).dataset();
    var myaction = mydataset.action;
    delete mydataset.action //damit die action nicht nochmal ans GET/POST als Param dran gehangen wird
    doSendRequest(myaction, mydataset);
});

2.3 Oder ganz einfach wie in der HTML5-Spec beschrieben, was im Endeffekt wohl dasselbe ist, wie 2.2. Da ich das Plugin nicht ausprobiert habe, kann ich derweil nicht sagen, was es mit den data-attributen in den doofen Browsern macht. Gilt noch zu recherchieren!

  jQuery('.listeelement').live( "click", function() {
    var mydataset =  this.dataset;
    var myaction = mydataset.action;
    delete mydataset.action //damit die action nicht nochmal ans GET/POST als Param dran gehangen wird
    doSendRequest(myaction, mydataset);
  });

3. Man schicke den ganzen Kram an die Action. Aber ACHTUNG, nicht funktionierend mit der Variante 1, da müßt ich dann nochmal bißchen Hirnschmalz für opfern, um das praktikable zu gestalten:

  function doSendRequest(pAction, pParams) {
      jQuery.ajax({
        type: "GET",
        url: pAction,
        data: pParams,
        success: doSomething
      });
  }

Na? Was meint Ihr? Ich finds insgesamt ner sehr gelungend Lösung und bin nahezu begeistert, mal abgesehn von der statischen Variante für die doofen Browser, aber sicherlich ließe sich auch hier noch was geeignetes finden.

Mein herzlicher Dank geht an den Input von der großartigen Community von Stackoverflow. Klickt ihr hier und hier um die entsprechenden Diskussionen anzuschauen.

JS: onclick oder nicht, das ist hier die Frage

Ich versuche mittlerweile Javascript immer möglichst generisch zu schreiben um Redundanzen zu vermeiden und zusätzlich möchte ich am liebsten auch kein Inline-Javascript verwenden. Diese hat neben den best-practice-unobstrusive-… auch Performance-Gründe, denn bei jedem sich öffnenden Script-Tag im Code wartet die Seite beim Laden bis dieser ausgeführt ist und wird dann erst weiter gerendert.

Jetzt stehe ich gerade vor dem Problem, dass ich eine Liste von Elementen habe, die bei einem Klick alle das gleiche machen sollen, nämlich einen Request, der HTML zurückliefert, womit ein anderer Bereich der Seite aktualisiert wird:

  <ul>
    <li class="listeelement" id="load-content-id-1"><div>contentzeug</div></li>
    <li class="listeelement" id="load-content-id-2"><div>contentzeug</div></li>
    <li class="listeelement" id="load-content-id-3"><div>contentzeug</div></li>
    <li class="listeelement" id="load-content-id-4"><div>contentzeug</div></li>
  </ul>

Im ersten Moment würde ich jetzt den Klick wie folgt an das .listelement hängen und den Request starten (so auch meine bisherige Vorgehensweise):

function sendRequest() {
  jQuery('.listeelement').live( "click", function() {
    1. Fummel die Id des nachzuladenden Contents aus der css-id raus
    2. Sende den Request an die Action
  });
}

Irgendwie nervt mich hierbei aber ganz gewaltig Punkt 1 bei der Verarbeitung des Klicks. Denn man muss etwaige Parameter erstens immer irgendwie im Markup ‘verstecken’ und zweitens es dann wieder rausfummeln um es an die Action zu schicken. Schön ist anders und unter dem Performance-Aspekt kann das ja auch nicht der Stein des Weisen sein.

Am einfachsten und mir am liebsten wäre meiner Ansicht nach einfach folgendes:

  <ul>
    <li onclick="sendRequest({'id' : '1' })"><div>contentzeug</div></li>
    <li onclick="sendRequest({'id' : '2' })"><div>contentzeug</div></li>
    <li onclick="sendRequest({'id' : '3' })"><div>contentzeug</div></li>
    <li onclick="sendRequest({'id' : '4' })"><div>contentzeug</div></li>
  </ul>

Woraus sich folgendes Script ergibt:

function sendRequest(pParams) {
      jQuery.ajax({
        type: "GET",
        url: action,
        dataType: "json",
        data: pParams,
        success: doSomething
      });
}

Die Verarbeitung ist, meiner Ansicht nach, damit definitv viel einfacher und auch schneller, würd ich mal meinen.
Ich bin aber etwas unsicher, ob das jetzt ne gute oder schlechte Idee ist diesen Weg zu gehn, da zumal auch nicht genau weiß, ob das onclick überhaupt noch verwendet werden soll.

Na vielleicht hat da ja irgendjemand ne Meinung dazu. Würde mich freuen und auch wirklich weiterhelfen.

JS: Simple Error Handling

Error-Handling für Javascript ist derzeit bei mir und meiner Umgebung ein viel diskutiertes Thema. Die Frage ist dabei: Wie fängt man die Fehler ab und loggt sie mit und das alles am besten noch so, dass die Performance nicht darunter leidet.

Der Königinnenweg ist mir dabei noch nicht ganz gekommen. Aber zwei Dinge sollte man von vorneherein schonmal machen, um Fehler einzugrenzen, abzufangen, wegzutun:

1. Alle Javascriptfehler global unterdrücken:

window.onerror=function(msg, url, linenumber){
   //request starten um fehler wegzuloggen
   return true;
}

Dies bewirkt, dass der User auch noch weiter klicken kann, selbst wenn irgendwo ein Semikolon im Js-Code gefehlt hat. Die drei Parameter msg, url und linenumber geben dabei genauer Auskunft darüber, was genau wo passiert ist.
Diese Info könnte man jetzt bspw. über einen xh-request irgendwo an den Server schicken und wegloggen.

2. Ajax-Request-Fehler abfangen

  onreadystatechange = myonreadystatecallback

In der myonreadystatecallback kann man dann die verschiedenen Stati des Requests reagieren und gegebenenfalls die Response-Fehler auswerten und wegloggen. Das ganze kann dann so aussehn:

  xhr.onreadystatechange = function() {

    if(xhr.readyState == 4 //4 = request fertig) {
      if(xhr.status != 200) {
        //request starten um fehler wegzuloggen
      }
    }
  }

Da der Post ja Simple Error Handling heißt, soll es das auch erstmal gewesen sein. In großen Projekten ist es natürlich sinnvoll für beide Lösungen eine globale Error-Klasse zu schreiben, die sich um die Behandlung kümmert.
Sowas behandlen wir dann vielleicht besser in einem Advanced Error Handling Post :) . Denn das Thema kann man bis zur Erschöpfung diskutieren und behandeln.

Insgesamt ist es aber so, dass man mit diesen beiden kleinen Lösungen schonmal jede Menge schaffen kann.
Kurzzeitig hab ich mal gedacht, dass es auch ne gute Idee wäre, um alles im Javascript ein try….catch drumzupacken und im catch dann den Fehler wegzuloggen. Mir wurde dann aber recht schnell klar (gemacht), dass dies sehr zur lasten der Performance geht und vor allem versucht wird die Fehler des Entwicklers abzufangen. Und der soll ja eigentlich gar nicht erst welche machen (jslint will hurt your feelings? joa…aber helfen tuts scho…)