views:

84

answers:

3

Hi folks,

I recently needed to append some data to dynamically created LI elements. In my first instance, I used .data() in a way like

var _newli = $('<li>foobar</li>');
_newli.data('base', 'ball');
// append _newli to an `ul`

that.. was terribly slow. This logic happens in a loop which can easily grow up to 500+ items, it took ages! Sometimes it even broke the javascript execution time frame.

So I changed to $.data(). Somehow, attaching data to an object with that is like 8x faster than doing it over the .data() method call. So now it looked like

var _newli = $('<li>foobar</li>');
$.data(_newli[0], 'base', 'ball');
// append _newli to an `ul`

That was/is indeed faster, but still it took like 3-4 seconds(!) to build up all my elements (In my real code there are like 6 calls to $.data per element).

So I was really stuck with that, I asked myself why the heck to use .data() or $.data() anyway? I could just attach my data to the DOM object. So I did

var _newli = $('<li>foobar</li>');
_newli[0].base = 'ball';
// append _newli to an `ul`

Voila, wow to my shock, that was incredibly fast! I couldn't believe that this ran so good without any disadvantage. So that is what is my question all about actually. I didn't find any disadvantage for this technique so far on the net. There are reads about circular references you can create using this way, but AFAIK "only" on IE's and only if you refer to objects.

Any thoughts experts ?

update

Thanks for the good comments and postings guys. Short update @patrick dw:

You are right, I was passing the underlaying DOM element when using $.data(). It does not even work with jQuery objects, at least not as expected. The idea about using one object and pass it through $.date() I had myself, but then again I was so shocked about the performance difference I decided just to ignore the .data() method like forever.

A: 

These might help:

And read the following on using a custom DTD:

In short, for the most part I don't think you'll run into any problems using custom attributes. Most sensible/current browsers will fine with it. I will say, I did run into problems with a webapp I developed for MobileSafari, which forced me to resort to using $.data hidden elements. Fortunately, I did not have 500+ elements, but more like five or six.

karim79
@karim: I guess we're talking about two different storys. If you attach some data to `element.somedata = {foo: 5};` there is no `attribute` created for that. It's like mixing `ECMAland` and `DOMland` somehow, since an `DOM element` is treated like an object in ECMAscript.
jAndy
+2  A: 

You are correct about circular references, that isn't an issue outside of IE, and in IE it only becomes an issue when JavaScript has a reference to a DOM object, and a JS object is assigned to one of the DOM object's properties. I believe this can be resolved by simply by nullifying any references in JS to the DOM object.

The $().data() method is an overly complicated wrapper for $.data() (see jQuery.fn.data: http://github.com/jquery/jquery/blob/master/src/data.js#L126, which in turn calls jQuery.data: http://github.com/jquery/jquery/blob/master/src/data.js#L20), so cutting out that middle man will save a non trivial amount of time, especially if it's to be done 500 times.

In this case, the $().data('foo', 'bar') method doesn't do much more than el.foo = 'bar'. Do what's fastest.

Ryan Tenney
A: 

Not only is using DOM object properties faster, it is also more concise and natural looking

$(element).data('omg', 'wtf');

vs

element.omg = 'wtf';

Why anyone would use a "helper" function for something so fundamentally simple is beyond me. CIRCULAR REFERENCES!? Oh noes! On the off chance that it happens and actually has a non-negligible impact on your program, then worry about it. Until then, write code that does not look ridiculous.

How might one react coming across code like this I wonder:

function $$$addKeyValuePairToObject(object, key, value) {

  //Just in case IE doesn't automatically stringify the key
  //It probably does but you never know! CROSS BROWSER SMASH
  if (typeof key !== 'string') {

      //I could just write `key = ''+key;` I guess but I'm 
      //sure this way is best practice somehow 
      if ('toString' in key && typeof key.toString === 'function') {
        key = key.toString();
      } else if (typeof key === 'undefined') {
        key = 'undefined';
      } else if (key === null) {
        key = 'null';
      } else {
        key = 'wtf?';
      }
   }

   //Wait...could this "object" be a DOM Node!?
   if ('ownerDocument' in object && object.ownerDocument === document) {
     //It is! BEST PRACTICE ATTACK

     alert("CONSIDERED HARMFUL CONSIDERED HARMFUL. Adding "+ 
           "ANYTHING to a DOM node IS CONSIDERED HARMFUL. " +
           "Don't do it, don't think about it, don't ask why, " +
           "and go repeat what I'm telling you on Stackoverflow." +
           "In a just world, every object in JS would be as locked " +
           "down as a geriatric Java programmer's most cherished " +
           " private static final method");

     //Now that that is out of the way, create a new array like object
     //with the requested key value pair and push the sacred DOM node 
     //onto it. This is elegant.
     var $$$CrossBrowserDOMWrapperClass = function() {};
     $$$CrossBrowserDOMWrapperClass.prototype = new Array();

     var $$$crossBrowserDOMWrapperObject = new $$$nesecaryCrossBrowserDOMWrapperClass();
     $$$crossBrowserDOMWrapperObject.push(object);

     object = $$$crossBrowserDOMWrapperObject;
   }

   //And just to drive the point home...
   if (key === 'dynamic' || key === 'with' || key === 'eval' || key === 'goto' ||
       key === 'continue' || key === 'break' || key === 'global' || key === '++' ||
       key === '--' || key === '==' || key === '!=') {

       //Dargh, unleash the raptors!
       window.location.href = 'http://www.jslint.com/';
   }

   //Check for IE, just for the sake of it...
   if (typeof document.body.currentStyle !== 'undefined') {
     1+2+3+4+5+6+7+8+9;
     //...That felt good
   }


   //TODO: this line does not seem to have enough $ signs, should work on this
   object[key] = value;

   return object;

}

That's pretty much exactly what the code for $.data looks like.

MooGoo