Unable to set data attribute using jQuery Data() API
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="" />
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.
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!
@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.
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.
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"));
As mentioned, the .data()
method won't actually set the value of the data-
attribute, nor will it read updated values if the data-
attribute changes.
My solution was to extend jQuery with a .realData()
method that actually corresponds to the current value of the attribute:
// Alternative to .data() that updates data- attributes, and reads their current value.
(function($){
$.fn.realData = function(name,value) {
if (value === undefined) {
return $(this).attr('data-'+name);
} else {
$(this).attr('data-'+name,value);
}
};
})(jQuery);
NOTE: Sure you could just use .attr()
, but from my experience, most developers (aka me) make the mistake of viewing .attr()
and .data()
as interchangeable, and often substitute one for the other without thinking. It might work most of the time, but it's a great way to introduce bugs, especially when dealing with any sort of dynamic data binding. So by using .realData()
, I can be more explicit about the intended behavior.
Had the same problem. Since you can still get data using the .data() method, you only have to figure out a way to write to the elements. This is the helper method I use. Like most people have said, you will have to use .attr. I have it replacing any _ with - as I know it does that. I'm not aware of any other characters it replaces...however I have not researched that.
function ExtendElementData(element, object){
//element is what you want to set data on
//object is a hash/js-object
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++){
var key = keys[i];
$(element).attr('data-'+key.replace("_", "-"), object[key]);
}
}
EDIT: 5/1/2017
I found there were still instances where you could not get the correct data using built in methods so what I use now is as follows:
function setDomData(element, object){
//object is a hash
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++){
var key = keys[i];
$(element).attr('data-'+key.replace("_", "-"), object[key]);
}
};
function getDomData(element, key){
var domObject = $(element).get(0);
var attKeys = Object.keys(domObject.attributes);
var values = null;
if (key != null){
values = $(element).attr('data-' + key);
} else {
values = {};
var keys = [];
for (var i = 0; i < attKeys.length; i++) {
keys.push(domObject.attributes[attKeys[i]]);
}
for (var i = 0; i < keys.length; i++){
if(!keys[i].match(/data-.*/)){
values[keys[i]] = $(element).attr(keys[i]);
}
}
}
return values;
};
精彩评论