Nie można ustawić atrybutu danych za pomocą interfejsu API jQuery Data ()

131

Mam następujące pole w widoku MVC:

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

W oddzielnym pliku js chcę ustawić data-helptextatrybut na wartość ciągu. Oto mój kod:

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

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

alert()Wezwanie działa dobrze, to pokazuje tekst „stary tekst” w oknie alertu. Jednak wywołanie ustawienia data-helptextatrybutu na „Testowanie 123” nie działa. „Stary tekst” jest nadal aktualną wartością atrybutu.

Czy używam wywołania data () nieprawidłowo? Sprawdziłem to w sieci i nie widzę, co robię źle.

Oto znaczniki HTML:

<input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />
Jason Evans
źródło
Kod wygląda dobrze. Nie ma problemu z tym demo . Której wersji jQuery używasz?
andyb
Używam wersji 1.5.1 dostarczonej z szablonem projektu ASP NET MVC. Czy to możliwe, że muszę zaktualizować jQuery?
Jason Evans
OK, to nie jest wersja jQuery. Myślałem, że to może być naprawdę stara wersja. Interfejs API data (), którego używasz, został dodany w wersji 1.2.3
andyb
Czy mógłbyś dodać znaczniki? Czy używasz niestandardowego data-atrybutu HTML5 ?
andyb
Jak obserwujesz wartość? jQuery nie utrwala wartości z powrotem do modelu DOM, chociaż poprawnie ją aktualizuje. Zobacz moją odpowiedź poniżej na test i wyjaśnienie
andyb

Odpowiedzi:

239

Jest o tym mowa w .data()dokumentacji

Atrybuty data- są pobierane przy pierwszym dostępie do właściwości danych, a następnie nie są już dostępne ani mutowane (wszystkie wartości danych są następnie przechowywane wewnętrznie w jQuery)

Zostało to również omówione w artykule Dlaczego zmiany w jQuery $ .fn.data () nie aktualizują odpowiednich atrybutów html 5 data- *?

Demo mojej oryginalnej odpowiedzi poniżej nie wydaje się już działać.

Zaktualizowana odpowiedź

Znowu z .data()dokumentacji

Sposób traktowania atrybutów z osadzonymi myślnikami został zmieniony w jQuery 1.6, aby był zgodny ze specyfikacją W3C HTML5.

Tak więc, <div data-role="page"></div>co następuje$('div').data('role') === 'page'

Jestem całkiem pewien, że to $('div').data('data-role')działało w przeszłości, ale wydaje się, że już tak nie jest. Stworzyłem lepszą prezentację, która loguje się do HTML, zamiast otwierać konsolę i dodałem dodatkowy przykład konwersji wielu łączników do danych atrybutów camelCase .

Zaktualizowane demo (25.07.2015)

Zobacz także 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();

Starsze demo


Oryginalna odpowiedź

W przypadku tego kodu HTML:

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

i ten JavaScript (z 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;
});

Zobacz demo

Używając konsoli Chrome DevTools do inspekcji DOM, nie aktualizuje wartości, jak widać w konsoli, ale tak.$('#foo').data('helptext', 'Testing 123'); $('#foo').attr('data-helptext', 'Testing 123');

andyb
źródło
1
Nie jestem pewien, co się zmieniło, ale twoje demo skrzypiec zwraca undefined na konsoli Chrome
manubkk
Więc jaki jest sens jQuery, pozwalając ci wstawić drugi argument? Aby zaktualizować wartość przechowywaną w pamięci podręcznej zmiennych js?
ahnbizcad
@gwho Nie jestem pewien, czy całkowicie rozumiem Twoje pytanie, ale zakładam, że odnosisz się do oryginalnej odpowiedzi z 2011 r. przy użyciu jQuery 1.6.2. Jeśli tak, to. data('key', 'value')Metoda nie aktualizuje wartość w pamięci podręcznej jQuery, ale ze względu na wydajność (chyba mutację DOM) sam DOM nie jest aktualizowana.
andyb
2
więc jeśli chcesz zaktualizować DOM, musisz to zrobić .attr('key','value')niezależnie od tego, czy to zrobiłeś, .data('key', 'value')czy nie, prawda? Wydaje mi się to zbędne i mam problem z wyobrażeniem sobie scenariusza, w którym chciałbyś pisać do pamięci podręcznej DOM, ale nie do prawdziwego DOM. Może nie rozumiem cache jQuery; Czy zatem odwiedzający zobaczyłby wszystkie zmiany .data()na ekranie, czy też nie?
ahnbizcad
1
Więc to nie tylko kwestia wydajności; nie można ich porównać. Mają zupełnie inne cele i zmieniają różne „wersje” DOM. Wracając do pytania: jaki jest sens używania .data (), jeśli musisz zrobić .attr (), aby zmienić ACUTAL DOM? wydaje się zbędny.
ahnbizcad
34

Miałem poważne problemy z

.data('property', value);

To nie było ustawianie data-propertyatrybutu.

Zaczęto używać jQuery .attr():

Uzyskaj wartość atrybutu dla pierwszego elementu w zestawie dopasowanych elementów lub ustaw jeden lub więcej atrybutów dla każdego dopasowanego elementu.

.attr('property', value)

aby ustawić wartość i

.attr('property')

aby pobrać wartość.

Teraz to po prostu działa!

Leniel Maccaferri
źródło
1
U mnie mogłem zmienić właściwość data za pomocą data (), ale zauważyłem w narzędziach programistycznych, że nie pokazuje zmiany, więc z tego powodu poszedłem z attr ()
drooh
8

Zaakceptowana odpowiedź @ andyb zawiera mały błąd. W nawiązaniu do mojego komentarza do jego postu powyżej ...

W przypadku tego kodu HTML:

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

Musisz uzyskać dostęp do atrybutu w następujący sposób:

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

ale metoda danych taka:

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

Powyższa poprawka dla metody .data () zapobiegnie „undefined”, a wartość danych zostanie zaktualizowana (podczas gdy HTML nie będzie)

Celem atrybutu „data” jest wiązanie (lub „łączenie”) wartości z elementem. Bardzo podobny do onclick="alert('do_something')"atrybutu, który wiąże akcję z elementem ... tekst jest bezużyteczny, po prostu chcesz, aby akcja zadziałała po kliknięciu elementu.

Po powiązaniu danych lub czynności z elementem zwykle * nie ma potrzeby aktualizowania kodu HTML, a jedynie dane lub metodę, ponieważ tego właśnie użyłaby Twoja aplikacja (JavaScript). Jeśli chodzi o wydajność, nie rozumiem, dlaczego miałbyś chcieć również aktualizować HTML, nikt nie widzi atrybutu html (z wyjątkiem Firebuga lub innych konsol).

Jeden ze sposobów, w jaki możesz o tym pomyśleć: HTML (wraz z atrybutami) to tylko tekst. Dane, funkcje, obiekty itp. Używane przez JavaScript istnieją na osobnej płaszczyźnie. Tylko wtedy, gdy JavaScript zostanie poinstruowany, aby to zrobić, odczyta lub zaktualizuje tekst HTML, ale wszystkie dane i funkcje utworzone za pomocą JavaScript działają całkowicie niezależnie od tekstu / atrybutów HTML, które widzisz w konsoli Firebug (lub innej).

* Zwykle kładę nacisk na to, że jeśli masz przypadek, w którym musisz zachować i wyeksportować HTML (np. Jakiś rodzaj mikro-formatu / edytora tekstu obsługującego dane), gdzie HTML załaduje się świeżo na innej stronie, może potrzebujesz zaktualizować HTML też.

Frank Forte
źródło
Dzięki. Pomogło to wśród wszystkich innych odpowiedzi z nieprawidłowymi przykładami! dataW attr('data-helptext'czyni różnicę, jeżeli odpowiedź zaakceptowane, a te z wielu głosów nie działają. +1
Aleks
6

Zdarzyło mi się to samo. Okazało się, że

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

daje obiekt, którego nie można zapisać. Rozwiązałem to używając:

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

Od tamtej pory databył to standardowy, zapisywalny obiekt JS.

Nico
źródło
Jak więc do niego piszesz?
Works for a Living
Przepraszam Thom, nie wiem, co masz na myśli ... Po wykonaniu tej czynności $.extend...możesz użyć, datajak chcesz:, data.name = 'Nico'; data.questionSolved = true;i console.log(data)wyświetli te nowo dodane właściwości
Nico
3

Aby zacytować wycenę:

Atrybuty data- są pobierane przy pierwszym dostępie do właściwości data, a następnie nie są już dostępne ani mutowane (wszystkie wartości danych są następnie przechowywane wewnętrznie w jQuery).

.data() - Dokumentacja jQuery

Zauważ, że to (szczerze dziwne ) ograniczenie jest powstrzymywane tylko w przypadku używania .data().

Rozwiązanie? Użyj .attrzamiast tego.

Oczywiście wielu z was może czuć się nieswojo, nie korzystając z dedykowanej metody. Rozważ następujący scenariusz:

  • „Standard” jest aktualizowany, aby część danych atrybutów niestandardowych nie była już wymagana / została zastąpiona

Zdrowy rozsądek - dlaczego mieliby zmieniać już ustalony atrybut w ten sposób? Wyobraź sobie, że classzaczynasz przemianować się na grupę i idna identyfikator . Internet się załamie.

I nawet wtedy sam Javascript może to naprawić - I oczywiście, pomimo niesławnej niezgodności z HTML, REGEX (i wiele innych podobnych metod) może szybko zmienić nazwy atrybutów na ten nowy mityczny „standard”.

TL; DR

alert($(targetField).attr("data-helptext"));
Super Cat
źródło
1

Jak wspomniano, .data()metoda w rzeczywistości nie ustawi wartości data-atrybutu ani nie odczyta zaktualizowanych wartości, jeśli data-atrybut się zmieni.

Moim rozwiązaniem było rozszerzenie jQuery o .realData()metodę, która faktycznie odpowiada aktualnej wartości atrybutu:

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

UWAGA: Jasne, że możesz po prostu użyć .attr(), ale z mojego doświadczenia wynika, że ​​większość programistów (aka ja) popełnia błąd, przeglądając .attr()i .data()jako wymienne, i często zastępuje jeden za drugim bez zastanowienia. Może to działać przez większość czasu, ale jest to świetny sposób na wprowadzenie błędów, szczególnie w przypadku wszelkiego rodzaju dynamicznych powiązań danych. Więc używając .realData(), mogę bardziej szczegółowo opisać zamierzone zachowanie.

Yarin
źródło
0

Miałem ten sam problem. Ponieważ nadal możesz pobierać dane za pomocą metody .data (), musisz tylko znaleźć sposób zapisu do elementów. To jest metoda pomocnicza, której używam. Jak powiedziała większość ludzi, będziesz musiał użyć .attr. Mam zamianę dowolnego _ na - jak wiem, robi to. Nie znam żadnych innych postaci, które zastępuje ... jednak nie badałem tego.

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]);
    }
}

EDYCJA: 01.05.2017

Okazało się, że nadal istnieją przypadki, w których nie można uzyskać poprawnych danych za pomocą wbudowanych metod, więc teraz używam:

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
źródło