selector de datos jquery

184

Necesito seleccionar elementos basados ​​en valores almacenados en el .data()objeto de un elemento . Como mínimo, me gustaría seleccionar propiedades de datos de nivel superior utilizando selectores, tal vez así:

$('a').data("category","music");
$('a:data(category=music)');

O tal vez el selector estaría en formato de selector de atributo regular:

$('a[category=music]');

O en formato de atributo, pero con un especificador para indicar que está en .data():

$('a[:category=music]');

He encontrado que la implementación de James Padolsey parece simple, pero buena. Los formatos del selector sobre los métodos espejo que se muestran en esa página. También existe este parche Sizzle .

Por alguna razón, recuerdo haber leído hace un tiempo que jQuery 1.4 incluiría soporte para selectores de valores en el .data()objeto jquery . Sin embargo, ahora que lo estoy buscando, no puedo encontrarlo. Tal vez fue solo una solicitud de función que vi. ¿Hay soporte para esto y simplemente no lo estoy viendo?

Idealmente, me gustaría admitir subpropiedades en data () usando notación de puntos. Me gusta esto:

$('a').data("user",{name: {first:"Tom",last:"Smith"},username: "tomsmith"});
$('a[:user.name.first=Tom]');

También me gustaría admitir múltiples selectores de datos, donde solo se encuentran elementos con TODOS los selectores de datos especificados. El selector múltiple jquery regular realiza una operación OR. Por ejemplo, $('a.big, a.small')selecciona aetiquetas con clase bigo small). Estoy buscando un AND, tal vez así:

$('a').data("artist",{id: 3281, name: "Madonna"});
$('a').data("category","music");
$('a[:category=music && :artist.name=Madonna]');

Por último, sería genial si los operadores de comparación y las funciones de expresiones regulares estuvieran disponibles en los selectores de datos. Entonces $(a[:artist.id>5000])sería posible. Me doy cuenta de que probablemente podría hacer mucho de esto usando filter(), pero sería bueno tener un formato de selector simple.

¿Qué soluciones hay disponibles para hacer esto? ¿Es la mejor solución de Jame's Padolsey en este momento? Mi preocupación se centra principalmente en el rendimiento, pero también en las características adicionales como la notación de puntos de subpropiedad y los selectores de datos múltiples. ¿Hay otras implementaciones que admitan estas cosas o que sean mejores de alguna manera?

Tauren
fuente
Use el jquery ui core api.jqueryui.com/category/ui-core Tiene un selector: data ()
regisbsb

Respuestas:

101

He creado un nuevo dataselector que debería permitirle realizar consultas anidadas y condiciones AND. Uso:

$('a:data(category==music,artist.name==Madonna)');

El patrón es:

:data( {namespace} [{operator} {check}]  )

"operador" y "cheque" son opcionales. Entonces, si solo lo tiene :data(a.b.c), simplemente verificará la veracidad de a.b.c.

Puede ver los operadores disponibles en el siguiente código. Entre ellos está el ~=que permite la prueba de expresiones regulares:

$('a:data(category~=^mus..$,artist.name~=^M.+a$)');

Lo he probado con algunas variaciones y parece funcionar bastante bien. Probablemente agregaré esto como un repositorio de Github pronto (con un conjunto de pruebas completo), ¡así que esté atento!

El código:

(function(){

    var matcher = /\s*(?:((?:(?:\\\.|[^.,])+\.?)+)\s*([!~><=]=|[><])\s*("|')?((?:\\\3|.)*?)\3|(.+?))\s*(?:,|$)/g;

    function resolve(element, data) {

        data = data.match(/(?:\\\.|[^.])+(?=\.|$)/g);

        var cur = jQuery.data(element)[data.shift()];

        while (cur && data[0]) {
            cur = cur[data.shift()];
        }

        return cur || undefined;

    }

    jQuery.expr[':'].data = function(el, i, match) {

        matcher.lastIndex = 0;

        var expr = match[3],
            m,
            check, val,
            allMatch = null,
            foundMatch = false;

        while (m = matcher.exec(expr)) {

            check = m[4];
            val = resolve(el, m[1] || m[5]);

            switch (m[2]) {
                case '==': foundMatch = val == check; break;
                case '!=': foundMatch = val != check; break;
                case '<=': foundMatch = val <= check; break;
                case '>=': foundMatch = val >= check; break;
                case '~=': foundMatch = RegExp(check).test(val); break;
                case '>': foundMatch = val > check; break;
                case '<': foundMatch = val < check; break;
                default: if (m[5]) foundMatch = !!val;
            }

            allMatch = allMatch === null ? foundMatch : allMatch && foundMatch;

        }

        return allMatch;

    };

}());
James
fuente
@JP: Muy dulce, ¡sabía que podía contar contigo! Me dirigí a la cama ahora, pero lo intentaré mañana. ¿Es posible hacer operaciones OR también? No lo necesito, solo curiosidad.
Tauren
1
@ JP: Además, tengo curiosidad acerca de su opinión sobre las otras soluciones de selector de datos, pros / contras para los suyos frente a los de ellos. Por ejemplo, este complemento: plugins.jquery.com/project/dataSelector .
Tauren
1
Es posible hacer operaciones OR, pero podría ser mejor simplemente tener dos selectores, $("a:data(condition),a:data(orCondition)")... tendrá el mismo efecto. Cuantas más funciones agregue, más lento será. Si la lógica es compleja, úsela $(foo).filter(function(){...}).
James
3
@JP: ¿Alguna vez convertiste esto en un proyecto github? Estoy ejecutando una prueba usando jQuery 1.5.1 usando el selector $ ("input: data (test> 400)") en una entrada con el atributo html5 data-test = "500" pero el selector no devuelve nada.
James South
1
@JamesSouth Eso se debe a que el selector en esta respuesta usa el nivel bajo jQuery.data, que no obtiene los datos definidos en los atributos HTML5. Si desea esa funcionalidad, puede cambiar jQuery.dataa $('selector').data, pero eso es una compensación por la velocidad.
Shef
175

En este momento estoy seleccionando así:

$('a[data-attribute=true]')

Lo que parece funcionar bien, pero sería bueno si jQuery pudiera seleccionar por ese atributo sin el prefijo 'data-'.

No he probado esto con datos agregados a elementos a través de jQuery dinámicamente, por lo que podría ser la caída de este método.

Ceniza
fuente
Esto es exactamente lo que estaba buscando. Dulce
MikeMurko
109
En realidad, esto no funciona si adjunta / modifica datos a través de JS a través de la función .data (), el selector de atributos solo verifica el DOM, JS almacena .data () en la memoria
Clarence Liu
1
¿Podría decirme cómo accede por atributo si lo adjunta a través de .data ()?
Maxim V. Pavlov
1
Sugeriría que las otras respuestas proporcionadas en este hilo son una mejor opción para cualquier cosa que no sea un caso de uso muy simple.
Ash
La solución de Dmitri me parece agradable, ya que no se basa en código de terceros.
Ash
83

También puede usar una función de filtrado simple sin complementos. Esto no es exactamente lo que quieres, pero el resultado es el mismo:

$('a').data("user", {name: {first:"Tom",last:"Smith"},username: "tomsmith"});

$('a').filter(function() {
    return $(this).data('user') && $(this).data('user').name.first === "Tom";
});
Dmitri
fuente
2
Esta es una gran idea, olvidé que la filterfunción transversal podría aceptar una función de prueba =) gracias
Clarence Liu
¿Cómo podría hacer un "contiene" en lugar de es igual a Tom?
Alisso
1
Además, en lugar de name.first == "Tom" use name.first && name.first.indexOF ("Tom")> -1;
Dmitri
24

Quiero advertirle que $('a[data-attribute=true]')no funciona, según la respuesta de Ashley, si adjuntó datos a un elemento DOM a través de la función data ().

Funciona como es de esperar si agrega un atributo de datos real en su HTML, pero jQuery almacena los datos en la memoria, por lo que los resultados que obtendría $('a[data-attribute=true]')no serían correctos.

Deberá usar el complemento de datos http://code.google.com/p/jquerypluginsblog/ , usar la filtersolución de Dmitri o hacer un $ .each sobre todos los elementos y verificar .data () iterativamente

Clarence Liu
fuente
En mi caso, está bien, ya que estoy rellenando previamente el campo en el lado del servidor y solo necesito consultarlo de esta manera en la carga inicial. El filtro es probablemente igual de rápido (cada uno es ciertamente más lento) pero esto gana en legibilidad
Don
11

Hay un :data()complemento de filtro que hace exactamente esto :)

Algunos ejemplos basados ​​en su pregunta:

$('a:data("category=music")')
$('a:data("user.name.first=Tom")');
$('a:data("category=music"):data("artist.name=Madonna")');
//jQuery supports multiple of any selector to restrict further, 
//just chain with no space in-between for this effect

El rendimiento no va a ser extremadamente bueno en comparación con lo que es posible , seleccionar $._cachey tomar los elementos correspondientes es, con mucho, el más rápido, pero mucho más redondo y no muy "jQuery-ey" en términos de cómo llegar a cosas (generalmente vienes desde el lado del elemento). De todos modos, no estoy seguro de que esto sea más rápido de todos modos ya que el proceso de pasar de un Id único a un elemento es complicado en sí mismo, en términos de rendimiento.

El selector de comparación que mencionó será mejor en un .filter(), no hay soporte incorporado para esto en el complemento, aunque puede agregarlo sin muchos problemas.

Nick Craver
fuente
Gracias por la respuesta completa. ¿Sabe si usar data-*atributos HTML5 y seleccionarlos sería más rápido que seleccionar .data()propiedades? Además, ¿alguna idea de dónde puedo encontrar más información sobre $ ._ caché? Busqué en Google, pero no encuentro mucho.
Tauren
@Tauren - Mi culpa no es $.cacheasí $._cache, puedes ver cómo se implementa y usa en jQuery core aquí: github.com/jquery/jquery/blob/master/src/data.js#L4 Cuando llamas .data(), en realidad lo almacena como un objeto in $.cache[elementUniqueID], que es un Id asignado según sea necesario de manera incriminatoria a cada elemento, por ejemplo, 1, 2, 3, etc. Ese ID de escalada se expondrá en jQuery 1.4.3, creo, basado en los comentarios git del otro día. Supongo que la ruta HTML 5 sería más rápida, depende de las optimizaciones del navegador que estén disponibles (estoy seguro de que habrá más disponibles).
Nick Craver
7

Puede establecer un data-*atributo en un olmo usando attr(), y luego seleccionar usando ese atributo:

var elm = $('a').attr('data-test',123); //assign 123 using attr()
elm = $("a[data-test=123]"); //select elm using attribute

y ahora para ese olmo, ambos attr()y data()producirán 123 :

console.log(elm.attr('data-test')); //123
console.log(elm.data('test')); //123

Sin embargo, si se modifica el valor de ser 456 utilizando attr(), data() todavía será 123 :

elm.attr('data-test',456); //modify to 456
elm = $("a[data-test=456]"); //reselect elm using new 456 attribute

console.log(elm.attr('data-test')); //456
console.log(elm.data('test')); //123

Entonces, según tengo entendido, parece que probablemente debería evitar la mezcla attr()y los data()comandos en su código si no es necesario. Porque attr()parece corresponder directamente con el DOM, mientras que data()interactúa con la 'memoria', aunque su valor inicial puede ser del DOM. Pero el punto clave es que los dos no están necesariamente sincronizados en absoluto.

Así que ten cuidado.

En cualquier caso, si no está cambiando el data-*atributo en el DOM o en la memoria, no tendrá ningún problema. Tan pronto como comience a modificar los valores, es cuando pueden surgir problemas potenciales.

Gracias a @Clarence Liu a la respuesta de @ Ash, así como a esta publicación .

D.Tate
fuente
5

Si también usa jQueryUI, obtiene una versión (simple) del :dataselector que verifica la presencia de un elemento de datos, por lo que puede hacer algo como $("div:data(view)"), o $( this ).closest(":data(view)").

Ver http://api.jqueryui.com/data-selector/ . No sé por cuánto tiempo lo han tenido, ¡pero está allí ahora!

Pablo
fuente
Esto solo verifica la presencia del elemento de datos como usted dijo. No verifica los valores (de acuerdo con los documentos). Aunque es bueno saber sobre esto
Fractalf
3

Aquí hay un complemento que simplifica la vida https://github.com/rootical/jQueryDataSelector

Úselo así:

data selector           jQuery selector
  $$('name')              $('[data-name]')
  $$('name', 10)          $('[data-name=10]')
  $$('name', false)       $('[data-name=false]')
  $$('name', null)        $('[data-name]')
  $$('name', {})          Syntax error
Rootical V.
fuente