为什么修改jQuery $.fn.data()不会更新相应的html 5 data-*属性?

33

这里有一个简单的示例来说明行为:

给定以下 HTML 标记:

<div data-company="Microsoft"></div>

并且这段jQuery代码(使用jQuery 1.5.1):

// read the data
alert($("div").data("company"));
// returns Microsoft <<< OK!

// set the data
$("div").data("company","Apple");
alert($("div").data("company"));
// returns Apple <<< OK!

// attribute selector
alert($("div[data-company='Apple']").length);
// returns 0  <<< WHY???

// attribute selector again
alert($("div[data-company='Microsoft']").length);
// returns 1  <<< WHY???

// set the attribute directly
$("div").attr("data-company","Apple");
alert($("div[data-company='Apple']").length);
// now returns 1 <<< OK!

既然jQuery自动将HTML5 data-*导入到jQuery的data对象中,那么当数据发生变化时,属性也应该被更新,对吗?


1
@James "div[data-company='Apple'" - 你忘记了加上 ] 来闭合。 - Šime Vidas
3个回答

51

通常情况下,如果您在访问/设置/修改DOM元素上的数据时始终使用.data(),则不需要进行往返操作。因此,避免每次.data()设置/修改操作都访问DOM的性能开销是有意义的(.data()在内部将其值存储在jQuery.cache中)。

如果您想强制执行往返行为,可以订阅"setData"或"changeData"事件,然后通过.attr()将这些事件中的.data()更新传递到相应的DOM元素。


2
有道理 - 那么在选择器的上下文中使用 .data() 的首选方法是什么?比如我想访问所有 data-company='Microsoft' 的元素,这些元素是通过 .data() 设置/修改的,应该怎么做? - James H
1
我认为没有一种简单的方法可以针对.data()缓存进行查询。如果您想要使用这样的选择器,您需要在“changeData”事件上实现往返操作,就像我之前提到的那样。然后,您可以将您的数据属性与.data()更改保持同步(并且您还可以仅选择性地在稍后要查询的元素上执行此操作)。 - Dave Ward
谢谢你,Dave!顺便说一下,我很喜欢你在Tekpub上的系列。 - James H
谢谢,很高兴听到这个消息。如果你还没有看完,我们在第10或11集中讨论了如何处理“changeData”事件,这对这里会有所帮助。 - Dave Ward
jQuery中最大的陷阱:使用.data()在新元素上设置数据,然后将其添加到DOM中,然后从DOM中检索它,然后发现没有数据。文档没有足够地强调在此情况下必须使用.attr('data-...') - Roman Starkov

17

根据文档,这是正确的行为:

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

(来源:http://api.jquery.com/data)


这一直是这样吗?还是在某个版本中进行了更改? - John Magnolia
1
看起来这一直是这样的。上面的引用来自文档,也在1.4.3版本首次发布时的发行说明中提到:http://blog.jquery.com/2010/10/16/jquery-143-released/ - Craig

0

[data-company] 的查询选择器检查属性,而 .data 不会更新它们。

您可以更改代码,仅使用 .attr('data-,完全避免使用 .data

您可以设置自己的函数来更新数据和属性:

// update both data and corresponding attribute 'data-x'
$.fn.attrdata = function (a, b)
{
    if (arguments.length > 1)
        this.attr('data-' + a, b);
    else if (typeof a === 'object')
        this.attr(Object.keys(a).reduce(function (obj, key)
        {
            obj['data-' + key] = a[key];
            return obj;
        }, {}));
    return this.data.apply(this, arguments);
};

使用方法如下:

$("div").attrdata("company", "Apple");
$("div").attrdata({company: "Apple"}); // also possible
console.log($("div").data("company")); // Apple
console.log($("div").attr("data-company")); // Apple
console.log($("div[data-company='Apple']").length); // 1

如果您不使用CSS选择器,可以创建自己的jQuery选择器:

$.expr[':'].data = function(elem, index, match) {
  var split = match[3].split('=');
  return $(elem).data(split[0]) == split[1];
};

使用方法如下:

$("div").attr("data-company", "Microsoft");
$("div").data("company", "Apple");
console.log($('div:data(company=Apple)').length); // 1
console.log($('div[data-company="Apple"]').length); // 0

https://jsfiddle.net/oriadam/a14jvqcw/


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