jQuery .data () non funziona, ma .attr () sì


107

Perdonami per non essere più specifico su questo. Ho un bug così strano. Dopo il caricamento del documento, eseguo il ciclo di alcuni elementi che avevano originariamente data-itemname=""e imposto quei valori usando .attr("data-itemname", "someValue").

Problema: quando più tardi faccio il loop di quegli elementi, se lo faccio elem.data().itemname, ottengo "", ma se lo faccio elem.attr("data-itemname"), ottengo "someValue". È come se il .data()getter di jQuery ottenga solo elementi impostati inizialmente per contenere un valore, ma se sono originariamente vuoti e successivamente impostati, .data()non ottiene il valore in seguito.

Ho provato a ricreare questo bug ma non sono stato in grado di farlo.

modificare

Ho ricreato il bug! http://jsbin.com/ihuhep/edit#javascript,html,live

Inoltre, frammenti dal link sopra ...

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>


1
Non è elem.data("itemname")vero elem.data().itemname?
Hogan

mentre elem.data (). itemname ti permetterà di leggere il valore, NON ti permetterà di impostare il valore. (Quindi elem.data().itemname = somevalue;non cambia i dati sottostanti.)
Hogan

@dkamins - Ho ricreato il bug, vedi la versione modificata.
Ian Davis

Risposte:


210

Mi sono imbattuto in un "bug" simile qualche giorno fa quando lavoravo con .data()e .attr('data-name')per gli attributi di dati HTML5.

Il comportamento che stai descrivendo non è un bug, ma è di progettazione.

La .data()chiamata è speciale: non solo recupera gli attributi dei dati HTML5, ma tenta anche di valutare / analizzare gli attributi. Quindi con un attributo come data-myjson='{"hello":"world"}'quando recuperato tramite .data()restituirà un Objectmentre il recupero tramite .attr()restituirà una stringa. Vedi l'esempio di jsfiddle.

Poiché .data()jQuery esegue un'elaborazione aggiuntiva, memorizza i risultati della valutazione degli attributi in $.cache- dopotutto, una volta che un attributo di dati è stato valutato sarebbe uno spreco rivalutare ogni .data()chiamata, soprattutto perché le variabili di dati possono contenere stringhe JSON complesse.

Ho detto tutto questo per dire quanto segue: Dopo aver recuperato un attributo tramite .data()le modifiche apportate da .attr('data-myvar', ''), non verrà visualizzato dalle .data()chiamate successive . Provalo su jsfiddle.

Per evitare questo problema non mescolare .datae .attr()chiamare. Usa l'uno o l'altro.


contrassegnandolo come risposta. si prega di consultare questo test per tutti i possibili scenari con .data () e .attr (), che include il recupero e l'impostazione utilizzando ciascuno e l'output della lunghezza dei selettori dopo che sono stati impostati, jsbin.com/acegef/edit# javascript, html, live
Ian Davis

9
Almeno questo spiega il comportamento apparentemente non intuitivo ... anche se questo potrebbe causare alcuni piccoli mal di testa quando si utilizzano librerie di terze parti, alcune delle quali utilizzano solo data () e altre modificano l'attributo effettivo.
Haroldo_OK

1
@leepowers, "Dopo aver recuperato un attributo tramite .data () tutte le modifiche apportate da .attr ('data-myvar', '') non saranno viste dalle successive chiamate .data ()", se questo è davvero una causa della cache ( $ .cache), quindi non sembra buono! invece di affidarsi ciecamente alla cache, le successive chiamate .data () potrebbero eseguire alcuni controlli di base () come se il valore è vuoto ora o la lunghezza della stringa è cambiata (facendo corrispondere la cache con il valore corrente)
minhajul

1
Va notato che anche il tentativo di impostare i dati ('id', val) fallisce se il valore non è stato recuperato in precedenza ... ottimo design della funzione data () di Jquery.
andreszs

3
Quindi, è la cache dei dati () che mi trattiene per ore. Non c'è da stupirsi perché, indipendentemente da quanto cambio il suo valore, restituisce comunque lo stesso valore. Grazie per questo.
Lynnell Emmanuel Neri

17

Questo è il risultato di un malinteso: dataNON è una funzione di accesso per gli data-*attributi . È sia più che meno.

dataè una funzione di accesso per la cache dei dati di jQuery sull'elemento. Quella cache viene inizializzata dagli data-*attributi se ce ne sono presenti, ma datanon scrive mai negli attributi, né la modifica dell'attributo cambia la cache dei dati dopo l'inizializzazione:

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>

datamassaggia anche ciò che trova in vari modi, indovinando i tipi di dati, creando data("answer")un elemento con data-answer="42"un numero, non una stringa, o persino analizzando le cose come JSON se sembrano 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>

Se vuoi usare gli attributi (sia leggendoli che impostandoli), usa attr, not data. attr è una funzione di accesso per gli attributi.


6

Questo perché il nome dell'attributo è data-itemname. Non è possibile utilizzare -nella obj.attributenotazione abbreviata (obj.data-itemname verrebbe interpretato come "obj.data meno itemname").


dice chi? Puoi fornire un collegamento?
jahrichie

6

.attr("data-itemname", "someValue") modifica il DOM.

.data("itemname", "someValue") modifica la cache jQuery.

Per farlo funzionare seguendo Javascript e in aggiunta in CSS devi impostare entrambi.

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

4

Perché non lo usi .data()ovunque?

Puoi anche dichiarare i valori predefiniti inline sull'HTML, il che va bene.

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

e

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

se vuoi cambiarlo devi solo farlo

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

e per rimuoverlo del tutto potresti invocare

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

dovresti davvero cercare di evitare di usare .attr("data-*"), non so perché vorresti farlo comunque.


s / should / must, l'utilizzo .attr('data-*', ...)non renderà i dati visibili a.data()
ThiefMaster

Ma non è farlo con attr () nel modo in cui dovresti farlo senza jQuery? (con getAttribute tho)
powerbuoy

Se non hai jQuery usi getAttribute()e setAttribute(), quindi entrambi i metodi accederanno agli attributi effettivi e funzionerà di nuovo. Oppure utilizzeresti semplicemente la dataSetproprietà fornita dai browser moderni.
ThiefMaster

1
Non selezionare elementi del genere. È orribilmente inefficiente poiché dovrà iterare su ogni singolo elemento del documento. Usa classinvece un poiché i browser hanno funzioni native per ottenere elementi con una certa classe.
ThiefMaster

1
ma "555" è dati, quindi dovrei usare la logica data (). inserire quei dati nel nome della classe significa mescolare i dati con la presentazione. modo diverso di farlo suppongo.
Ian Davis

1

È necessario impostare i dati utilizzando .data('itemname', 'someValue');. L'utilizzo .attr()per impostare gli attributi dei dati non funzionerà: http://jsfiddle.net/ThiefMaster/YHsKx/

Tuttavia, puoi fornire valori inline utilizzando ad esempio <div data-key="value">nel markup.


in jsfiddle, get funziona dopo aver eseguito entrambi i set. quindi, facendo attr ("data-itemname", "value") FUNZIONA. Sto usando Chrome.
Ian Davis

@ Ian uh, no. la .data()chiamata imposta l'attributo, mentre la .attr()chiamata non fa nulla.
bevacqua

@IanDavis: Fai clic su "set attr" e "get" ti darà un oggetto vuoto. Funziona solo "set data".
ThiefMaster

@Nico - questo è esattamente quello che ho fatto: (1) fare clic sul pulsante "set attr", quindi (2) fare clic sul pulsante "get". ecco cosa è successo: mi ha avvisato, "{" test ":" meow "}". quindi, il .attr () imposta l'attributo. altrimenti l'avviso sarebbe stato vuoto. Se segui esattamente questi passaggi, ottieni risultati diversi? Grazie.
Ian Davis

1
@ThiefMaster - in theaterA nel codice fornito, non sto usando .attr()affatto, solo .data()e, la lunghezza del selettore $(".someLink[data-tofilllater='theater5link']"), è zero. quindi è come se dovessi usare .attr(): /
Ian Davis il

0

Vedo che questo ha sollevato alcune divisioni su come affrontare l'impostazione degli attributi dei dati.

Anch'io mi imbatto in questo problema e ho scoperto che il problema sembrava essere semplicemente la formattazione del nome dell'attributo dei dati .

Nella mia esperienza, dovresti evitare di usare trattini nella variabile data (il nome della variabile che viene dopo " data- ").

Questo non ha funzionato per me:

[Markup]

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

[JQuery]

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

Ma quanto segue ha funzionato perfettamente! :):

(Uso un trattino basso invece di un trattino quando richiesto)

[Markup]

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

[JQuery]

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

Spero possa essere d'aiuto. Salute a tutti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.