Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (Munich) with:

jQuery Data() Method Associates Data With DOM Elements - SWEET ASS SWEET!

By Ben Nadel on

I'm sorry that I cannot remember who posted this link, but yesterday on Twitter, someone posted a link to an article titled, "5 Tips for Better jQuery Code." It was a short article with HUGE bomb shell: the jQuery Data() method. I had never seen this jQuery method before, but apparently, it allows you to associate any type of data with a DOM element. This data can, of course, be referenced later using the same keys.

 
 
 
 
 
 
 
 
 
 

When I saw this, it totally blew my mind! I now think back on all the times that I have completely misused REL or ID or TITLE attributes to store critical pieces of data (that were not really REL, ID, or TITLE data respectively). It makes me feel so dirty; there's been this jQuery Data() method sitting there the whole time, doing exactly what I need it to do, and I've just been disrespecting the DOM left and right to achieve my own ends.

As shown in the demonstration above, I set up a small test page to explore the jQuery Data() method. In it, I am basically setting up several links, associating data with them, and then alerting that associated data when the respective link is clicked:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>jQuery Data() Method</title>
  • <!-- Linked files. -->
  • <script type="text/javascript" src="jquery-1.2.6.min.js"></script>
  • <script type="text/javascript">
  •  
  • // Init page upon DOM load.
  • $(
  • function(){
  • var jLink1 = AddLink( "Kit", 4, "Cute" );
  • var jLink2 = AddLink( "Michelle", 43, "Fun", jLink1 );
  • var jLink3 = AddLink( "Sarah", 3943, "Stubby", jLink2 );
  • var jLink4 = AddLink( "Lori", 23, "Sassy", jLink3 );
  • var jLink5 = AddLink( "Niki", 9, "Angry", jLink4 );
  • }
  • );
  •  
  •  
  • // Add link to DOM.
  • function AddLink( strName, intID, strProperty, jPrevLink ){
  • var jParent = $( "ul:first" );
  • var jListItem = $( "<li></li>" );
  • var jLink = $( "<a>" + strName + "</a>" );
  •  
  • // Associate data with this link.
  • jLink.data(
  • "Data",
  • {
  • Name: strName,
  • ID: intID,
  • Property: strProperty,
  • Prev: jPrevLink
  • }
  • );
  •  
  • // Set link properties.
  • jLink
  • .attr( "href", "javascript:void(0)" )
  • .click( ClickHandler )
  • ;
  •  
  • // Add the link to the list item and then add the
  • // list item to the DOM.
  • jParent.append(
  • jListItem.append( jLink )
  • );
  •  
  • // Return new link.
  • return( jLink );
  • }
  •  
  •  
  • // Handles the link clicks.
  • function ClickHandler( objEvent ){
  • var jThis = $( this );
  •  
  • // Get the property data for this element that was
  • // associated when createing the element.
  • var objData = jThis.data( "Data" );
  •  
  • // Alert data.
  • alert(
  • objData.Name +
  • " is " +
  • objData.Property +
  • (
  • objData.Prev ?
  • "\n\nPrevious Girl: " +
  • objData.Prev.data( "Data" ).Name
  • :
  • ""
  • )
  • );
  •  
  • // Prevent default.
  • objEvent.preventDefault();
  • return( false );
  • }
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery Data() Method
  • </h1>
  •  
  • <ul>
  • <!-- Data to be added here dynamically. -->
  • </ul>
  •  
  • </body>
  • </html>

In the demo, I am storing all the associated data at a single "Data" index within the associated set. I am not sure if this is considered good or if I should be storing each property individually. Either way, I am just so freakin' pumped up to finally know that this jQuery method exists. This is going to revolutionize the way I build complex AJAX applications.




Reader Comments

Only problem that I see with this is that you have to know, in advance, that information is associated with that link, as it otherwise wouldn't be completely obvious to someone jumping into the middle of your code.

I second Todd regarding the metadata plugin. The metadata plugin allows you to associate metadata with a DOM element by storing the information in the "class" attribute of the element.

So instead of creating those DOM elements and assigning the metadata to them via a JavaScript iteration, you could create the links and the metadata via a ColdFusion-based iteration and populate the metadata from CF variables.

@Andy,

I think that's a valid issue to consider. However, I don't think the "behind the scenes" nature of it should cast a shadow on the potential power that this would create. I am thinking in terms of AJAX applications where you might need to create DOM elements that have all sorts of useful properties that would tie into AJAX calls and other events.

@Brian,

I have used CSS classes to create "hooks" such as "save" and "delete" for links and what not. I think one time I stored an ID like:

class="save id:4"

... or something like that. But mostly, I have not used it for data. I will check into this meta data plug-in. Thanks.

One thing I forgot to say: the thing I gained from reading that list of tips was learning about the livequery() plugin. I've built a few UIs where the user ends up creating new DOM elements, and having to assign jQuery events to the new element after it's created is a (modest) hassle. Sounds like livequery would solve that problem for me.

@Brian,

I will have to look into that one a bit more. When I create DOM elements on the fly, they usually all pass through some sort of Init() method that binds all the events. I am not sure if the livequery would do much more than that.

@Ben: I do a similar thing: any event bindings that need to be done more than once on the page are kept in a separate function. If livequery eliminates the need for me to do that, that's just one less thing to code.

I discovered this feature a couple of weeks ago, and it's been a revelation to me!

Hadn't yet got round to blogging it, and now you've beaten me to it...

@Henry,

I don't necessarily these two concepts as being conflicting. Even with an MVC architecture, you still need to pass data around with each event. Think about server side request handling - even with MVC where you have an "action" variable, you still need to pass along query string variables (most of the time) along with the action. In the client, you can use this data() method to pass those type of variables around.

Let's look at a really simple example: a dynamic link that has to do something. For arguments sake, let's say that when you click a link, it deletes itself (and a record on the server).

Let's say the links are created as the result of an AJAX call (in which the ID of the target record was passed back). You could do this:

function AddLinkHandler( strID ){
. . . . var jLink = $( "<a>Delete Record" + strID + "</a>" );
. . . .
. . . . // Bind the ID to the link.
. . . . jLink.bind( "id", strID );
. . . .
. . . . // Bind click handler.
. . . . jLink.click( LinkClickHandler );
. . . .
. . . . // Add link to do document.
. . . . AddLinkToDocument( jLink );
}

Now, have a link on the screen that has a click handler and a bound data item. Now, the click handler could be something like this:

function LinkClickHandler( objEvent ){
. . . . var jLink = $( this );
. . . .
. . . . // Delete link on server.
. . . . DeleteRemoteRecord( jLink.data( "id" ) );
. . . .
. . . . // Remove link from document.
. . . . jLink.remove();
. . . .
. . . . // Prevent default.
. . . . objEvent.preventDefault();
}

Now, we have all of our "Requests" going through centralized methods. I believe that this is what MVC is on the client, right? To be honest, I have not worried about MVC on the client much, so I may be way off.

That said, as the request goes through, the data that is bound to the DOM element goes through with it and can then be referenced as part of the request later on (like the query string on a server-side request).

I see...

The current solution I have for storing the 'id' of an element is to use the id field of the dom element, (must prepend a string since the id attribute cannot start with numbers).

I can see the benefit of using .data(). I guess using .data() to store metadata like id is a good idea.

However, if the underlying data that the view (i.e. DOM elements) represents is more than a few pieces of metadata, or the app needs to do calculations on the metadata, I think having a real JS model (e.g. array of objects) would be a better way to go. Agree?

@Henry,

I think I probably agree. I am fairly new to large, client-rich applications, so I'm just feeling my way out in this territory now.

I hear you on just storing the ID in the "id" attribute. I have pulled that move. But I think (in more MVC style goodness), we want to separate the view (HTML) from the data. But, again, I'm really just making guesses at this point since this is all so new and wonderful :)

@Henry

Here's one way I use the .data() storage. We often build table rows or add elements dynamically to the page. When we build these things, we give them an id and append a counter (e.g. "UserName_row1_col2"). When can store the appended value in the .data() key like so

newElement.data("suffix","row1_col2");

Then when we write event handlers for the new elements, we can just grab the suffix out of the .data instead of parsing the ID of the element.

//old way
var id = e.target.id.replace("UserName", "");

//new way
var id = "UserName" + $(e.target).data("suffix");

Then we can use the suffix for to find any child elements by id as well.

var suffix = $(parentDiv).data("suffix");
var text = $("#textbox" + suffix).val();
var select = $("#select" + suffix).val();

It may be a personal preference, but when you are dealing with widgets, it makes the event handlers a little cleaner IMO.

@Scott,

I think it also is going to keep the data much more clear and readable. Thinking about the idea of even having to parse strings to grab mission-critical data? Seems crazy when you can easily pass it through with obvious keys.

The other thing is that by not messing with existing HTML attributes you can more easily design unobtrusive code.

for me jQuery's ability to layer on stuff to a functional base means 508 compliance is much easier.

The other thing is that by not messing with existing HTML attributes you can more easily design unobtrusive code.

for me jQuery's ability to layer on stuff to a functional base means 508 compliance is much easier.

Found this blog post via the DZone site, where the poster describes how extend jQuery's selector functions so you can actually select DOM elements based on the metadata assigned to the elements via the jQuery data() method:

http://james.padolsey.com/javascript/extending-jquerys-selector-capabilities/

...so, referring back to Ben's code example, you could use this technique to select all of the "Sassy" DOM elements. (Grin) Because we all know we like the sassy page elements the best.

Hi Ben

thanks for that cool explenation and sample. I've done a little bit of jquery but seems I've missed a cool part how to use selectors. Sorry for the beginners question, but what does this selectors acutally do here in your sample:
$( "<li></li>" );
$( "<a>" + strName + "</a>" );

what are they 'selectin'? Thanks for your help ;-)
Gerald

Gerald...

Those two lines aren't selecting anything. They're creating a new jQuery object that Ben uses later in his code.

You see in the very next line, he does a jLink.data() call. He's storing data in the anchor tag for future reference. Then later he does jListItem.append(jLink) which wraps the anchor tag in the empty list item.

Thanks for your quick reply and explanation Andy :-) Hmm, cool, haven't realized that you could actually also create NeW jQuery objects like this. Think I have to play around with it to fully understand how it can be used...

Gerald

@Gerald,

Correct, and just to further clarify, not only are you creating new jQuery objects, you are creating new HTML elements inside of those jQuery objects. So, the LI and A tags are actually getting created as new DOM elements not currently attached to any tree.

@Gerald,

When you pass HTML to the jQuery factory method ($), it actually creates a DOM element(s) and stores it inside the new jQuery object. So, the line $( "<li></li>" ) creates this jQuery object:

[ <li /> ]

... where the new LI DOM element is the only element in the jQuery "array". To then add the new element to the visible, rendered HTML page, you simply have to inject it with something like this (assuming there is a UL with id, list):

var jLI = $( "<li />" );

$( "#list" ).append( jLI );

Here, we are creating the NEW list element, then finding a reference to the UL element and appending the new list item.

Thanks again Ben for explaining. I've got it now and it's been added safely to my gray mass storage ;-)

Take care
Gerald

You can associate a property set in a separate object structure, but this data feature is way more convenient.

A plug-in that uses the class attribute falls under the category of misusing HTML attributes.

I just learned from John Resig today that jQuery 1.3.3 will allow us to call the data() method with no arguments to get a full set of the stored data items.

Hi Ben, great article / video. I, like you, am blown away that I didn't know this before. Question, how would one use this on new content added to the dom? I have built a calendar app that allows you to click on a jquery datepicker and it loads in the appointments for the day. The appointments for the day are HTML content sent from the server to the calling js function. I guess my question is, is there a way to reset the data method and apply it to new HTML?

Thanks again for the article!

Greg, you would use the function the same as with existing DOM elements. Let's say you have some AJAX filling a given DIV with some raw HTML.

In jQuery success function:
$("div.container").html(data);

To apply data to one of its child elements:
$("div.container").find("yourSelector").data("dataName", "dataValue");

Hope that helps!

@Greg,

@Stephen is right - you can use the data() method on any item in the DOM (or even on non-attached DOM nodes such as those in an intermediary HTML AJAX response). The data() method is not shared by the various nodes; rather, it works on the nodes to store individual pieces of data with any given node.

@Sawyer,

Yeah, I have been loving FireQuery. Make is very easy to debug issues that would have otherwise been very difficult.