5

This code best demonstrates my confusion.

var nativeObj, jWrapped, jSelector;

//WIAT = "What I Am Thinking"
nativeObj = $( '#tableTab' ) [0];  //WIAT: unwrap the jQuery object created by the selector and get the native DOM object
jWrapped = $( nativeObj );  //WIAT: wrap up the native DOM object again... should be equal to $( '#tableTab' )
jSelector = $( '#tableTab' );   //WIAT: pass the jQuery object as reference to jSelector variable

// set the data with jQuery's .data method
$.data( jWrapped, 'key', { test: 12 } );    //WIAT: will be equivalant to using $( '#tableTab' ) and should attach the data to it
$.data( $( '#tableTab' ) [0], 'key', { test: 34 } );    //WIAT: using the native DOM obj, it shouldn't work with this, since it doesn't specify in the docs
$.data( $( '#tableTab' ) , 'key', { test: 56 } );   //WIAT: should rewrite the data in the element to { key: { test: 56} }

console.log( $.data ( jWrapped ) ); // {key:{test:12}}
console.log( $.data ( jWrapped[0] ) );  // {key:{test:34}}
console.log( $.data ( nativeObj ) );    // {key:{test:34}}
console.log( $.data ( $( nativeObj ), 'test' ) );  // undefined  
console.log( $.data ( $( '#tableTab' ) [0] ) );  // {key:{test:34}}
console.log( $.data ( $( '#tableTab' ) , 'test' ) ); // undefined

Whoa, wait, what's going on?

1.Why am I getting different results? I only used 1 selector and am referencing one element.

2.Why aren't the object reference jWrapped and the object from $( '#tableTab' ) producing the same result?

3.Furthermore jWrapped and jWrapped[0] are producing different results? The former being a jQuery wrapped object and the latter a native DOM object. Essentially they are referencing the same element with a different result!??

//Now let's see what's inside the objects
console.log( $( '#tableTab' ) [0]);  // [object HTMLDivElement]         
console.log( nativeObj );  // [object HTMLDivElement]
console.log( $( nativeObj ) );  // {0:({}), context:({}), length:1}
console.log( jWrapped );   // {0:({}), context:({}), length:1, jQuery182021025872972076787:{toJSON:(function () {}), data:{key:{test:12}}}}
console.log( $( '#tableTab' ) );    // {length:1, 0:({}), context:({}), selector:"#tableTab"}
console.log( jSelector );   // {length:1, 0:({}), context:({}), selector:"#tableTab"}

Good nativeObj == $( '#tableTab' ) [0] that's what I expected

Whoa, that was weird, why doesn't jWrapped == $( nativeObj ) ?

Good, jSelector = $( '#tableTab' ) that's also what I expected

Given this data, I would extrapolate that $.data must accept a native DOM element, however

$( '#tableTab' ).data( 'key' , { test: 78 } );
console.log($( '#tableTab' ).data('key')); // 78

Umm excuse me monsieur console... not cool man.

Ok I am royally confused and frustrated and I hate jQuery and I hate Javascript and I hate IE... Ok no, I just hate IE, but that's another story. Why is jQuery behaving so strange? Hanging out with IE too much I think...

My guess is that it has to do with the way $.data works in jQuery and that it does not actually attach data to elements, but rather stores the data in its own object and references the data based on parsing the data passed that is passed it. Did I find a bug?

Help.

Also I did take a look at How does jQuery .data() work? and while it did provide some good information, it still doesn't answer what's happening here, which is my real question. Though it does confirm my idea that no data is stored in the elements, but in a jQuery object.

Community
  • 1
  • 1
Klik
  • 1,757
  • 1
  • 21
  • 38

1 Answers1

3

Looking at the source in chrome developer tools, I found this comment around line 1564, in version 1.9 (GitHub source here, line 17 within function internalData)

// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically

So, whats happening here, is that when you pass in nativeObj, it will store the data in $.cache, but otherwise it will store the value onto the jQuery object you pass in.

take a look at this fiddle: http://jsfiddle.net/tomprogramming/SNqwh/

I made a few changes to your original example. The first is that I pass in the objects you set up at the top. The second is that you query the objects for a piece of data called "test", but that won't ever be there - you are storing an object that happens to have a property called test in it - under the property of "key".

At the end of your log statements, I added my own, using the object-oriented nature of $.data instead. Each time, I get the same result back. My guess here is that it uses the underlying dom node of each jQuery object to access the global cache, which in your case, has the value of {test:34}.

I do agree that this is unexpected behavior, as it would appear to the novice user that you are selecting the same element, but I believe this is just an underscore of the difference between $.data and $(selector).data(). The latter always uses the underlying dom node (and thus always be 'correct'), whereas the former will use the jQuery object you pass in, if available.

EDIT: this fiddle again underscores the difference. I set the value using $(selector).data(), and then pull it out again with $.data. The 'cache' used for the original objects hasn't changed (being the objects themselves), but the global cache has, for the underlying DOM node.

Lesson here: Always use a DOM node, or $().data. That is the "most consistent"

Thomas Jones
  • 4,892
  • 26
  • 34
  • +1 for looking in the comments in GitHub. That does explain why I can get different results with a 'seemingly' similar jQuery reference. However, why is it that `jWrapped` and `$('#tableTab')` behave differently? Both are jQuery objects correct? but I cannot get any data if I use the format `$.data($('#tableTab'))` which seems weird to me b/c jQuery usually accepts selecters in place of elements. Might it be b/c `jWrapped` is an already initialized object, whereas `$('#tableTab')` isn't? Seems counter-intuitive b/c `$('#tableTab).data()` works. Also would you elaborate on your, 'lesson here'. – Klik Feb 10 '13 at 07:14
  • That last fiddle did enlighten me to something. Where you wrote `jSelector.data('key', {test: 57});` and `jWrapped.data('key', {test: 116});` Both jSelector and jWrapped are jQuery objects and that each will have its own set of data. That just about solves this mystery, except, why doesn't $.data($('#tableTab')) work? Moreover, I assumed $() returns a jQuery object, so it would be the same as passing a jQuery object. – Klik Feb 10 '13 at 07:26
  • Trying to follow the code path, it looks like the two methods dont even follow the same code path. One goes directly into `internalData`, where the other one jumps around a bit inside a jQuery plugin. This is definitely odd behavior for jQuery. To answer the second question, I can only answer with: not all jQuery objects are created equal: http://jsfiddle.net/tomprogramming/NqR2V/. While the underlying node is the same, the objects themselves are different. – Thomas Jones Feb 10 '13 at 21:57
  • 1
    I can understand that jQuery objects are different. They are different instances containing their own individual data. But what I don't understand in the end is... actually I just figured it out while writing this. Ah... `$.data($('#tableTab')` won't work because while **$() DOES return a jQuery object**, it's creating a **NEW** object that obviously won't have any data in it already and is thus returning undefined since there's no data in the object. Thanks for the information. Wish I could upvote again :). – Klik Feb 11 '13 at 00:02