jQuery – What You Can do with the Data Method

A question came up yesterday on the jQuery mailing list where somebody wanted to effectively “embed” data into a link. They essentially wanted to call a function on click, but needed to pass arguments. Since jQuery is supposed to be unobtrusive (e.g. “Find Something, Do Something”), how can we tell jQuery that, when a specific link is clicked, there’s specific data associated with that link?

The jQuery data() method allows you to do just that.

Here’s a quick demo that illustrates how the data() method is used. There are no AJAX calls. The data is populated via a call to a CFC that returns a ColdFusion query object. All of the relevant data for a particular player is stored in a specific namespace. Code below.

First, a straightforward call to a CFC to obtain our data:

<cfset data = createObject( 'component', 'data' ) />
<cfset players = data.getPlayers() />

Which results in the data shown below:

cfdump screenshot

Nothing new or different yet. In fact, let’s flesh the page out a little bit more in the way that would be familiar to ColdFusion developers:

<cfset data = createObject( 'component', 'data' ) />
<cfset players = data.getPlayers() />

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title></title>

	<style type="text/css">
		#playerList, #playerInfo	{ float:left; width:160px; }
		#playerList			{ width:160px; }
		#playerInfo			{ width:400px; }
		#playerInfo img { float:left; padding-right:8px; }
	</style>
</head>

<body>

<div id="playerList">
<cfoutput query="players">
	<a href="##" id="player#playerID#">#playerName#</a><br />
</cfoutput>
</div>

<div id="playerInfo"></div>

</body>
</html>

Easy! We’ve got a list of players, retrieved from our CFC. Now we want to add some pop, so we’re going to use jQuery to listen for a click on one of the player name links, and then populate the (currently empty) “playerInfo” div with information about the player. To emphasize the beauty of jQuery’s unobtrusive nature, we won’t be touching the existing markup at all. Simply adding some script.

The first thing to do, of course, is to include the jQuery library. You can either refer to it on your own server, or the hosted version on Google’s servers. I’m doing the latter.

<script src="http://code.jquery.com/jquery-latest.js"></script>

If you scroll up and look at the code example where we’re outputting the links, you’ll see that each link has a unique ID value of “playerN”, where “N” is the playerID value returned from our query. We’re going to assign our data to this same namespace.

<script>
	$( document ).ready( function() {
		<cfoutput query="players">
			$( '##player#playerID#' ).data( 'playerInfo', { number:#playerNumber#, position:'#playerPosition#', image:'#PlayerImg#', college:'#PlayerCollege#' } );
		</cfoutput>
	});
</script>

Inside our $( document ).ready(), which fires automatically when the page is finished loading, I do a simple query-driven <cfoutput>. It might be easier to follow along if you see the actual JavaScript that is generated:

$('#player1').data('playerInfo', { number:13, position:'WR', image:'http://assets.giants.com/uploads/players/2FE2D3BDF4FB443D949D1D39B69ADC03.gif', college:'Cal Poly' });
$('#player2').data('playerInfo', { number:47, position:'TE', image:'http://assets.giants.com/uploads/players/7BD58CBD8C6844DE875964C1B06A4757.gif', college:'Wisconson' });
$('#player3').data('playerInfo', { number:95, position:'DT', image:'http://assets.giants.com/uploads/players/63E56B04414F4677B13B8314A3C3A6E9.gif', college:'Texas A&M' });
$('#player4').data('playerInfo', { number:89, position:'TE', image:'http://assets.giants.com/uploads/players/B82703AB4B5F416BAEE1CD35F4F49810.gif', college:'Western Oregon' });
$('#player5').data('playerInfo', { number:44, position:'RB', image:'http://assets.giants.com/uploads/players/003E58E09A764087802C550C738C2397.gif', college:'Marshall' });
$('#player6').data('playerInfo', { number:18, position:'P', image:'http://assets.giants.com/uploads/players/E4D8C7C767D74D31BE13F2042CDA5948.gif', college:'Miami (FL)' });
$('#player7').data('playerInfo', { number:27, position:'RB', image:'http://assets.giants.com/uploads/players/DDDE77B88074439FA9C7DC9D1C5F6289.gif', college:'Southern Illinois' });
$('#player8').data('playerInfo', { number:10, position:'QB', image:'http://assets.giants.com/uploads/players/1D2BC758565448988AB8BCB25AF580A2.gif', college:'Mississippi' });
$('#player9').data('playerInfo', { number:82, position:'WR', image:'http://assets.giants.com/uploads/players/BF00DD399A1348E481E050330306DBA6.gif', college:'Michigan' });
$('#player10').data('playerInfo', { number:91, position:'DE', image:'http://assets.giants.com/uploads/players/AA3798254BD444BFB7BC247FC4C43A11.gif', college:'Notre Dame' });

For each iteration of our output, we’re assigning data (using the data() method) to the element with ID “playerN” (again, “N” representing the playerID returned from the query). The data is going to be assigned to a variable named “playerInfo”. The value of the “playerInfo” variable is a JSON string containing the number, position, image, and college of each of the players. All of these values were populated from our cfquery data.

Now that we’ve got this data, let’s get set up for displaying it.

We know that we have our links, each with ID “playerN”. So we’re going to want to listen for a click event on an “a” element that has an ID attribute that starts with “player”. We can do this with a jQuery selector and attribute filter:

$( 'a[ id^=player ]' ).click( function() { ( we're going to do stuff here ) } );

To break down that selector a bit… if we wanted to select every “a” element on the page, we’d simply do:

$('a')

The attribute filter allows us to filter those elements by a given attribute. If we wanted every “a” element that has an ID attribute, we’d do:

$( 'a[ id ]' )

If we wanted to filter for all “a” elements that have an ID attribute of “player”, we’d do:

$( 'a[ id=player ]' )

Because we only want “a” elements that have an ID attribute that begins with player, we end up with:

$( 'a[ id^=player ]' )

Notice the caret (^) character. This is what specifies to jQuery that we don’t want an ID with the exact value of “player”, but rather one that starts with the value “player”, which is exactly what we need.

Now that we’ve sufficiently established that we’re capturing the correct click event… what do we do inside of it?

var playerData = "";
playerData += '<img src="' + $(this).data('playerInfo').image + '" />';
playerData += "Number: " + $(this).data('playerInfo').number + "<br />";
playerData += "Position: " + $(this).data('playerInfo').position + "<br />";
playerData += "College: " + $(this).data('playerInfo').college;

First line creates a variable called “playerData”. Then with each subsequent line, I’m simply appending text to that variable. Most of it should be fairly straightforward string concatenation in JavScript. Literal values go in quotes, variables (JavaScript variables) outside of quotes, and a plus sign to join them together.

What should be new to you is the specific JavaScript variables. $(this) refers to the element that triggered the click event. $(this).data() is a hook into the data that we had previously associated with that element. We want to retrieve the “playerInfo” data, so we reference that specifically via $(this).data(‘playerInfo’). The data we stored in that variable was JSON. Think of JSON as a string representation of a ColdFusion structure. It’s got keys, and it’s got values. The “keys” that we set were “image”, “number”, “position”, and “college”. Therefore, on the appropriate lines, those are the keys we’re going to retrieve.

We should now have a bit of HTML stored in our playerData variable. The final step is to insert it into the page.

$( '#playerInfo' ).html( playerData );
return false;

Again, using a jQuery selector, we identify the element with the ID value of “playerInfo”. This is an empty div that we created on the page. Using jQuery’s built in html() function, we’re going to set the html by passing in the playerData variable (which was the HTML string we previously constructed). Finally, we return false to prevent the browser from attempting to follow the link… and we’re done.

The final markup:

<cfset data = createObject( 'component', 'data' ) />
<cfset players = data.getPlayers() />

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title></title>

	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script>
		$( document ).ready( function() {
			<cfoutput query="players">
				$( '##player#playerID#' ).data( 'playerInfo', { number:#playerNumber#, position:'#playerPosition#', image:'#PlayerImg#', college:'#PlayerCollege#' } );
			</cfoutput>
			
			$( 'a[ id^=player ]' ).click( function() {
				var playerData = "";
				playerData += '<img src="' + $( this ).data( 'playerInfo' ).image + '" />';
				playerData += "Number: " + $( this ).data( 'playerInfo' ).number + "<br />";
				playerData += "Position: " + $( this ).data( 'playerInfo' ).position + "<br />";
				playerData += "College: " + $( this ).data( 'playerInfo' ).college;
				
				$( '#playerInfo' ).html( playerData );
				return false;
			});
		});
	</script>

	<style type="text/css">
		#playerList, #playerInfo	{ float:left; width:160px; }
		#playerList			{ width:160px; }
		#playerInfo			{ width:400px; }
		#playerInfo img { float:left; padding-right:8px; }
	</style>
</head>

<body>

<div id="playerList">
<cfoutput query="players">
	<a href="##" id="player#playerID#">#playerName#</a><br />
</cfoutput>
</div>

<div id="playerInfo"></div>

</body>
</html>

You can copy/paste and run locally as long as you have a CFC named “data.cfc” in the same directory, and that CFC has a method called getPlayers() that returns a query with columns playerID, playerNumber, playerName, playerPosition, playerImg, and playerCollege.