I want to use a .filter
function against a large number of elements where I'm checking for a data-*
attribute, however they are set by the .data
method.
The problem is that the jQuery selector always 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.
Here's my test HTML:
<div id="test" data-dummy="foo"></div>
<div id="result"</div>
Then:
$('#test').data('dummy', 'bar');
$('#result').
append('<div>Where dummy=foo: ' + $('[data-dummy="foo"]').length + '</div>').
append('<div>Where dummy=bar: ' + $('[data-dummy="bar"]').length + '</div>');
Outputs:
Where dummy=foo :1
Where dummy=bar :0
It looks like, for the selector engine, only the original value is used in selectors.
Update - cheers to initial answers, this is actually on purpose. .data
starts with data-*
values, but sets its own copy. I don't want to replace every call to .data(
with a call to .attr('data-' +
.
To work around this I'm using .filter
with a function, but as this filter will run for large numbers of elements I don't want to create a new jQuery object for each match. For this sort of circumstance jQuery provides some global functions.
Basically I should be able to use $.data(element,
instead of $(element).data(
, but this doesn't work either: $.data(element,
returns undefined
.
So my code ends up something like .filter(function(){return $.data(this, key) === value;})
However that appears to only be before a jQuery object is initialised:
var domEle = document.getElementById('test');
var globalBefore = $.data(domEle, 'dummy');
var first = $(domEle).data('dummy');
var globalAfter = $.data(domEle, 'dummy');
$('#result').
append('<div>$.data before: ' + globalBefore + '</div>').
append('<div>new $(): ' + first + '</div>').
append('<div>$.data after: ' + globalAfter + '</div>');
Outputs:
$.data before: undefined
new $(): foo
$.data after: foo
Weird. Never mind, the question is can I work around it? Is there any way to initialise whatever's happening for a new $
object without creating one for every tested node?
JS Fiddle demo: http://jsfiddle.net/kU4th/
It's not a bug, it's just really confusingly-designed behavior.
That's because
data
never setsdata-*
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:you need
If you want to both set the data and the attribute:
data
associates key/value pairs of data with elements using a jQuery-managed cache of objects. If you askdata
for a key that doesn't already exist in the element's data cache, it looks to see if that key matches adata-*
attribute and, if so, initializes the data cache with that value. At that point, there is no further connection betweendata
and thedata-*
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 viadata
, whereas of course attribute values are always strings. So for instance, if you do:...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. :-)
Right. The documentation for
$.data
tells you why:You've said you don't want to replace calls to
data
with calls toattr
, but I have to say that seems like the best situation, so you can actually use thosedata-*
attributes in selectors.Alternately, you can do this:
...which first looks at the
data
data and, if it gets back a falsey value (undefined
,null
,""
,false
,NaN
, or0
), goes and gets the attribute from the element instead. If you want to limit that to justundefined
: