jQuery .data () не работает, но .attr () работает

107

Простите меня за то, что я не уточнил по этому поводу. У меня такой странный баг. После загрузки документа я зацикливаю некоторые элементы, которые изначально были data-itemname="", и устанавливаю эти значения, используя .attr("data-itemname", "someValue").

Проблема: когда я позже перебираю эти элементы, elem.data().itemnameя получаю "", если да elem.attr("data-itemname"), то получаю "someValue". Это похоже на то, что .data()геттер jQuery получает только те элементы, которые изначально заданы как содержащие какое-то значение, но если они изначально пусты, а затем установлены, то .data()не получает значение позже.

Я пытался воссоздать эту ошибку, но не мог.

редактировать

Я воссоздал ошибку! 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
Постарайтесь воссоздать ошибку :-)
dkamins 03
1
Разве это не elem.data("itemname")не elem.data().itemname?
Hogan
а elem.data (). itemname позволит вам прочитать значение, которое НЕ позволит вам установить значение. (Таким образом elem.data().itemname = somevalue;, основные данные не меняются.)
Hogan
@dkamins - Я воссоздал ошибку, смотрите отредактированную версию.
Ян Дэвис,

Ответы:

211

Несколько дней назад я столкнулся с подобной «ошибкой» при работе с атрибутами данных HTML5 .data()и .attr('data-name')для них.

Поведение, которое вы описываете, не является ошибкой, а является преднамеренным.

.data()Вызов является особенным - он не только получить HTML5 атрибуты данных также попытки оценить / синтаксический анализ атрибутов. Таким образом, с таким атрибутом, как data-myjson='{"hello":"world"}'при извлечении через, .data()будет возвращена строка, Objectа при извлечении через .attr(). См. Пример jsfiddle.

Поскольку .data()дополнительная обработка, jQuery сохраняет результаты оценки атрибутов $.cache- в конце концов, после того, как атрибут данных был оценен, было бы бесполезно переоценивать при каждом .data()вызове - тем более, что переменные данных могут содержать сложные строки JSON.

Я сказал все это, чтобы сказать следующее: после получения атрибута с помощью .data()любых изменений, сделанных с помощью .attr('data-myvar', ''), не будут видны последующие .data()вызовы. Проверьте это на jsfiddle.

Чтобы избежать этой проблемы , не смешивайте .dataи .attr()звонки. Используйте то или другое.

leepowers
источник
отметив это как ответ. просмотрите этот тест для всех возможных сценариев с .data () vs. .attr (), который включает получение и настройку с использованием каждого из них и вывод длины селекторов после их установки, jsbin.com/acegef/edit# javascript, html, live
Ян Дэвис,
9
По крайней мере, это объясняет явно не интуитивное поведение ... хотя это может вызвать некоторые меньшие головные боли при использовании сторонних библиотек, некоторые из которых используют только data (), а другие меняют фактический атрибут.
Haroldo_OK
1
@leepowers, "После получения атрибута через .data () любые изменения, сделанные .attr ('data-myvar', ''), не будут видны последующими вызовами .data ()", если это действительно из-за кеша ( $ .cache), то вроде не годится! вместо того, чтобы слепо полагаться на кеш, последующие вызовы .data () могли бы выполнять некоторые базовые проверки (), например, изменилось ли значение сейчас пустое или длина строки (соответствие кеша текущему значению)
минхаджул
1
Следует отметить, что попытка установить data ('id', val) также не удалась, если значение не было получено ранее ... отличный дизайн функции data () с помощью JQuery.
andreszs
3
Итак, кеш data () держит меня часами. Неудивительно, почему, как бы я ни менял его значение, он все равно возвращает то же значение. Спасибо за это.
Линнелл Эммануэль Нери
17

Это результат недоразумения: 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")элемент с data-answer="42"числом, а не строкой, или даже анализируя вещи как 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, not data. attr - аксессуар для атрибутов.

TJ Crowder
источник
6

Это потому, что имя атрибута data-itemname. Вы не можете использовать -сокращенную obj.attributeзапись (obj.data-itemname будет интерпретироваться как «obj.data minus itemname»).

Марк Б
источник
говорит кто? можешь дать ссылку?
jahrichie
6

.attr("data-itemname", "someValue") изменяет DOM.

.data("itemname", "someValue") изменяет кеш jQuery.

Чтобы это работало в следующем Javascript и в дополнение к CSS, вы должны установить оба.

theaterA.find(".someLink").attr("data-itemname", "someValue");
theaterA.find(".someLink").data("itemname", "someValue");
Эльмар Хёфингхофф
источник
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-*"), я все равно не знаю, зачем вам это нужно.

Bevacqua
источник
s / should / must, использование .attr('data-*', ...)не сделает данные видимыми для.data()
ThiefMaster 03
Но разве это не делается с помощью attr () так, как вам пришлось бы делать это без jQuery? (с getAttribute tho)
powerbuoy
Если у вас нет jQuery, который вы используете getAttribute()и setAttribute()- значит, оба метода получат доступ к фактическим атрибутам, и он снова будет работать. Или вы бы просто использовали dataSetсвойство, которое предоставляют современные браузеры.
ThiefMaster 03
1
Не выбирайте такие элементы. Это ужасно неэффективно, так как придется перебирать каждый элемент документа. Используйте classвместо этого, поскольку в браузерах есть собственные функции для получения элементов с определенным классом.
ThiefMaster 03
1
но «555» - это данные, поэтому я должен использовать логику data (). размещение этих данных в имени класса смешивает данные с презентацией. я полагаю другой способ сделать это.
Ян Дэвис,
1

Вы должны установить данные с помощью .data('itemname', 'someValue');. Использование .attr()для установки атрибутов данных не сработает: http://jsfiddle.net/ThiefMaster/YHsKx/

Однако вы можете указать встроенные значения, используя, например, <div data-key="value">в разметке.

ThiefMaster
источник
в jsfiddle get работает после того, как вы выполните оба набора. Итак, выполнение attr ("data-itemname", "value") ДЕЙСТВИТЕЛЬНО работает. Я использую Chrome.
Ян Дэвис,
@ Ян, нет. .data()вызов устанавливает атрибут, в то время как .attr()вызов не делает ничего.
bevacqua 03
@IanDavis: нажмите «установить атрибут», и «получить» даст вам пустой объект. Работает только "набор данных".
ThiefMaster 03
@Nico - это именно то, что я сделал: (1) нажмите кнопку «установить атрибут», затем (2) нажмите кнопку «получить». вот что произошло: он предупредил меня, "{" test ":" meow "}". Итак, .attr () устанавливает атрибут. в противном случае предупреждение было бы пустым. Если вы будете следовать этим точным шагам, получите ли вы другие результаты? Спасибо.
Ян Дэвис
1
@ThiefMaster - в theatreA в моем предоставленном коде я вообще не использую .attr(), только .data(), и длина селектора $(".someLink[data-tofilllater='theater5link']")равна нулю. так что я должен использовать .attr(): /
Ян Дэвис
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");

Я надеюсь, что это помогает. Всем привет!

mmmdearte
источник