jQuery的.data()不起作用,但是.attr()可以。

128

很抱歉我没有更详细的描述。我有一个很奇怪的bug。文档加载后,我循环一些初始带有data-itemname=""属性的元素,并使用.attr("data-itemname", "someValue")设置这些值。

问题:当以后循环这些元素时,如果执行elem.data().itemname,我得到的是"",但如果执行elem.attr("data-itemname"),我会得到"someValue"。这就像jQuery的.data()获取器只能获取最初被设置为包含某个值的元素,但如果它们最初为空,然后稍后设置,那么.data()无法在以后获取该值。

我一直在尝试重现这个bug,但一直没成功。

编辑

我已经重新创建了这个bug!http://jsbin.com/ihuhep/edit#javascript,html,live

此外,以上链接的片段...

JS:

var theaters = [
    { name: "theater A", theaterId: 5 },
    { name: "theater B", theaterId: 17 }
];

$("#theaters").html(
    $("#theaterTmpl").render(theaters)
);

// DOES NOT WORK - .data("name", "val") does NOT set the val
var theaterA = $("[data-theaterid='5']");
theaterA.find(".someLink").data("tofilllater", "theater5link"); // this does NOT set data-tofilllater
$(".someLink[data-tofilllater='theater5link']").html("changed link text"); // never gets changed

// WORKS - .attr("data-name", "val") DOES set val
var theaterB = $("[data-theaterid='17']");
theaterB.find(".someLink").attr("data-tofilllater", "theater17link"); // this does set data-tofilllater
$(".someLink[data-tofilllater='theater17link']").html("changed link text");

HTML:

<body>
    <div id="theaters"></div>
</body>

<script id="theaterTmpl" type="text/x-jquery-tmpl">
    <div class="theater" data-theaterid="{{=theaterId}}">
        <h2>{{=name}}</h2>
        <a href="#" class="someLink" data-tofilllater="">need to change this text</a>
    </div>
</script>

4
请更加努力地尝试重现这个 bug :-) - dkamins
1
难道不应该是 elem.data("itemname") 而不是 elem.data().itemname 吗? - Hogan
虽然 elem.data().itemname 可以让你读取值,但它不能让你设置值。因此,elem.data().itemname = somevalue; 不会改变底层数据。 - Hogan
@dkamins - 我已经重新制造了这个bug,请查看编辑后的版本。 - Ian Davis
7个回答

240

几天前,我在使用 .data().attr('data-name') 处理 HTML5 数据属性时遇到了类似的“Bug”。

你所描述的行为并不是 Bug,而是设计如此。

.data() 调用比较特殊 - 它不仅检索 HTML5 数据属性,而且尝试对这些属性进行评估/解析。因此,对于类似 data-myjson='{"hello":"world"}' 的属性,通过 .data() 检索会返回一个 Object,而通过 .attr() 检索则会返回一个字符串。参见jsfiddle 示例.

由于 .data() 做了额外的处理,jQuery 在 $.cache 中存储了属性评估的结果。毕竟,一旦数据属性被评估,每次调用 .data() 都重新评估是很浪费的,特别是因为数据变量可能包含复杂的 JSON 字符串。

我说这些是为了说明以下内容:通过 .data() 检索属性后,通过 .attr('data-myvar', '') 进行的任何更改将无法被随后的 .data() 调用看到。 可以在jsfiddle 上测试这一点.

为避免这个问题,不要混合使用 .data().attr() 调用。 使用其中一个即可。


请查看此测试以了解使用 .data() vs. .attr() 的所有可能情况,其中包括使用 each 进行获取和设置,并在设置后输出选择器的长度。http://jsbin.com/acegef/edit#javascript,html,live - Ian Davis
9
至少这解释了看似不符合直觉的行为...但这可能会在使用第三方库时引起一些小麻烦,因为其中一些仅使用data(),而其他则更改了实际属性。 - Haroldo_OK
1
@leepowers,“通过.data()检索属性后,通过.attr('data-myvar', '')进行的任何更改都不会被后续的.data()调用看到”,如果这真的是因为缓存($.cache)的原因,那么似乎不太好!与其盲目依赖缓存,后续的.data()调用可以进行一些基本的检查(如当前值是否为空或字符串长度是否发生变化),以匹配缓存和当前值。 - MinhajulAnwar
1
应该注意,如果之前没有检索到值,则尝试设置 data('id', val) 也会失败... Jquery 的 data() 函数的设计非常出色。 - andreszs
3
所以,就是 data() 缓存让我卡了好几个小时。难怪无论我怎么改变它的值,它总是返回相同的数值。谢谢你的帮助。 - Lynnell Neri
显示剩余2条评论

26

这是一个误解的结果:data不是data-*属性的访问器。它比那更多,但也更少。

data是 jQuery 在元素上数据缓存的访问器。如果存在data-*属性,该缓存将从中进行初始化,但data永远不会进行写入属性,也不会在初始化后更改属性来更改数据缓存:

const div = $("[data-example]");
console.log('div.data("example"):', div.data("example"));
console.log('div.attr("data-example"):', div.attr("data-example"));
console.log('Using div.data("example", "updated")');
div.data("example", "updated");
console.log('div.data("example"):', div.data("example"));
console.log('div.attr("data-example"):', div.attr("data-example"));
<div data-example="initial value"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

data 也会以各种方式处理它发现的数据,猜测数据类型,在使用 data-answer="42" 属性的元素上进行 data("answer") 操作时,将其解析为数字而不是字符串,并且如果看起来像 JSON,则甚至会将其解析为 JSON:

console.log(typeof $("[data-answer]").data("answer"));
console.log(typeof $("[data-json]").data("json"));
console.log(typeof $("[data-str]").data("str"));
<div data-answer="42"></div>
<div data-json='{"answer":42}'></div>
<div data-str="example"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

如果你想使用属性(包括读取和设置它们),请使用attr,而不是dataattr是属性访问器。


21

.attr("data-itemname", "someValue") 修改DOM。

.data("itemname", "someValue") 修改jQuery缓存。

要在以下JavaScript中使其工作,并且还要在CSS中添加,您必须同时设置两者。

theaterA.find(".someLink").attr("data-itemname", "someValue");
theaterA.find(".someLink").data("itemname", "someValue");

找到了解决方法!好答案。 - Nabeel Khan

6
那是因为属性的名称是data-itemname,你不能在简写的obj.attribute符号中使用-obj.data-itemname会被解释为“obj.data减去itemname”)。

谁说的?能提供链接吗? - jahrichie

4

为什么不在所有地方都使用.data()呢?

您还可以在HTML中内联声明默认值,这也是可以的。

<span data-code="pony">text</span>

并且

$("span").data("code") == "pony" // true

如果您想更改它,只需执行以下操作

$("span").data("code", "not-a-pony");

如果要完全删除它,您可以调用以下命令:

$("span").removeData("code");

您应该尽量避免使用.attr("data-*"),我不知道为什么您想这样做。


但是,如果没有使用jQuery,您不是必须使用getAttribute()来完成它吗?(使用attr()的方式) - powerbuoy
@powers1 当我说“按照它应该使用”的方式使用它”时,那就是我的意思。 - bevacqua
如果您执行 .data("code", 555),然后尝试使用 $("[data-code='555']") 选择这些元素,那么该选择器的长度将为零。这在我的 jsbin 链接中有说明(在原始问题中进行了编辑)。谢谢。 - Ian Davis
1
不要那样选择元素。这样非常低效,因为它将必须遍历文档中的每个元素。使用class代替,因为浏览器具有获取具有特定类的元素的本机函数。 - ThiefMaster
1
但是,“555”是数据,所以我应该使用data()逻辑。将该数据放入类名中会混淆数据和表示层。也许有不同的做法。 - Ian Davis
显示剩余7条评论

1

您必须使用.data('itemname', 'someValue');来设置数据。使用.attr()来设置数据属性是行不通的:http://jsfiddle.net/ThiefMaster/YHsKx/

但是,您可以通过在标记中使用例如<div data-key="value">来提供内联值。


@Ian,不是的。.data() 调用设置属性,而 .attr() 调用则无效。 - bevacqua
@IanDavis:点击“设置属性”,“获取”将会给你一个空对象。只有“设置数据”有效。 - ThiefMaster
@Nico - 这正是我所做的:(1)单击“设置属性”按钮,然后(2)单击“获取”按钮。这是发生的事情:它向我发出警报,内容为“{"test":"meow"}"。因此,.attr()设置了属性。否则,警报将为空。如果你按照这些确切的步骤,是否会有不同的结果呢?谢谢。 - Ian Davis
@ThiefMaster - 请查看我的先前评论。我是否有什么做错了?我正在使用Chrome。感谢大家的帮助。 - Ian Davis
1
@ThiefMaster - еңЁжҲ‘жҸҗдҫӣзҡ„д»Јз ҒдёӯпјҢжҲ‘еңЁеү§йҷўAдёӯж №жң¬жІЎжңүдҪҝз”Ё.attr()пјҢеҸӘз”ЁдәҶ.data()гҖӮиҖҢдё”пјҢйҖүжӢ©еҷЁ$(".someLink[data-tofilllater='theater5link']")зҡ„й•ҝеәҰжҳҜйӣ¶гҖӮжүҖд»ҘжҲ‘еҘҪеғҸеҝ…йЎ»дҪҝз”Ё.attr() :/ - Ian Davis
显示剩余3条评论

0

我可以看到这引起了一些关于如何处理数据属性设置的分歧。

我也遇到了这个问题,我发现问题似乎只是数据属性名称格式的问题。

根据我的经验,您应该避免在数据变量(紧跟“data-”后面的变量名)中使用连字符。

这对我没有用:

[标记]

<div class="list" id="myElement1" data-item-order="some-value"> .... </div>

[jQuery]

jQuery("#myElement1").data("item-order", "my-new-value");

但是以下代码完美运行! :) :

(当需要时,我使用下划线代替连字符)

[标记]

<div class="list" id="myElement1" data-item_order="some-value"> .... </div>

[jQuery]

jQuery("#myElement1").data("item_order", "my-new-value");

希望能对大家有所帮助。 祝大家好运!


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