Context in live events in jQuery 1.4

Performance tests continued

In my previous post I measured how the performance of a web application is impacted by jQuery live events. The conclusion was that live events will normally not have a big performance impact, but in complex applications with lots of events it can be a problem. An important factor that I didn’t cover very well is the level of nesting in the dom-tree. I have created a test case that demonstrates better how the performance impact changes with a more nested dom-tree.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <title>jQuery 1.4a2 nested live tester</title>
  </head>

  <body>
    <div id="result1">waiting for results...</div>
    <div id="result2">waiting for results...</div>
    <div id="result3">waiting for results...</div>
    <div id="result4">waiting for results...</div>
    <div id="result5">waiting for results...</div>
    <div id="result6">waiting for results...</div>
    <div id="result7">waiting for results...</div>

    <script type="text/javascript" src="jquery-1.4a2.min.js"></script>
    <script type="text/javascript">

    /*<![CDATA[*/

      jQuery(document).ready(function(){

        function measureClick(target, output, text) {
          target = jQuery(target);
          var count = 1;
          var start = new Date();
          for(var i=0; i<count; i++) {
            target.click();
          }
          var stop = new Date();
          jQuery(output).html(text + ((stop.getTime() - start.getTime())/count) + 'ms');
        }

        function addEvents(count, selector) {
          for(var i=0; i<count; i++) {
            jQuery(selector + i).live('click', function() {});
          }
        }

        //Add nested DOM-tree
        var element = jQuery('body');
        for(var i=1; i<=30; i++) {
          element = jQuery('<div id="level'+i+'">level'+i+'</div>').appendTo(element);
        }

        addEvents(100, '.garbage');

        measureClick('#level1', '#result1', 'click at level 1: ');

        measureClick('#level5', '#result2', 'click at level 5: ');

        measureClick('#level10', '#result3', 'click at level 10: ');

        measureClick('#level15', '#result4', 'click at level 15: ');

        measureClick('#level20', '#result5', 'click at level 20: ');

        measureClick('#level25', '#result6', 'click at level 25: ');

        measureClick('#level30', '#result7', 'click at level 30: ');
      });

    /*]]>*/

    </script>

  </body>

</html>

In this test we have a dom-tree with 30 nested div’s and we register 100 live events. We then trigger a click event on different levels of the tree and measure the time required to process it. On my macbook I get the following result:

click at level 1: 24ms
click at level 5: 54ms
click at level 10: 91ms
click at level 15: 130ms
click at level 20: 174ms
click at level 25: 210ms
click at level 30: 253ms

We see that the performance impact of the live events is greater on clicks that are made deep down a nested dom-tree.

Can the context help?

In my previous post on performance in jquery live events I proposed that it would be a good improvement to support a scope for the live events, so that the performance impact can be limited to a specific part in the dom-tree. One way to implement this  could be through the context parameter in the core jQuery() object constructor.  When live events were introduced they did not support the use of a context parameter, but the coming jQuery 1.4 release is supposed to support it. More details about this can be found at learningjquery.com. I was curious to se how this context was being used and if it would allow me to limit the impact of the live events to a specific part of dom-tree. To investigate this I modified the above test so that a context is provided when registering the live events. Note that in the previous test as well as this one i use the latest jQuery 1.4 alpha 2 release.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <title>jQuery 1.4a2 nested live tester with context</title>
  </head>

  <body>
    <div id="level">current level...</div>
    <div id="result1">waiting for results...</div>
    <div id="result2">waiting for results...</div>
    <div id="result3">waiting for results...</div>
    <div id="result4">waiting for results...</div>
    <div id="result5">waiting for results...</div>
    <div id="result6">waiting for results...</div>
    <div id="result7">waiting for results...</div>

    <script type="text/javascript" src="jquery-1.4a2.min.js"></script>
    <script type="text/javascript">

    /*<![CDATA[*/

      jQuery(document).ready(function(){

        function measureClick(target, output, text) {
          target = jQuery(target);
          var count = 1;
          var start = new Date();
          for(var i=0; i<count; i++) {
            target.click();
          }
          var stop = new Date();
          jQuery(output).html(text + ((stop.getTime() - start.getTime())/count) + 'ms');
        }

        function addEvents(count, selector) {
          for(var i=0; i<count; i++) {
            jQuery(selector + i, jQuery('.context')).live('click', function() {});
          }
        }

        //Add nested DOM-tree
        var element = jQuery('body');
        for(var i=1; i<=30; i++) {
          element = jQuery('<div id="level'+i+'">level'+i+'</div>').appendTo(element);
        }

        var level = '20';
        jQuery('#level').html('context set at level ' + level);
        jQuery('#level' + level).addClass('context');

        addEvents(100, '.garbage');

        measureClick('#level1', '#result1', 'click at level 1: ');

        measureClick('#level5', '#result2', 'click at level 5: ');

        measureClick('#level10', '#result3', 'click at level 10: ');

        measureClick('#level15', '#result4', 'click at level 15: ');

        measureClick('#level20', '#result5', 'click at level 20: ');

        measureClick('#level25', '#result6', 'click at level 25: ');

        measureClick('#level30', '#result7', 'click at level 30: ');
      });

    /*]]>*/

    </script>

  </body>

</html>

In this test we use one of the nested divs as context. I was hoping that jQuery takes advantage of this context so that we don’t get any performance impact on events triggered above the context element, only events on elements inside the context should get the performance impact. However when I run the tests I get the following result:

context set at level 20
click at level 1: 30ms
click at level 5: 66ms
click at level 10: 113ms
click at level 15: 160ms
click at level 20: 207ms
click at level 25: 255ms
click at level 30: 305ms

We see that the resulting figures are similar to the results where no context was used. The live events even has slightly bigger performance impact now. Events on all elements in the dom-tree gets the performance impact regardless of which context we specify. So the context did not help us here. This is probably due to that jQuery attaches the delegating event on the document instead of the context element. We can verify that jQuery puts the events on the document element with a simple check in firebug. By opening firebug, reloading the test, and then invoking the following command in the console

 jQuery(document).data('events').live

We see that the live events are stored on the document and not on the context element. In my view it would be bad if this behavior is kept in the final release of jQuery 1.4 unless the context is still unsupported like in 1.3. Once the functionality is released it can not be changed without breaking some applications that rely on the current behavior. I guess the idea could be that live events should be kept even if you remove the context element and that you should be able to load the context element dynamically. But I think that it would be more valuable to be able to limit the performance impact by putting the delegating event on the context. It would also provide an easy way to control the lifetime of live events by simply removing the context element.

Tags: , , ,

Leave a comment