Não foi possível definir o atributo de dados usando a API jQuery Data ()

131

Eu tenho o seguinte campo em uma exibição MVC:

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

Em um arquivo js separado, desejo definir o data-helptextatributo como um valor de sequência. Aqui está o meu código:

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

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

A alert()chamada funciona bem, mostra o texto "Texto antigo" em uma caixa de diálogo de alerta. No entanto, a chamada para definir o data-helptextatributo como "Testando 123" não funciona. "Texto antigo" ainda é o valor atual do atributo.

Estou usando a chamada para dados () incorretamente? Eu procurei isso na web e não consigo ver o que estou fazendo de errado.

Aqui está a marcação HTML:

<input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />
Jason Evans
fonte
O código parece bom. Não há problema nesta demonstração . Qual versão do jQuery você está usando?
andyb
Estou usando o 1.5.1, que veio com o modelo de projeto do ASP NET MVC. Será que eu preciso atualizar o jQuery?
26611 Jason Evans
OK, não é a versão do jQuery então. Eu estava pensando que poderia ser uma versão muito antiga. A API data () que você está usando foi adicionada na v1.2.3
andyb
Você poderia adicionar a marcação, por favor? Você está usando um data-atributo HTML5 personalizado ?
26611 Andy
Como você está observando o valor? O jQuery não persiste o valor de volta ao DOM, embora o atualize corretamente. Veja minha resposta abaixo para um teste e explicação
andyb

Respostas:

239

É mencionado na .data()documentação

Os atributos de dados são extraídos na primeira vez que a propriedade de dados é acessada e, em seguida, não são mais acessados ​​ou alterados (todos os valores de dados são armazenados internamente no jQuery)

Isso também foi abordado no Por que as alterações no jQuery $ .fn.data () atualizam os atributos correspondentes do html 5 data- *?

A demonstração da minha resposta original abaixo parece não funcionar mais.

Resposta atualizada

Novamente, a partir da .data()documentação

O tratamento de atributos com traços incorporados foi alterado no jQuery 1.6 para estar em conformidade com a especificação W3C HTML5.

Então, para <div data-role="page"></div>o seguinte é verdade$('div').data('role') === 'page'

Tenho certeza de que $('div').data('data-role')funcionou no passado, mas isso não parece mais ser o caso. Criei uma vitrine melhor que registra em HTML, em vez de ter que abrir o console, e adicionei um exemplo adicional da conversão de atributos de dados multi-hífen para camelCase .

Demonstração atualizada (25-07-2015)

Veja também 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();

Demo mais antiga


Resposta original

Para este HTML:

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

e esse JavaScript (com 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;
});

Ver demonstração

Usando o Chrome DevTools Console para inspecionar o DOM, $('#foo').data('helptext', 'Testing 123'); ele não atualiza o valor como visto no console, mas $('#foo').attr('data-helptext', 'Testing 123');sim.

andyb
fonte
1
Não sei o que mudou, mas sua demo retorna violino indefinido em cromo consola
manubkk
Então, qual é o objetivo do jQuery permitindo que você coloque um segundo argumento? Para atualizar o valor armazenado no cache da variável js?
ahnbizcad
@ gwho Não sei se entendi completamente sua pergunta, mas presumo que você esteja se referindo à resposta original de 2011 usando o jQuery 1.6.2. Se sim, então o. data('key', 'value')método faz atualizar o valor no cache jQuery, mas por motivos de desempenho (eu acho mutação DOM) do próprio DOM não é atualizado.
18720 Andyb
2
portanto, se você deseja atualizar o DOM, precisará fazer isso .attr('key','value')independentemente de fazer .data('key', 'value')ou não, certo? Isso me parece redundante, e estou tendo problemas para imaginar um cenário em que você deseje gravar no DOM em cache, mas não no DOM real. Talvez eu não esteja entendendo o cache do jQuery; Então, um visitante veria todas as coisas que são .data()modificadas na tela ou não?
ahnbizcad
1
Portanto, não é apenas uma questão de desempenho; eles não podem ser comparados. Eles têm finalidades completamente diferentes e alteram "versões" diferentes do DOM. Então, voltando à pergunta: qual é o sentido de usar .data () se você precisar fazer .attr () para alterar o DOM ACUTAL? parece redundante.
ahnbizcad
34

Eu estava tendo sérios problemas com

.data('property', value);

Não estava definindo o data-propertyatributo.

Começou a usar jQuery .attr():

Obtenha o valor de um atributo para o primeiro elemento no conjunto de elementos correspondentes ou defina um ou mais atributos para cada elemento correspondente.

.attr('property', value)

para definir o valor e

.attr('property')

para recuperar o valor.

Agora simplesmente funciona!

Leniel Maccaferri
fonte
1
Para mim, eu era capaz de alterar a propriedade de dados com dados (), mas eu notei em ferramentas de desenvolvimento que não mostrar a mudança, por isso, por essa razão eu fui com attr ()
drooh
8

A resposta aceita do @yyb tem um pequeno erro. Além do meu comentário em seu post acima ...

Para este HTML:

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

Você precisa acessar o atributo assim:

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

mas o método de dados como este:

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

A correção acima para o método .data () impedirá "indefinido" e o valor dos dados será atualizado (enquanto o HTML não)

O objetivo do atributo "data" é vincular (ou "vincular") um valor ao elemento Muito parecido com o onclick="alert('do_something')"atributo, que liga uma ação ao elemento ... o texto é inútil, você só quer que a ação funcione quando eles clicarem no elemento.

Depois que os dados ou ações são vinculados ao elemento, geralmente * não há necessidade de atualizar o HTML, apenas os dados ou o método, pois é isso que seu aplicativo (JavaScript) usaria. Em termos de desempenho, não vejo por que você também desejaria atualizar o HTML, ninguém vê o atributo html (exceto no Firebug ou em outros consoles).

Uma maneira de pensar sobre isso: o HTML (junto com os atributos) são apenas texto. Os dados, funções, objetos etc. usados ​​pelo JavaScript existem em um plano separado. Somente quando o JavaScript é instruído a fazê-lo, ele lê ou atualiza o texto HTML, mas todos os dados e funcionalidades que você cria com JavaScript agem completamente separados do texto / atributos HTML que você vê no console do Firebug (ou outro).

* Eu enfatizo geralmente porque, se você tem um caso em que precisa preservar e exportar HTML (por exemplo, algum tipo de editor de texto com micro-formato / reconhecimento de dados) em que o HTML será carregado em outra página, talvez seja necessário atualizar o HTML também.

Frank Forte
fonte
Obrigado. Isso ajudou, entre todas as outras respostas, com exemplos incorretos! O datain attr('data-helptext'faz a diferença, onde a resposta aceita e aquelas com muitos votos não estão funcionando. +1
Aleks
6

Aconteceu o mesmo comigo. Acontece que

var data = $("#myObject").data();

fornece um objeto não gravável. Eu o resolvi usando:

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

E a partir de então, dataera um objeto JS gravável e padrão.

Nico
fonte
Como você escreve para ele?
Works for a Living
Desculpe Thom, não sei o que você quer dizer ... Depois de fazer $.extend..., você pode usar datao que quiser: data.name = 'Nico'; data.questionSolved = true;e console.log(data)exibirá as propriedades adicionadas recentemente
Nico
3

Para citar uma citação:

Os atributos de dados são extraídos na primeira vez que a propriedade de dados é acessada e, em seguida, não são mais acessados ​​ou alterados (todos os valores de dados são armazenados internamente no jQuery).

.data() - Documentação de jQuery

Observe que essa limitação (francamente ímpar ) é retida apenas para o uso de .data().

A solução? Use em .attrvez disso.

Obviamente, muitos de vocês podem se sentir desconfortáveis ​​por não usar seu método dedicado. Considere o seguinte cenário:

  • O 'padrão' é atualizado para que a parte de dados dos atributos personalizados não seja mais necessária / substituída

Bom senso - Por que eles mudariam um atributo já estabelecido como esse? Basta imaginar classcomeçar renomeado para grupo e idde identificador . A Internet quebraria.

E mesmo assim, o próprio Javascript tem a capacidade de corrigir isso - e, é claro, apesar da infame incompatibilidade com o HTML, o REGEX (e uma variedade de métodos semelhantes) pode renomear rapidamente seus atributos para esse novo mítico 'padrão'.

TL; DR

alert($(targetField).attr("data-helptext"));
Super Cat
fonte
1

Como mencionado, o .data()método não definirá realmente o valor do data-atributo, nem lerá valores atualizados se o data-atributo for alterado.

Minha solução foi estender o jQuery com um .realData()método que realmente corresponde ao valor atual do atributo:

// 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);

OBSERVAÇÃO: Claro que você pode usar .attr(), mas, pela minha experiência, a maioria dos desenvolvedores (eu) cometem o erro de visualizar .attr()e .data()são intercambiáveis, e geralmente substituem um pelo outro sem pensar. Pode funcionar na maioria das vezes, mas é uma ótima maneira de introduzir bugs, especialmente quando se lida com qualquer tipo de ligação de dados dinâmicos. Portanto, usando .realData(), posso ser mais explícito sobre o comportamento pretendido.

Yarin
fonte
0

Teve o mesmo problema. Como você ainda pode obter dados usando o método .data (), você só precisa descobrir uma maneira de gravar nos elementos. Este é o método auxiliar que eu uso. Como a maioria das pessoas disse, você terá que usar .attr. Eu tenho que substituir qualquer _ por - como eu sei que faz isso. Eu não estou ciente de nenhum outro personagem que ele substitua ... no entanto, eu não pesquisei isso.

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: 1/5/2017

Eu descobri que ainda havia casos em que você não conseguia obter os dados corretos usando métodos internos, então o que eu uso agora é o seguinte:

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;
};
Matthew Pautzke
fonte