Archive for the ‘html5’ 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.