jQuery: Storing and retrieving data related to elements
It's very common to need to get information about a DOM element when a
user interacts with it -- for example, perhaps you have an unordered
list of names, and when a user clicks on a name, you want to show a
picture of the person above the list. In order to do this, you need to
figure out which person the clicked list item represents.
Many beginning jQuery users will attempt to achieve this by putting ID
attributes on each list item, such as id="rebecca". Then, they'll read the ID attribute off the clicked element and use it to build a URL for the related image.
Paul
You could do:
- Paul
- Rebecca
- Alex
- Adam
var portrait = $('#portrait');
$('ul.people li').click(function() {
var name = $(this).attr('id');
portrait.html('');
});Strictly speaking, this will work. But is the ID really the right place
to store this information? What if you need this behavior on other
elements on the page too? You can't have more than one element with the
same ID on the page, so you might find yourself using funny prefixes in
your IDs, like "person_rebecca", and then stripping out the prefix. You
could do it with classes, but then you'd have the opposite problem:
you're using (generally) unique classes like "rebecca", but really
classes are meant to indicate similarities among a set of elements. And
what if you need to store more than one piece of information about an
element on the element? Next thing you know you've got id="person_alex_red" and you're jumping through all sorts of hoops to parse out the data you need.
Custom Data Attributes
HTML5 makes available custom data attributes, and they prove to be a much more elegant and robust solution to this problem. They're custom, so they can contain pretty much anything you want, and each element can have as many of them as you want.- Paul
- Rebecca
- Alex
- Adam
var $portrait = $('#portrait');
$('ul.people li').click(function() {
var $li = $(this),
name = $li.attr('data-name'), color = $li.attr('data-hairColor'),
$img = $('');
$portrait.append($img).css('border', '5px solid ' + color);
});$.fn.data
When you want to embed information about an element in the HTML you send down from your server, custom data attributes offer a clear and easy solution. But what if you want to attach data to elements that you've added to the page using JavaScript? For example, you might have some data you fetched from your server via an Ajax request:{
"items" : [
{ "name" : "Paul", "image" : "paul", "hairColor" : "black" },
{ "name" : "Rebecca", "image" : "rebecca", "hairColor" : "brown" },
{ "name" : "Alex", "image" : "alex", "hairColor" : "red" },
{ "name" : "Adam", "image" : "adam", "hairColor" : "red" }
]
}You're going to iterate over the data to produce a structure much like
the one above, but in this case, it doesn't make sense to store the
related data in markup, because you'll just have to extract it again
later. Instead, you can use the $.fn.data() method in jQuery to store the data using JavaScript instead of markup:
var $target = $('ul.people');
$.each(response.items, function(i, data) {
$('', { html : data.name })
.data({
name : data.image,
hairColor: data.hairColor })
.appendTo($target);
});
Later, you can read the data off the element using the $.fn.data() method again, this time passing just the name of the key you're after:
var $portrait = $('#portrait');
$('ul.people li').click(function() {
var $li = $(this),
name = $li.data('name'),
color = $li.data('hairColor'),
$img = $('');
$portrait.append($img).css('border', '5px solid ' + color);
});Mixing the two methods
This is all well and good, but what if you have a list of people that was sent from the server using HTML and custom data attributes, and then you add elements to it later using JavaScript and store data on them using $.fn.data()? Now your data is stored on elements in two different ways, so how do you extract it reliably? One option is to handle both cases. First, you'll switch to using jQuery's delegate method for the event binding, so you don't have to keep binding click handlers as you add list items to your list. Then, inside of your click handler, you'll figure out where you can get your data from:var $portrait = $('#portrait');
$('ul.people').delegate('li', 'click', function() {
var $li = $(this), name = $li.attr('data-name'), color, $img;
if (!name) { // did the li have custom data attributes?
name = $li.data('name');
color = $li.data('hairColor');
} else {
color = $li.attr('data-hairColor');
}
$img = $('');
$portrait.append($img).css('border', '5px solid ' + color);
});Another option is to iterate over the original elements, and store the
data from the custom data attributes using the $.fn.data() method:
var $portrait = $('#portrait'),
$ul = $('ul.people');
$ul.find('li').each(function() {
var $li = $(this);
$li.data({
name : $li.attr('data-name'),
hairColor : $li.attr('data-hairColor')
});
});
$('ul.people').delegate('li', 'click', function() {
var $li = $(this),
name = $li.data('name'),
color = $li.data('hairColor'),
$img = $('');
$portrait.append($img).css('border', '5px solid ' + color);
});
// load more list items via ajax at some point
// to make that whole delegate thing worthwhileWhich option you use will depend on how large your original list is (and
thus how long it will take to iterate over it), and how likely people
are to click on a lot of items in the list (and thus whether the initial
iteration is worth the time). I leave it as an exercise for the reader
to decide which approach makes sense for you.
The Metadata Plugin
If you're really into this stuff, you probably also want to check out the jQuery Metadata plugin, which offers the option of reading all custom data attributes on an element and returning an object. So for example, given this markup:var myData = $myListItem.metadata({ type : 'attr' });
// returns { name : "paul", hairColor : "black" }In Conclusion
When you need to attach data to elements and then extract that data later, there are options beyond classes and IDs, and in fact classes and IDs may be an especially poor way to approach the problem. Taking advantage of custom data attributes and the $.fn.data() method in jQuery can make it painless to store and retrieve data related to elements, and the metadata plugin can streamline the process for you even further.Tags:
- Login or register to post comments
- 1870 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)



