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
datanever 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:
dataassociates key/value pairs of data with elements using a jQuery-managed cache of objects. If you askdatafor 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 betweendataand thedata-*attribute.The reason
datadoesn'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
$.datatells you why:You've said you don't want to replace calls to
datawith 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
datadata 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: