mike mike - 1 month ago 7
Javascript Question

Can read initial data attribute (data-*), but can't read updated data attribute

A page I am working on currently generates a div with an initial set of values, stored as JSON in a

data-*
attribute:

The JSON is:

{"name":"Qm9i","gen":"1","level":"4","topic":"","checked":"eyIxIjoiNCIsIjciOiI0IiwiOCI6IjQiLCI5IjoiNCJ9","pronoun":"2"}


In the element like this:

<div id="comment-1" class="container-element" data-all="{&quot;name&quot;:&quot;Qm9i&quot;,&quot;gen&quot;:&quot;1&quot;,&quot;level&quot;:&quot;4&quot;,&quot;topic&quot;:&quot;&quot;,&quot;checked&quot;:&quot;eyIxIjoiNCIsIjciOiI0IiwiOCI6IjQiLCI5IjoiNCJ9&quot;,&quot;pronoun&quot;:&quot;2&quot;}">


There are a number of these divs, with unique ID's, and the user can make a final edit to content within the div (and the changes are reflected in the data attribute) before final output.

When the user clicks EDIT, the following function is called:

// Get the id of the current comment block
var index = getIndex($(this).attr('id'));
var commentFrame = $('#comment-' + index);


var fr = commentFrame.attr('id');
console.log("Edit button for " + fr + " was clicked...");

var b64 = commentFrame.data('all').checked;
var lvl = commentFrame.data('all').level;

console.log("Retrieved from the bottom of the container...")
console.log("level: " + lvl + " , checked: " + b64 + " or " + atob(b64));

//rest of code here


(apologies for the abundance of log statements - I've ben trying to debug this for quite a while now)

When the EDIT button is first clicked, the code seems to execute correctly, and the data is retrieved, as evidenced in the console log...

Edit button for comment-1 was clicked...
Retrieved from the bottom of the container...
level: 4 , checked: eyIxIjoiNCIsIjciOiI0IiwiOCI6IjQiLCI5IjoiNCJ9 or {"1":"4","7":"4","8":"4","9":"4"}


... edits to the contents of the div are made, and the data attribute is updated with the new values in another function

var details = JSON.parse(parent.attr('data-all'));
details.checked = fdata.checked; //new value for 'checked'
details.level = avg.toFixed(2); //new value for 'level'

// update everything...
parent.attr('data-all', JSON.stringify(details));


...and this is immediately reflected in the data attribute for the div ('level' and 'checked' have both changed.

<div id="comment-1" class="container-element" data-all="{&quot;name&quot;:&quot;Qm9i&quot;,&quot;gen&quot;:&quot;1&quot;,&quot;level&quot;:&quot;3.25&quot;,&quot;topic&quot;:&quot;&quot;,&quot;checked&quot;:&quot;eyIxIjoiMSIsIjciOiIzIiwiOCI6IjQiLCI5IjoiNSJ9&quot;,&quot;pronoun&quot;:&quot;2&quot;}">


However...

The problem arises when you click edit on a previously edited div (for example if the user changes his mind a second time).

As one would expect, the exact same code block above is called, but instead of returning the updated 'level' and 'checked' values from the data attribute, it is instead returning the initial values for both 'level' and 'checked', which have been overritten in the tag by the earlier edit and shouldn't be accessible any more.

Edit button for comment-1 was clicked...
Retrieved from the bottom of the container...
level: 4 , checked: eyIxIjoiNCIsIjciOiI0IiwiOCI6IjQiLCI5IjoiNCJ9 or {"1":"4","7":"4","8":"4","9":"4"}


I've been racking my brain and staring at this for a while now and getting nowhere, so I was hoping that the good people here might be able to offer a suggestion as to where I might be going wrong.

Answer

data is not an accessor for data-* attributes. It accesses and manages jQuery's data cache for an element, which is initialized from data-* attributes but is otherwise completely disconnected from them. This is a point of confusion for a large number of people.

If you want to use the actual attribute, use attr("data-all") consistently in your code.

If you want to use jQuery's data cache for the element, use data("all") consistently in your code.

If you mix attr("data-all") and data("all") in your code, you will get inconsistent results.

Simpler example:

var d = $("#foo");
console.log("d.attr('data-all'): " + d.attr("data-all"));
console.log("d.data('all'): " + d.data("all"));
console.log("Updating the data cache doesn't update the attribute:");
d.data("all", "data updated");
console.log("d.attr('data-all'): " + d.attr("data-all"));
console.log("d.data('all'): " + d.data("all"));
console.log("Updating the attribute doesn't update the data cache:");
d.attr("data-all", "attr updated");
console.log("d.attr('data-all'): " + d.attr("data-all"));
console.log("d.data('all'): " + d.data("all"));
<div id="foo" data-all="initial"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Comments