selettore dati jquery


184

Devo selezionare gli elementi in base ai valori memorizzati .data()nell'oggetto di un elemento . Come minimo, vorrei selezionare le proprietà dei dati di livello superiore utilizzando i selettori, forse in questo modo:

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

O forse il selettore dovrebbe essere nel normale formato del selettore di attributi:

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

O in formato attributo, ma con un identificatore per indicare che è in .data():

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

Ho trovato l'implementazione di James Padolsey semplice, ma buona. Il selettore formatta sopra i metodi mirror mostrati su quella pagina. C'è anche questa patch Sizzle .

Per qualche motivo, ricordo di aver letto qualche tempo fa che jQuery 1.4 avrebbe incluso il supporto per i selettori sui valori .data()nell'oggetto jquery . Tuttavia, ora che lo sto cercando, non riesco a trovarlo. Forse è stata solo una richiesta di funzionalità che ho visto. C'è supporto per questo e non lo vedo?

Idealmente, vorrei supportare le sotto-proprietà nei dati () usando la notazione a punti. Come questo:

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

Vorrei anche supportare più selettori di dati, in cui sono presenti solo elementi con TUTTI i selettori di dati specificati. Il selettore multiplo jquery normale esegue un'operazione OR. Ad esempio, $('a.big, a.small')seleziona i atag con una classe bigo small). Sto cercando un AND, forse così:

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

Infine, sarebbe fantastico se gli operatori di confronto e le funzionalità regex fossero disponibili sui selettori di dati. Quindi $(a[:artist.id>5000])sarebbe possibile. Mi rendo conto che probabilmente avrei potuto fare molto di questo usandofilter() , ma sarebbe bello avere un semplice formato di selezione.

Quali soluzioni sono disponibili per farlo? Jame's Padolsey è la migliore soluzione in questo momento? La mia preoccupazione riguarda principalmente le prestazioni, ma anche le funzionalità extra come la notazione delle proprietà secondarie e i selettori di dati multipli. Ci sono altre implementazioni che supportano queste cose o sono migliori in qualche modo?


Risposte:


101

Ho creato un nuovo dataselettore che dovrebbe consentirti di eseguire query nidificate e condizioni AND. Uso:

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

Lo schema è:

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

"operator" e "check" sono opzionali. Quindi, se solo lo hai :data(a.b.c), verificherai semplicemente la veridicità dia.b.c .

Puoi vedere gli operatori disponibili nel codice qui sotto. Tra questi c'è quello ~=che consente il test regex:

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

L'ho provato con alcune varianti e sembra funzionare abbastanza bene. Probabilmente lo aggiungerò presto come repository Github (con una suite di test completa), quindi dai un'occhiata!

Il codice:

(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;

    };

}());

@JP: Molto dolce, sapevo di poter contare su di te! Andato a letto ora, ma lo proverò domani. È possibile eseguire anche operazioni OR? Non ne ho bisogno, solo curioso.
Tauren,

1
@JP: Inoltre, sono curioso di sapere come prendi le altre soluzioni di selezione dei dati, pro / contro per i tuoi contro i loro. Ad esempio, questo plugin: plugins.jquery.com/project/dataSelector .
Tauren,

1
È possibile eseguire operazioni OR, ma potrebbe essere meglio avere semplicemente due selettori, $("a:data(condition),a:data(orCondition)")... avrà lo stesso effetto. Più funzionalità aggiungi, più lento sarà. Se la logica è complessa, utilizzare $(foo).filter(function(){...}).
James,

3
@JP: L'hai mai trasformato in un progetto github? Sto eseguendo un test utilizzando jQuery 1.5.1 utilizzando il selettore $ ("input: data (test> 400)") su un input con l'attributo html5 data-test = "500" ma il selettore non restituisce nulla.
James South,

1
@JamesSouth Questo perché il selettore in questa risposta utilizza il livello basso jQuery.data, che non ottiene i dati definiti negli attributi HTML5. Se si desidera che la funzionalità, è possibile modificare jQuery.dataa $('selector').data, ma questo è un compromesso per la velocità.
Shef,

175

Al momento sto selezionando in questo modo:

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

Il che sembra funzionare bene, ma sarebbe bello se jQuery fosse in grado di selezionare per quell'attributo senza il prefisso 'data-'.

Non ho provato questo con i dati aggiunti agli elementi tramite jQuery in modo dinamico, quindi potrebbe essere la rovina di questo metodo.


Questo e 'esattamente quello che stavo cercando. Dolce
MikeMurko,

109
Questo in realtà non funziona se si collegano / modificano i dati tramite JS tramite la funzione .data (), il selettore di attributi controlla solo il DOM, JS memorizza .data () in memoria
Clarence Liu

1
Potresti dire come si accede per attributo se si collega tramite .data ()?
Maxim V. Pavlov,

1
Suggerirei che le altre risposte fornite in questo thread siano un'opzione migliore per qualcosa di diverso da un caso d'uso molto semplice.
Ash,

La soluzione di Dmitri mi sembra carina in quanto non si basa su codice di terze parti.
Ash,

83

Puoi anche utilizzare una semplice funzione di filtro senza plug-in. Questo non è esattamente quello che vuoi ma il risultato è lo stesso:

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

$('a').filter(function() {
    return $(this).data('user') && $(this).data('user').name.first === "Tom";
});

2
Questa è un'ottima idea, ho dimenticato che la filterfunzione di attraversamento potrebbe accettare una funzione di test =) grazie
Clarence Liu

Come potrei fare un "contiene" invece di è uguale a Tom?
Alisso,

1
Alisso, invece di name.first == "Tom" usa name.first && name.first.indexOF ("Tom")> -1;
Dmitri,

24

Voglio avvertirti $('a[data-attribute=true]') non funziona, secondo la risposta di Ashley, se hai collegato i dati a un elemento DOM tramite la funzione data ().

Funziona come ci si aspetterebbe se si aggiungesse un vero attr di dati nel proprio HTML, ma jQuery memorizza i dati in memoria, quindi i risultati che si $('a[data-attribute=true]')otterrebbero non sarebbero corretti.

Dovrai utilizzare il plug-in di dati http://code.google.com/p/jquerypluginsblog/ , utilizzare la filtersoluzione di Dmitri o fare $ .each su tutti gli elementi e controllare .data () iterativamente


Nel mio caso va bene dato che sto precompilando il campo sul lato server e ho solo bisogno di consultarlo in questo modo al caricamento iniziale. Il filtro è probabilmente altrettanto veloce (ognuno è quasi certamente più lento) ma questo vince sulla leggibilità.
Don

11

C'è un :data()plug-in di filtro che fa proprio questo :)

Alcuni esempi basati sulla tua domanda:

$('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

Le prestazioni non saranno estremamente grandi rispetto a ciò che è possibile , selezionare $._cachee afferrare gli elementi corrispondenti è di gran lunga il più veloce, ma molto più circolare e non molto "jQuery-ey" in termini di come si arriva a roba (di solito arrivi dal lato dell'elemento). In cima alla mia testa, non sono sicuro che questo sia comunque il più veloce poiché il processo di passaggio dall'ID univoco all'elemento è contorto in sé, in termini di prestazioni.

Il selettore di confronto che hai citato sarà il migliore da fare in a .filter(), non c'è supporto integrato per questo nel plugin, anche se potresti aggiungerlo senza troppi problemi.


grazie per la risposta approfondita. Sai se usare gli data-*attributi HTML5 e selezionarli sarebbe più veloce che selezionare le .data()proprietà? Inoltre, hai idea di dove posso trovare ulteriori informazioni su $ ._ cache? Ho cercato su Google per questo, ma non sto trovando molto.
Tauren,

@Tauren - La mia colpa non lo $.cacheè $._cache, puoi vedere come è implementato e utilizzato nel core jQuery qui: github.com/jquery/jquery/blob/master/src/data.js#L4 Quando chiami .data()lo sta effettivamente memorizzando come oggetto in $.cache[elementUniqueID], che è un ID assegnato secondo necessità in modo incrementale a ciascun elemento, ad esempio 1, 2, 3, ecc. Quell'ID di arrampicata sarà esposto in jQuery 1.4.3 Credo, sulla base dei commenti git dell'altro giorno. Suppongo che il percorso HTML 5 sarebbe più veloce, dipende da quali ottimizzazioni del browser sono disponibili (sono sicuro che ne saranno disponibili di più).
Nick Craver

7

Puoi impostare un data-*attributo su un olm usando attr(), quindi selezionare usando quell'attributo:

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

e ora per quell'olmo, entrambi attr()e data()cederanno 123 :

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

Tuttavia, se si modifica il valore per essere 456 utilizzando attr(), data() sarà comunque 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

Quindi, a quanto ho capito, sembra che probabilmente dovresti evitare di mescolare attr()e data()comandi nel tuo codice se non devi. Perché attr()sembra corrispondere direttamente con il DOM mentre data()interagisce con la "memoria", sebbene il suo valore iniziale possa provenire dal DOM. Ma il punto chiave è che i due non sono necessariamente sincronizzati.

Quindi stai solo attento.

In ogni caso, se non stai modificando l' data-*attributo nel DOM o nella memoria, non avrai problemi. Non appena si inizia a modificare i valori è quando possono sorgere potenziali problemi.

Grazie a @Clarence Liu alla risposta di @ Ash, così come a questo post .



5

Se usi anche jQueryUI, otterrai una versione (semplice) del :dataselettore che controlla la presenza di un elemento di dati, in modo da poter fare qualcosa del genere $("div:data(view)"), o $( this ).closest(":data(view)").

Vedi http://api.jqueryui.com/data-selector/ . Non so da quanto tempo ce l'hanno, ma è lì adesso!


Questo controlla solo la presenza dell'elemento dati come hai detto. Non controlla i valori (secondo i documenti). Bello sapere questo però
Fractalf

3

Ecco un plugin che semplifica la vita https://github.com/rootical/jQueryDataSelector

Usalo così:

data selector           jQuery selector
  $$('name')              $('[data-name]')
  $$('name', 10)          $('[data-name=10]')
  $$('name', false)       $('[data-name=false]')
  $$('name', null)        $('[data-name]')
  $$('name', {})          Syntax error
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.