Calling jQuery's Data() Method Without Arguments

With jQuery 1.4, I was very happy to see that they finally gave us the ability to invoke the data() method without arguments. Calling the data() method in this way returns an object containing all of the keys stored using the data() method on this object. When I was working on my jQuery image zoom experiment, I made heavy use of this feature to store data points without having to call the data() method each end every time. I was able to do this because the object returned by the data() method is the actual data cache object - passed by reference - and not a copy of it. As such, once you have the data cache, direct changes to it will be reflected in all subsequent calls to the data() method on that object.

Of course, it never is quite that straightforward. jQuery's data() method only returns the object's data cache if the cache exists; and, the cache only exists if a value is currently being stored in it. If no keys have been stored in the cache, or all the keys have been deleted (using removeData()), then the data() method will return null, not an empty cache object. I assume that they are doing this for optimization purposes, but there's something I don't like about it; it makes the data() method's behavior dependent on the previous uses of data() and this difference in data() potential just seems dangerous.

To make the data() method's behavior uniform, we can override the original data() method to apply some intercepting business logic. In the following demo, you'll see that I wrap the original data method using my own data method (is that AOP - Aspect Oriented Programming??). My version of the data method then applies the call to the original data method and, if the data method returns a null cache object, I store a data point in the elements data cache before returning the data cache. While this definitely feels sloppy in and of itself, storing this bunk name/value pair forces the data cache to become sticky.

<!DOCTYPE HTML>

<html>

<head>

<title>Calling jQuery's Data() Method Without Arguments</title>

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

<script type="text/javascript">

// Here, we want to override the data() method so that

// non-argument calls to it will always return the data

// store object.

(function( $ ){

// Get a reference to the original data method.

var originalData = jQuery.fn.data;

// Override the data method to ensure that the data

// store is always returned.

jQuery.fn.data = function(){

// Check to see if there are any arguments passed

// in. This is the only case we care about.

if (!arguments.length){

// Get the storage object.

var dataStore = originalData.apply( this );

// Check to see if the dataStore exists. If

// it does, then just return it; otherwise,

// we'll have to create one.

if (dataStore){

// Return current store.

return( dataStore );

}

// If we made it this far, then the data store

// returned was null which means it has never

// be accessed before. To create it, we

// actually have to set a key in it. We also

// have to keep the key in there, otherwise

// jQuery will try to delete the data store.

originalData.apply( this, [ "_", "_" ] );

// Now that the dataStore object has been set

// as the hash, return it.

return( originalData.apply( this ) );

} else {

// No special case, simply pass method call

// to original method and return the result.

return(

originalData.apply( this, arguments )

);

}

};

})( jQuery );

// -------------------------------------------------- //

// -------------------------------------------------- //

// When the DOM is ready, init the scripts.

jQuery(function( $ ){

// Get a reference to the P tag.

var para = $( "p:first" );

// Get a reference to the data storage struct.

// When you call the data() method without any

// arguments, jQuery returns the actual storage

// object, not a duplicate of it.

dataStore = para.data();

// Now that we have the data storage object, we

// can update the values in it directly.

dataStore.name = "Tricia";

dataStore.isSexy = true;

// Now that we have updated the store object, let's

// update the text of the paragraph by re-referencing

// the data storage mechanism.

//

// NOTE: We did NOT have to re-store the data object

// in order to make these values available.

para.text(

para.data( "name" ) +

" is " +

(para.data( "isSexy" ) ? "" : " not " ) +

"sexy!"

);

});

</script>

</head>

<body>

<h1>

Calling jQuery's Data() Method Without Arguments

</h1>

<p>

<!-- Will be populated dynamically. -->

</p>

</body>

</html>

As you can see, the first part of this script overrides jQuery's native data() method to intercept and manipulate calls to the data() method made without any arguments. If the data() method returns null, I store the key, "_", in the element's data cache before re-querying the data() method to return a valid data cache object reference. In doing so, the rest of the demo can successfully retreive the data cache using the data() object regardless of the current state of the cache. In doing so, I am able to directly mutate the data cache without making unnecessary calls to the data() method. And, since this data cache is returned by reference, my data alterations are reflected in subsequent calls to the data() method.

When we run the above code, we get the following paragraph output:

Tricia is sexy!

As you can see, the direct data manipulation we performed on the data store object was successfully reflected in later calls to the data() method.

jQuery 1.4 finally gave us the ability to directly access the data cache being used by the data() method. The behavior of this method, however, depends tightly on the current state of the cache. To me, this difference in data() behavior feels unnecessary; and, while the above solution is not perfect by any means, I like that it simplifies the way in which the data() method can be used.