jQuery .data() does not work, but .attr() does

Jquery

Jquery Problem Overview


Forgive me for not being more specific on this. I have such a strange bug. After the doc loads, I loop some elements that originally have data-itemname="", and I set those values using .attr("data-itemname", "someValue").

Issue: When I later loop thru those elements, if I do elem.data().itemname, I get "", but if I do elem.attr("data-itemname"), I get "someValue". It's like jQuery's .data() getter only gets elements that are set initially to contain some value, but if they are originally empty, and later set, then .data() doesn't get the value later on.

I've been trying to recreate this bug but have not been able to.

Edit

I have recreated the bug! http://jsbin.com/ihuhep/edit#javascript,html,live

Also, snippets from above link...

JS:

var theaters = [
    { name: "theater A", theaterId: 5 },
    { name: "theater B", theaterId: 17 }
];

$("#theaters").html(
    $("#theaterTmpl").render(theaters)
);

// DOES NOT WORK - .data("name", "val") does NOT set the val
var theaterA = $("[data-theaterid='5']");
theaterA.find(".someLink").data("tofilllater", "theater5link"); // this does NOT set data-tofilllater
$(".someLink[data-tofilllater='theater5link']").html("changed link text"); // never gets changed

// WORKS - .attr("data-name", "val") DOES set val
var theaterB = $("[data-theaterid='17']");
theaterB.find(".someLink").attr("data-tofilllater", "theater17link"); // this does set data-tofilllater
$(".someLink[data-tofilllater='theater17link']").html("changed link text");

HTML:

<body>
    <div id="theaters"></div>
</body>

<script id="theaterTmpl" type="text/x-jquery-tmpl">
    <div class="theater" data-theaterid="{{=theaterId}}">
        <h2>{{=name}}</h2>
        <a href="#" class="someLink" data-tofilllater="">need to change this text</a>
    </div>
</script>

Jquery Solutions


Solution 1 - Jquery

I ran into a similar "bug" a few days ago when working with .data() and .attr('data-name') for HTML5 data attributes.

The behavior you're describing is not a bug, but is by design.

The .data() call is special - not only does it retrieve HTML5 data attributes it also attempts to evaluate/parse the attributes. So with an attribute like data-myjson='{"hello":"world"}' when retrieved via .data() will return an Object while retrieval via .attr() will return a string. See jsfiddle example.

Since .data() does extra processing jQuery stores the results of attribute evaluation in $.cache - after all, once a data attribute has been evaluated it would be wasteful to re-evaluate on every .data() call - especially since data variables can contain complex JSON strings.

I said all that to say the following: After retrieving an attribute via .data() any changes made by .attr('data-myvar', '') will not be seen by subsequent .data() calls. Test this out on jsfiddle.

To avoid this problem don't intermix .data and .attr() calls. Use one or the other.

Solution 2 - Jquery

This is the result of a misunderstanding: data is NOT an accessor for data-* attributes. It's both more and less than that.

data is an accessor for jQuery's data cache on the element. That cache is initialized from data-* attributes if there are any present, but data never writes to the attributes, nor does changing the attribute change the data cache after initialization:

const div = $("[data-example]");
console.log('div.data("example"):', div.data("example"));
console.log('div.attr("data-example"):', div.attr("data-example"));
console.log('Using div.data("example", "updated")');
div.data("example", "updated");
console.log('div.data("example"):', div.data("example"));
console.log('div.attr("data-example"):', div.attr("data-example"));

<div data-example="initial value"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

data also massages what it finds in various ways, guessing at data types, making data("answer") on an element with data-answer="42" a number, not a string, or even parsing things as JSON if they look like JSON:

console.log(typeof $("[data-answer]").data("answer"));
console.log(typeof $("[data-json]").data("json"));
console.log(typeof $("[data-str]").data("str"));

<div data-answer="42"></div>
<div data-json='{"answer":42}'></div>
<div data-str="example"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

If you want to use the attributes (both reading and setting them), use attr, not data. attr is an accessor for attributes.

Solution 3 - Jquery

.attr("data-itemname", "someValue") modifies the DOM.

.data("itemname", "someValue") modifies the jQuery cache.

To get this working in following Javascript and in addition in CSS you have to set both.

theaterA.find(".someLink").attr("data-itemname", "someValue");
theaterA.find(".someLink").data("itemname", "someValue");

Solution 4 - Jquery

That's because the attribute's name is data-itemname. You cannot use - in the shorthand obj.attribute notation (obj.data-itemname would be intepreted as "obj.data minus itemname").

Solution 5 - Jquery

Why don't you just use .data() everywhere?

You can also declare default values inline on the HTML, which is fine too.

<span data-code="pony">text</span>

and

$("span").data("code") == "pony" // true

if you want to change it you just do

$("span").data("code", "not-a-pony");

and to remove it altogether you could invoke

$("span").removeData("code");

you should really try and avoid using .attr("data-*"), I don't know why you'd want to do so anyways.

Solution 6 - Jquery

You must set the data using .data('itemname', 'someValue');. Using .attr() to set data attributes won't work: http://jsfiddle.net/ThiefMaster/YHsKx/

However, you can provide inline values by using e.g. <div data-key="value"> in the markup.

Solution 7 - Jquery

I can see that this has brought up some division on how to approach the data attribute setting.

I too run into this problem and I found that the issue seemed to be simply the data attribute name formatting.

In my experience, you should avoid using hyphens in the data variable (the variable name that comes after "data-").

This didn't work for me:

[Markup]

<div class="list" id="myElement1" data-item-order="some-value"> .... </div>

[jQuery]

jQuery("#myElement1").data("item-order", "my-new-value");

But the following worked just fine! :) :

(I use an underscore instead of a hyphen when required)

[Markup]

<div class="list" id="myElement1" data-item_order="some-value"> .... </div>

[jQuery]

jQuery("#myElement1").data("item_order", "my-new-value");

I hope it helps. Cheers everyone!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIan DavisView Question on Stackoverflow
Solution 1 - JqueryleepowersView Answer on Stackoverflow
Solution 2 - JqueryT.J. CrowderView Answer on Stackoverflow
Solution 3 - JqueryElmar HöfinghoffView Answer on Stackoverflow
Solution 4 - JqueryMarc BView Answer on Stackoverflow
Solution 5 - JquerybevacquaView Answer on Stackoverflow
Solution 6 - JqueryThiefMasterView Answer on Stackoverflow
Solution 7 - JquerymmmdearteView Answer on Stackoverflow