No se puede establecer el atributo de datos utilizando la API jQuery Data ()

131

Tengo el siguiente campo en una vista MVC:

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

En un archivo js separado, quiero establecer el data-helptextatributo en un valor de cadena. Aquí está mi código:

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

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

La alert()llamada funciona bien, muestra el texto "Texto antiguo" en un diálogo de alerta. Sin embargo, la llamada para establecer el data-helptextatributo en "Prueba 123" no funciona. "Texto antiguo" sigue siendo el valor actual del atributo.

¿Estoy usando la llamada a datos () incorrectamente? He buscado esto en la web y no puedo ver qué estoy haciendo mal.

Aquí está el marcado HTML:

<input data-helptext="Old Text" id="Course_Title" name="Course.Title" type="text" value="" />
Jason Evans
fuente
El código se ve bien. No hay problema en esta demo . ¿Qué versión de jQuery estás usando?
andyb
Estoy usando 1.5.1 que vino con la plantilla de proyecto ASP NET MVC. ¿Podría ser que necesito actualizar jQuery?
Jason Evans
OK, entonces no es la versión de jQuery. Estaba pensando que podría ser una versión muy antigua. La API data () que está utilizando se agregó en v1.2.3
andyb
¿Podría agregar el marcado por favor? ¿Estás utilizando un data-atributo HTML5 personalizado ?
andyb
¿Cómo estás observando el valor? jQuery no conserva el valor de vuelta al DOM, aunque lo actualiza correctamente. Vea mi respuesta a continuación para obtener una prueba y una explicación
andyb

Respuestas:

239

Se menciona en la .data()documentación.

Los atributos de datos se extraen la primera vez que se accede a la propiedad de datos y luego ya no se accede ni se muta (todos los valores de datos se almacenan internamente en jQuery)

Esto también se cubrió en ¿Por qué los cambios en jQuery $ .fn.data () actualizan los atributos html 5 data- * correspondientes?

La demostración de mi respuesta original a continuación ya no parece funcionar.

Respuesta actualizada

De nuevo, de la .data()documentación.

El tratamiento de los atributos con guiones incrustados se modificó en jQuery 1.6 para cumplir con la especificación W3C HTML5.

Entonces, para <div data-role="page"></div>lo siguiente es cierto$('div').data('role') === 'page'

Estoy bastante seguro de que $('div').data('data-role')funcionó en el pasado, pero ese ya no parece ser el caso. Creé un mejor escaparate que registra en HTML en lugar de tener que abrir la consola y agregué un ejemplo adicional de la conversión de múltiples guiones a los atributos de datos de camelCase .

Demo actualizada (2015-07-25)

Ver también 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 anterior


Respuesta original

Para este HTML:

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

y este JavaScript (con 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 demo

Al usar la Consola Chrome DevTools para inspeccionar el DOM, no actualiza el valor como se ve en la Consola, pero sí lo hace.$('#foo').data('helptext', 'Testing 123'); $('#foo').attr('data-helptext', 'Testing 123');

andyb
fuente
1
No estoy seguro de qué ha cambiado, pero su demostración de violín regresa indefinida en la consola de Chrome
manubkk
Entonces, ¿cuál es el punto de jQuery que le permite poner un segundo argumento? Para actualizar el valor almacenado en la caché variable js?
ahnbizcad
@gwho No estoy seguro de entender completamente tu pregunta, pero supongo que te estás refiriendo a la respuesta original de 2011 usando jQuery 1.6.2. Si es así, entonces el. data('key', 'value')método hace actualizar el valor en la memoria caché de jQuery, pero por razones de rendimiento (supongo mutación DOM) el DOM en sí y no se actualiza.
andyb
2
así que si quisieras actualizar el DOM, deberías hacerlo .attr('key','value')independientemente de si lo hiciste .data('key', 'value')o no, ¿verdad? Eso me parece redundante, y tengo problemas para imaginar un escenario en el que desee escribir en el DOM en caché, pero no en el DOM real. Tal vez no entiendo el caché jQuery; Entonces, ¿vería un visitante todas las cosas que se .data()modifican en su pantalla, o no?
ahnbizcad
1
Entonces no es solo una cuestión de rendimiento; No pueden ser comparados. Tienen propósitos completamente diferentes y cambian diferentes "versiones" del DOM. Volvamos a la pregunta: ¿cuál es el punto de usar .data () si tienes que hacer .attr () para cambiar el DOM ACUTAL? Parece redundante.
ahnbizcad
34

Estaba teniendo serios problemas con

.data('property', value);

No estaba configurando el data-propertyatributo.

Comenzó a usar jQuery's .attr():

Obtenga el valor de un atributo para el primer elemento del conjunto de elementos coincidentes o establezca uno o más atributos para cada elemento coincidente.

.attr('property', value)

para establecer el valor y

.attr('property')

para recuperar el valor

¡Ahora simplemente funciona!

Leniel Maccaferri
fuente
1
Para mí, pude cambiar la propiedad de datos con data () pero noté que en las herramientas del desarrollador no mostraba el cambio, por lo que fui con attr ()
drooh
8

La respuesta aceptada de @andyb tiene un pequeño error. Además de mi comentario en su publicación anterior ...

Para este HTML:

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

Necesita acceder al atributo de esta manera:

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

pero el método de datos como este:

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

La corrección anterior para el método .data () evitará "indefinido" y el valor de los datos se actualizará (mientras que el HTML no)

El objetivo del atributo "datos" es vincular (o "vincular") un valor con el elemento. Muy similar al onclick="alert('do_something')"atributo, que une una acción al elemento ... el texto es inútil, solo desea que la acción funcione cuando hacen clic en el elemento.

Una vez que los datos o la acción están vinculados al elemento, generalmente no es necesario * actualizar el HTML, solo los datos o el método, ya que eso es lo que usaría su aplicación (JavaScript). En cuanto al rendimiento, no veo por qué querría actualizar también el HTML de todos modos, nadie ve el atributo html (excepto en Firebug u otras consolas).

Una forma en que puede pensar: el HTML (junto con los atributos) son solo texto. Los datos, funciones, objetos, etc. que utiliza JavaScript existen en un plano separado. Solo cuando se le indique a JavaScript que lo haga, leerá o actualizará el texto HTML, pero todos los datos y la funcionalidad que cree con JavaScript actúan completamente separados del texto / atributos HTML que ve en su consola Firebug (u otra).

* Pongo énfasis en general, porque si usted tiene un caso en el que hay que preservar y HTML de exportación (por ejemplo, algún tipo de formato de micro / datos editor de texto conscientes) donde el HTML se carga fresca en otra página, entonces tal vez usted necesita actualizado el código HTML también.

Frank Forte
fuente
Gracias. ¡Esto ayudó, entre todas las otras respuestas con ejemplos incorrectos! El dataen el attr('data-helptext'que hace la diferencia, donde la respuesta aceptada y los que tienen muchos votos no están funcionando. +1
Aleks
6

Me pasó lo mismo a mí. Resulta que

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

le da un objeto que no se puede escribir. Lo resolví usando:

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

Y a partir de entonces, datafue un objeto JS estándar y grabable.

Nico
fuente
¿Cómo se escribe entonces?
Trabaja para
Lo siento Thom, no sé a qué te refieres ... Después de hacerlo $.extend..., puedes usar datalo que quieras: data.name = 'Nico'; data.questionSolved = true;y console.log(data)mostrará estas propiedades recién agregadas
Nico
3

Para citar una cotización:

Los atributos de datos se extraen la primera vez que se accede a la propiedad de datos y luego ya no se accede ni se muta (todos los valores de datos se almacenan internamente en jQuery).

.data() - Documentación jQuery

Tenga en cuenta que esta limitación (francamente extraña ) solo se retiene al uso de .data().

¿La solución? Usar en su .attrlugar.

Por supuesto, varios de ustedes pueden sentirse incómodos al no usar su método dedicado. Considere el siguiente escenario:

  • El 'estándar' se actualiza para que la porción de datos de los atributos personalizados ya no sea necesaria / se reemplace

Sentido común: ¿por qué cambiarían un atributo ya establecido como ese? Imagínese classcomenzar a cambiar el nombre al grupo y idal identificador . Internet se rompería.

E incluso entonces, Javascript en sí mismo tiene la capacidad de solucionar esto. Y, por supuesto, a pesar de su infame incompatibilidad con HTML, REGEX (y una variedad de métodos similares) podría cambiar rápidamente el nombre de sus atributos a este nuevo 'estándar' mítico.

TL; DR

alert($(targetField).attr("data-helptext"));
Super gato
fuente
1

Como se mencionó, el .data()método en realidad no establecerá el valor del data-atributo, ni leerá valores actualizados si el data-atributo cambia.

Mi solución fue extender jQuery con un .realData()método que realmente corresponde al valor actual del 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);

NOTA: Claro que podría usarlo .attr(), pero desde mi experiencia, la mayoría de los desarrolladores (también conocido como yo) cometen el error de verlo .attr()y .data()son intercambiables, y a menudo sustituyen uno por el otro sin pensar. Puede funcionar la mayor parte del tiempo, pero es una excelente manera de introducir errores, especialmente cuando se trata de cualquier tipo de enlace de datos dinámico. Entonces .realData(), usando , puedo ser más explícito sobre el comportamiento previsto.

Yarin
fuente
0

Tuve el mismo problema Como todavía puede obtener datos utilizando el método .data (), solo tiene que encontrar una manera de escribir en los elementos. Este es el método auxiliar que uso. Como la mayoría de la gente ha dicho, tendrá que usar .attr. Lo tengo reemplazando cualquier _ con - como sé que lo hace. No conozco ningún otro personaje que reemplace ... sin embargo, no he investigado eso.

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

EDITAR: 01/05/2017

Descubrí que todavía había casos en los que no podía obtener los datos correctos utilizando los métodos integrados, por lo que lo que uso ahora es el siguiente:

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
fuente