使用jQuery Data() API无法设置数据属性

131

我在MVC视图中有以下字段:

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

我想在一个单独的js文件中将data-helptext属性设置为字符串值。这是我的代码:

alert($(targetField).data("helptext"));

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

alert()函数可以正常工作,它会在一个警告对话框中显示文本"Old Text",然而调用data-helptext属性设置为"Testing 123"却不起作用。"Old Text"仍然是该属性的当前值。

我是否错误地使用了data()函数?我在网上查找了一下,但没有找到我做错了什么。

这是HTML标记:

<input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />

代码看起来没问题。这个演示没有问题。你使用的是哪个版本的jQuery? - andyb
我正在使用随 ASP NET MVC 项目模板一起提供的1.5.1版本。是不是需要更新 jQuery? - Jason Evans
好的,那就不是 jQuery 的版本问题了。我原本以为可能是一个非常旧的版本。你正在使用的 data() API 是在 v1.2.3 中添加的。 - andyb
请添加标记吗?您是否使用自定义的HTML5 data-属性? - andyb
你是如何观察这个值的?jQuery并没有将该值持久化到DOM中,尽管它确实正确地更新了它。请参见下面的答案以获取测试和解释。 - andyb
7个回答

239

.data() 文档 中提到:

第一次访问数据属性时会获取数据属性,然后不再访问或修改(所有数据值都存储在 jQuery 内部)。

这也在 为什么更改 jQuery $.fn.data() 不会更新相应的 html 5 data-* 属性? 中有所涉及。

我的原始答案中的演示似乎不再有效。

更新的答案

同样来自于 .data() 文档:

在 jQuery 1.6 中,带有嵌入破折号的属性处理方式已更改以符合 W3C HTML5 规范。

因此,对于 <div data-role="page"></div>,以下内容是正确的:$('div').data('role') === 'page'

我非常确定过去 $('div').data('data-role') 是有效的,但现在不再是这种情况。我创建了一个更好的演示,将日志记录到 HTML 中而不是必须打开控制台,并添加了一个多连字符转换为驼峰式的data- 属性的附加示例。

更新的演示(2015-07-25)

另请参见 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>

以及这个JavaScript代码(使用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;
});

查看演示

使用 Chrome 开发者工具 中的 控制台 检查 DOM,$('#foo').data('helptext', 'Testing 123'); 不能 更新值,但是 $('#foo').attr('data-helptext', 'Testing 123'); 可以。


1
不确定发生了什么变化,但您的fiddle演示在Chrome控制台中返回未定义。 - manubkk
@gwho,我不确定我完全理解你的问题,但我认为你是指2011年使用jQuery 1.6.2的原始答案。如果是这样,那么.data('key', 'value')方法确实会更新jQuery缓存中的值,但出于性能考虑(我猜测是DOM突变),DOM本身不会被更新。 - andyb
2
如果您想要更新DOM,无论您是否执行了.data('key', 'value'),都需要执行.attr('key','value'),对吗?这对我来说似乎是多余的,而且我很难想象一个场景,在这个场景中,您希望写入缓存的DOM,但不写入真实的DOM。也许我没有理解jQuery缓存;那么,访问者会在屏幕上看到所有.data()修改的内容吗,还是不会看到? - ahnbizcad
1
因此,这不仅仅是性能问题;它们无法进行比较。它们具有完全不同的目的,并且更改不同版本的DOM。那么回到问题:如果您必须使用.attr()来更改实际的DOM,那么使用.data()的意义何在?似乎多余。 - ahnbizcad
这是否也会影响新添加的元素?我克隆了一个表格行,看起来像这样:<tr data-id="5"></tr>,检索了id,递增它,然后使用$clone.data('id',new_index),然后使用$tbody.append(clone)将新TR附加到表格中。但是新的TR仍然具有data-id="5"。为什么这在新创建的元素上不起作用? - Buttle Butkus
显示剩余12条评论

35

我遇到了严重的问题:

.data('property', value);

它没有设置data-property属性。

开始使用jQuery的.attr()

获取匹配元素集合中第一个元素的属性值,或为每个匹配元素设置一个或多个属性。

.attr('property', value)

设置值和

.attr('property')

获取该值。

现在它只是正常工作!


1
对我来说,我能够使用data()更改数据属性,但是我注意到在开发者工具中它没有显示更改,因此出于这个原因,我选择了attr()。 - drooh

9

@andyb的答案被接受了,但存在一个小问题。根据我在他上面的评论中提到的...

对于这个HTML:

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

您需要像这样访问属性:

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

但是数据处理的方法就像这样:
$('#foo').data('helptext', 'Testing 123');

上述对于 .data() 方法的修复将防止 "undefined" 并更新数据值(而 HTML 不会)。
"data" 属性的作用是将一个值与元素绑定(或 "链接")。非常类似于 onclick="alert('do_something')" 属性,它将一个动作绑定到元素上... 如果您只想在单击元素时使动作起作用,那么文本就没什么用了。
一旦数据或动作绑定到元素上,通常* 只需要更新数据或方法,无需更新 HTML,因为这是你的应用程序(JavaScript)将使用的。就性能而言,我不明白为什么你还要更新 HTML,毕竟只有在 Firebug 或其他控制台中才能看到 html 属性。
你可以这样考虑: HTML(以及属性)只是文本。被 JavaScript 使用的数据、函数、对象等存在于一个独立的平面上。只有当 JavaScript 被指示这样做时,它才会读取或更新 HTML 文本,但你用 JavaScript 创建的所有数据和功能都与你在 Firebug(或其他)控制台中看到的 HTML 文本/属性完全分离。
*我强调“通常”是因为如果你有一个需要保留并导出 HTML 的情况(例如某种微格式/数据感知的文本编辑器),其中 HTML 将在另一页上重新加载,则可能也需要更新 HTML。

谢谢。这个帮助很大,尤其是在所有其他答案都有错误示例的情况下!attr('data-helptext' 中的 data 是关键所在,而被接受的答案和得到许多赞的答案都不起作用。+1 - Aleks

6
我也遇到了同样的问题。事实证明,
var data = $("#myObject").data();

该函数返回一个不可写对象。我使用以下方法解决了这个问题:

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

从那时起,data 就成为了一个标准的、可写的 JS 对象。


那么你如何向它写入数据呢? - Works for a Living
抱歉,Thom,我不知道你的意思... 在执行 $.extend... 后,您可以随心所欲地使用 datadata.name = 'Nico'; data.questionSolved = true;,并且 console.log(data) 将显示这些新添加的属性。 - Nico

3

引用一段话:

第一次访问数据属性时会调用数据属性,然后不再访问或改变数据属性(所有数据值都将在jQuery内部存储)。

.data() - jQuery文档

请注意,这个(相当奇怪的)限制仅针对使用.data()的情况。

解决方案?改用.attr

当然,你们中的一些人可能不太舒服,不想使用专门的方法。考虑以下场景:

  • “标准”更新,自定义属性的数据部分不再需要/被替换

常识-为什么他们要更改已经建立的属性呢?想象一下将class更名为group,并将id更名为identifier。互联网将被打破。

即使如此,Javascript本身也有能力解决这个问题-当然,尽管它与HTML的兼容性不佳,但REGEX(和各种类似的方法)可以快速地将您的属性重命名为这个新的神话“标准”。

TL;DR

alert($(targetField).attr("data-helptext"));

1
如上所述,.data()方法实际上不会设置data-属性的值,也不会在data-属性更改时读取更新后的值。
我的解决方案是通过扩展jQuery添加一个.realData()方法来实际对应于属性的当前值:
// 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);

注意: 当然你可以只使用.attr(),但是根据我的经验,大多数开发者(包括我)会错误地将.attr().data()视为可互换的,并且经常在不加思考的情况下将其替换。虽然它可能大部分时间都能工作,但是这样做很容易引入错误,特别是在处理任何形式的动态数据绑定时。因此,通过使用.realData(),我可以更明确地表达预期的行为。


0

我曾经遇到过同样的问题。由于您仍然可以使用.data()方法获取数据,因此您只需要想出一种写入元素的方法。这是我使用的辅助方法。像大多数人说的那样,您将不得不使用.attr。我让它将任何_替换为-,因为我知道它会这样做。我不知道它是否替换其他字符...但我没有研究过。

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]);
    }
}

编辑:2017年5月1日

我发现仍然有一些情况下,使用内置方法无法获取正确的数据,所以我现在使用的方法如下:

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;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接