It's not a bug, it's just really confusingly-designed behavior.
The problem is that the jQuery selector seems to use the original value. If I set a data value using $('whatever').data('key', newValue) and then try to use a selector $('[data-key=newValue]') then nothing is found.
That's because data
never sets data-*
attributes, it just initializes itself from them. It's assymetrical (reads from them but doesn't write to them).
To actually update the attribute, use attr
. E.g., instead of:
$('whatever').data('key', newValue);
you need
$('whatever').attr('data-key', newValue);
If you want to both set the data and the attribute:
$('whatever').data('key', newValue).attr('data-key', newValue);
data
associates key/value pairs of data with elements using a jQuery-managed cache of objects. If you ask data
for a key that doesn't already exist in the element's data cache, it looks to see if that key matches a data-*
attribute and, if so, initializes the data cache with that value. At that point, there is no further connection between data
and the data-*
attribute.
The reason data
doesn't write back to the attributes is probably at least partially down to the fact that you can store anything via data
, whereas of course attribute values are always strings. So for instance, if you do:
$("whatever").data('key', {
nice: "complex",
obj: "here",
answer: 42,
when: new Date()
});
...then later $("whatever").data('key')
will give you back that object. That object cannot be stored in an attribute.
You're not, by far, the first person to be burned by this surprising design. :-)
Basically I should be able to use
$.data(element
, instead of$(element).data(
, but this doesn't work either:$.data(element
, returnsundefined
.
Right. The documentation for $.data
tells you why:
Note: This is a low-level method; a more convenient
.data()
is also available.Regarding HTML5 data-* attributes: This low-level method does NOT retrieve the data-* attributes unless the more convenient
.data()
method has already retrieved them.
You've said you don't want to replace calls to data
with calls to attr
, but I have to say that seems like the best situation, so you can actually use those data-*
attributes in selectors.
Alternately, you can do this:
matchingElements.filter(function(){
return ($.data(this, key) || this.getAttribute('data-' + key)) === value;
});
...which first looks at the data
data and, if it gets back a falsey value (undefined
, null
, ""
, false
, NaN
, or 0
), goes and gets the attribute from the element instead. If you want to limit that to just undefined
:
matchingElements.filter(function(){
var value = $.data(this, key);
return typeof value === "undefined" ? this.getAttribute('data-' + key) : value;
});