Unable to set data attribute using jQuery Data() API

132,982

Solution 1

It is mentioned in the .data() documentation

The data- attributes are pulled in the first time the data property is accessed and then are no longer accessed or mutated (all data values are then stored internally in jQuery)

This was also covered on Why don't changes to jQuery $.fn.data() update the corresponding html 5 data-* attributes?

The demo on my original answer below doesn't seem to work any more.

Updated answer

Again, from the .data() documentation

The treatment of attributes with embedded dashes was changed in jQuery 1.6 to conform to the W3C HTML5 specification.

So for <div data-role="page"></div> the following is true $('div').data('role') === 'page'

I'm fairly sure that $('div').data('data-role') worked in the past but that doesn't seem to be the case any more. I've created a better showcase which logs to HTML rather than having to open up the Console and added an additional example of the multi-hyphen to camelCase data- attributes conversion.

Updated demo (2015-07-25)

Also see jQuery Data vs Attr?

HTML

<div id="changeMe" data-key="luke" data-another-key="vader"></div>
<a href="#" id="changeData"></a>
<table id="log">
    <tr><th>Setter</th><th>Getter</th><th>Result of calling getter</th><th>Notes</th></tr>
</table>

JavaScript (jQuery 1.6.2+)

var $changeMe = $('#changeMe');
var $log = $('#log');

var logger;
(logger = function(setter, getter, note) {
    note = note || '';
    eval('$changeMe' + setter);
    var result = eval('$changeMe' + getter);
    $log.append('<tr><td><code>' + setter + '</code></td><td><code>' + getter + '</code></td><td>' + result + '</td><td>' + note + '</td></tr>');
})('', ".data('key')", "Initial value");

$('#changeData').click(function() {
    // set data-key to new value
    logger(".data('key', 'leia')", ".data('key')", "expect leia on jQuery node object but DOM stays as luke");
    // try and set data-key via .attr and get via some methods
    logger(".attr('data-key', 'yoda')", ".data('key')", "expect leia (still) on jQuery object but DOM now yoda");
    logger("", ".attr('key')", "expect undefined (no attr <code>key</code>)");
    logger("", ".attr('data-key')", "expect yoda in DOM and on jQuery object");

    // bonus points
    logger('', ".data('data-key')", "expect undefined (cannot get via this method)");
    logger(".data('anotherKey')", ".data('anotherKey')", "jQuery 1.6+ get multi hyphen <code>data-another-key</code>");
    logger(".data('another-key')", ".data('another-key')", "jQuery < 1.6 get multi hyphen <code>data-another-key</code> (also supported in jQuery 1.6+)");

    return false;
});

$('#changeData').click();

Older demo


Original answer

For this HTML:

<div id="foo" data-helptext="bar"></div>
<a href="#" id="changeData">change data value</a>

and this JavaScript (with jQuery 1.6.2)

console.log($('#foo').data('helptext'));

$('#changeData').click(function() {
    $('#foo').data('helptext', 'Testing 123');
//  $('#foo').attr('data-helptext', 'Testing 123');
    console.log($('#foo').data('data-helptext'));
    return false;
});

See demo

Using the Chrome DevTools Console to inspect the DOM, the $('#foo').data('helptext', 'Testing 123'); does not update the value as seen in the Console but $('#foo').attr('data-helptext', 'Testing 123'); does.

Solution 2

I was having serious problems with

.data('property', value);

It was not setting the data-property attribute.

Started using jQuery's .attr():

Get the value of an attribute for the first element in the set of matched elements or set one or more attributes for every matched element.

.attr('property', value)

to set the value and

.attr('property')

to retrieve the value.

Now it just works!

Solution 3

@andyb's accepted answer has a small bug. Further to my comment on his post above...

For this HTML:

<div id="foo" data-helptext="bar"></div>
<a href="#" id="changeData">change data value</a>

You need to access the attribute like this:

$('#foo').attr('data-helptext', 'Testing 123');

but the data method like this:

$('#foo').data('helptext', 'Testing 123');

The fix above for the .data() method will prevent "undefined" and the data value will be updated (while the HTML will not)

The point of the "data" attribute is to bind (or "link") a value with the element. Very similar to the onclick="alert('do_something')" attribute, which binds an action to the element... the text is useless you just want the action to work when they click the element.

Once the data or action is bound to the element, there is usually* no need to update the HTML, only the data or method, since that is what your application (JavaScript) would use. Performance wise, I don't see why you would want to also update the HTML anyway, no one sees the html attribute (except in Firebug or other consoles).

One way you might want to think about it: The HTML (along with attributes) are just text. The data, functions, objects, etc that are used by JavaScript exist on a separate plane. Only when JavaScript is instructed to do so, it will read or update the HTML text, but all the data and functionality you create with JavaScript are acting completely separate from the HTML text/attributes you see in your Firebug (or other) console.

*I put emphasis on usually because if you have a case where you need to preserve and export HTML (e.g. some kind of micro format/data aware text editor) where the HTML will load fresh on another page, then maybe you need the HTML updated too.

Solution 4

Happened the same to me. It turns out that

var data = $("#myObject").data();

gives you a non-writable object. I solved it using:

var data = $.extend({}, $("#myObject").data());

And from then on, data was a standard, writable JS object.

Solution 5

To quote a quote:

The data- attributes are pulled in the first time the data property is accessed and then are no longer accessed or mutated (all data values are then stored internally in jQuery).

.data() - jQuery Documentiation

Note that this (Frankly odd) limitation is only withheld to the use of .data().

The solution? Use .attr instead.

Of course, several of you may feel uncomfortable with not using it's dedicated method. Consider the following scenario:

  • The 'standard' is updated so that the data- portion of custom attributes is no longer required/is replaced

Common sense - Why would they change an already established attribute like that? Just imagine class begin renamed to group and id to identifier. The Internet would break.

And even then, Javascript itself has the ability to fix this - And of course, despite it's infamous incompatibility with HTML, REGEX (And a variety of similar methods) could rapidly rename your attributes to this new-mythical 'standard'.

TL;DR

alert($(targetField).attr("data-helptext"));
Share:
132,982
Jason Evans
Author by

Jason Evans

Software developer currently working for Confused.com in Wales, UK. My core skills are .NET C# ASP.NET MVC JavaScript Reputation tracker My Notes http://blogs.microsoft.co.il/blogs/sasha/archive/2009/07/31/garbage-collection-thread-suspension-delay-250ms-multiples.aspx

Updated on October 15, 2020

Comments

  • Jason Evans
    Jason Evans over 3 years

    I've got the following field on an MVC view:

    @Html.TextBoxFor(model => model.Course.Title, new { data_helptext = "Old Text" })</span>
    

    In a seperate js file, I want to set the data-helptext attribute to a string value. Here's my code:

    alert($(targetField).data("helptext"));
    
    $(targetField).data("helptext", "Testing 123");
    

    The alert() call works fine, it shows the text "Old Text" in an alert dialog. However, the call to set the data-helptext attribute to "Testing 123" does not work. "Old Text" is still the attribute's current value.

    Am I using the call to data() incorrectly? I've looked this up on the web, and I can't see what I'm doing wrong.

    Here's the HTML markup:

    <input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />
    
  • manubkk
    manubkk over 10 years
    Not sure what has changed, but your fiddle demo returns undefined in chrome console
  • ahnbizcad
    ahnbizcad almost 10 years
    So then what is the point of jQuery allowing you to put in a second argument? To update the value stored in the js variable cache?
  • andyb
    andyb almost 10 years
    @gwho I am not sure I completely understand your question, but I assume you are referring to the original answer from 2011 using jQuery 1.6.2. If so, then the .data('key', 'value') method does update the value in the jQuery cache but for performance reasons (I guess DOM mutation) the DOM itself is not updated.
  • ahnbizcad
    ahnbizcad almost 10 years
    so if you wanted to update the DOM, you'd need to do .attr('key','value') regardless of whether you did .data('key', 'value') or not, right? That seems redundant to me, and I'm having trouble imagining a scenario where you would want to write to the cached DOM, but not the real DOM. Maybe I'm not understand the jQuery cache; So would a visitor see all the things that .data() modifies on their screen, or would they not?
  • andyb
    andyb almost 10 years
    Correct, if you really want to update the DOM you need something like .attr('key', 'value') or just simply element.attributeName = value. It is jQuery's decision to pull once from the DOM but that doesn't stop you using other methods if you really need the DOM data- attributes updating. Don't forget this is about HTML5 custom attributes which are not intended to compete with microformats. You still have access to the updated value in JavaScript where I would expect it to be used.
  • ahnbizcad
    ahnbizcad almost 10 years
    So it's not just a matter of performance; they can't be compared. They have completely different purposes, and they change different "versions" of the DOM. So back to the question: what is the point of using .data() if you have to do .attr() to change the ACUTAL DOM? seems redundant.
  • andyb
    andyb almost 10 years
    I have always believed it to be solely for performance reasons. this test linked from MDN Using data-* attributes shows a huge performance penalty using setAttribute
  • Works for a Living
    Works for a Living almost 10 years
    How do you then write to it?
  • Nico
    Nico almost 10 years
    Sorry Thom, I don't know what do you mean... After doing $.extend..., you can use data as you please: data.name = 'Nico'; data.questionSolved = true;, and console.log(data) will display this newly added properties
  • Frank Forte
    Frank Forte almost 10 years
    there seems to be a bug: console.log($('#foo').data('data-helptext')); should probably be: console.log($('#foo').data('helptext'));
  • andyb
    andyb almost 10 years
    @Frankforte that syntax was for the old version of jQuery. I left my original answer since there are multiple ways to get data-* attributes using different jQuery versions.
  • Buttle Butkus
    Buttle Butkus almost 10 years
    Does this also affect newly-added elements? I cloned a table row that looks like this: <tr data-id="5"></tr>, retrieved the id, incremented it, and then used $clone.data('id',new_index), and then $tbody.append(clone) to append the new TR. But the new TR still has data-id="5". Why wouldn't this work on a newly created element?
  • JoeBrockhaus
    JoeBrockhaus over 9 years
    This is very frustrating because jQuery's handling of this AND using CSS3's attr(data-customdata) breaks the expected HTML5 spec, right? If the only valid custom attributes are data- ones, then this valid use case content: attr(data-xxx); will break when using jQuery best practices of setting with jQuery.data() because the DOM is OOS.
  • Matt Komarnicki
    Matt Komarnicki almost 9 years
    It's completely weird. I have exactly opposite situation. $('article#story').data('current', marker_data.id); works while $('article#story').attr('data-current', marker_data.id); doesn't behave correctly. :/
  • andyb
    andyb almost 9 years
    @slick which version of jQuery are you using? Maybe something changed since I wrote the answer
  • Matt Komarnicki
    Matt Komarnicki almost 9 years
    @andyb, jQuery v2.1.1
  • andyb
    andyb almost 9 years
    @slick I'll update my demo soon to be a bit smarter but I cannot see any problems with newer jQuery. It's not clear from your comment what is working or not for you. Setting via .attr will not update the jQuery object.
  • SuperUberDuper
    SuperUberDuper over 8 years
    @JoeBrockhaus so what do you do if your css depends on the Dom changing?
  • JoeBrockhaus
    JoeBrockhaus over 8 years
    @SuperUberDuper This was a while ago so my context is limited now, but I think the answer to that you have to know when that is the case, and then in addition to using jQuery.data() (which doesn't modify DOM) you have to also update the DOM element attribute so that the content: attr(data-xxx); CSS picks it up.
  • Aleks
    Aleks about 8 years
    Thanks. This helped, among all other answers with incorrect examples! The data in attr('data-helptext' makes the difference, where the accepted answer and the ones with many votes are not working. +1
  • drooh
    drooh over 7 years
    For me I was able to change the data property with data() but I did notice in developer tools it did not show the change, so for that reason I went with attr()