sélecteur de données jquery


184

J'ai besoin de sélectionner des éléments en fonction des valeurs stockées dans l' .data()objet d' un élément . Au minimum, j'aimerais sélectionner les propriétés de données de niveau supérieur à l'aide de sélecteurs, peut-être comme ceci:

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

Ou peut-être que le sélecteur serait au format de sélecteur d'attribut normal:

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

Ou au format d'attribut, mais avec un spécificateur pour indiquer qu'il est dans .data():

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

J'ai trouvé la mise en œuvre de James Padolsey simple, mais bonne. Les formats de sélecteur ci-dessus reflètent les méthodes affichées sur cette page. Il y a aussi ce patch Sizzle .

Pour une raison quelconque, je me souviens avoir lu il y a quelque temps que jQuery 1.4 inclurait la prise en charge des sélecteurs sur les valeurs de l' .data()objet jquery . Cependant, maintenant que je le cherche, je ne le trouve pas. C'était peut-être juste une demande de fonctionnalité que j'ai vue. Y a-t-il un soutien pour cela et je ne le vois tout simplement pas?

Idéalement, j'aimerais prendre en charge les sous-propriétés dans data () en utilisant la notation par points. Comme ça:

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

Je voudrais également prendre en charge plusieurs sélecteurs de données, où seuls les éléments avec TOUS les sélecteurs de données spécifiés sont trouvés. Le sélecteur multiple jquery régulier effectue une opération OR. Par exemple, $('a.big, a.small')sélectionne les abalises avec la classe bigou small). Je recherche un ET, peut-être comme ceci:

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

Enfin, ce serait formidable si les opérateurs de comparaison et les fonctionnalités regex étaient disponibles sur les sélecteurs de données. Ce $(a[:artist.id>5000])serait donc possible. Je me rends compte que je pourrais probablement faire une grande partie de cela en utilisant filter(), mais ce serait bien d'avoir un format de sélecteur simple.

Quelles solutions sont disponibles pour ce faire? Est-ce que Jame's Padolsey est la meilleure solution en ce moment? Ma préoccupation concerne principalement les performances, mais aussi les fonctionnalités supplémentaires telles que la notation par points de sous-propriété et les sélecteurs de données multiples. Y a-t-il d'autres implémentations qui prennent en charge ces choses ou qui sont meilleures d'une certaine manière?


Utilisez le noyau jquery ui api.jqueryui.com/category/ui-core Il a un sélecteur: data ()
regisbsb

Réponses:


101

J'ai créé un nouveau datasélecteur qui devrait vous permettre d'effectuer des requêtes imbriquées et des conditions AND. Usage:

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

Le modèle est:

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

"opérateur" et "contrôle" sont facultatifs. Donc, si vous l'avez seulement, vous :data(a.b.c)vérifierez simplement la véracité de a.b.c.

Vous pouvez voir les opérateurs disponibles dans le code ci-dessous. Parmi eux, il est ~=possible de tester les regex:

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

Je l'ai testé avec quelques variantes et il semble fonctionner assez bien. Je vais probablement l'ajouter bientôt en tant que dépôt Github (avec une suite de tests complète), alors gardez un œil sur!

Le code:

(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: Très gentil, je savais que je pouvais compter sur toi! Je me suis couché maintenant, mais je vais essayer demain. Est-il possible de faire également des opérations OU? Je n'en ai pas besoin, juste curieux.
Tauren

1
@JP: Aussi, je suis curieux de savoir ce que vous pensez des autres solutions de sélecteur de données, avantages / inconvénients pour les vôtres par rapport aux leurs. Par exemple, ce plugin: plugins.jquery.com/project/dataSelector .
Tauren

1
Il est possible de faire des opérations OU, mais il peut être préférable d'avoir simplement deux sélecteurs, $("a:data(condition),a:data(orCondition)")... cela aura le même effet. Plus vous ajoutez de fonctionnalités, plus ce sera lent. Si la logique est complexe, utilisez $(foo).filter(function(){...}).
James

3
@JP: Avez-vous déjà transformé cela en projet github? J'exécute un test en utilisant jQuery 1.5.1 en utilisant le sélecteur $ ("input: data (test> 400)") sur une entrée avec l'attribut html5 data-test = "500" mais le sélecteur ne renvoie rien.
James South

1
@JamesSouth C'est parce que le sélecteur de cette réponse utilise le bas niveau jQuery.data, qui n'obtient pas les données définies dans les attributs HTML5. Si vous voulez cette fonctionnalité, vous pouvez passer jQuery.dataà $('selector').data, mais c'est un compromis pour la vitesse.
Shef

175

Pour le moment, je sélectionne comme ceci:

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

Ce qui semble fonctionner très bien, mais ce serait bien si jQuery pouvait sélectionner par cet attribut sans le préfixe «data-».

Je n'ai pas testé cela avec des données ajoutées aux éléments via jQuery dynamiquement, donc cela pourrait être la chute de cette méthode.


Ceci est exactement ce que je cherchais. Sweet
MikeMurko

109
Cela ne fonctionne pas réellement si vous attachez / modifiez des données via JS via la fonction .data (), le sélecteur d'attribut ne vérifie que le DOM, JS stocke .data () en mémoire
Clarence Liu

1
Pourriez-vous s'il vous plaît dire comment vous accédez par attribut si vous attachez via .data ()?
Maxim V. Pavlov

1
Je suggère que les autres réponses fournies dans ce fil sont une meilleure option pour autre chose qu'un cas d'utilisation très simple.
Ash

La solution de Dmitri me semble bien car elle ne repose pas sur du code tiers.
Ash

83

Vous pouvez également utiliser une fonction de filtrage simple sans aucun plug-in. Ce n'est pas exactement ce que vous voulez mais le résultat est le même:

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

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

2
C'est une excellente idée, j'ai oublié que la filterfonction de traversée pourrait accepter une fonction de test =) merci
Clarence Liu

Comment pourrais-je faire un "contient" au lieu de est égal à Tom?
Alisso

1
Alisso, au lieu de name.first == "Tom", utilisez name.first && name.first.indexOF ("Tom")> -1;
Dmitri

24

Je veux vous avertir que $('a[data-attribute=true]')cela ne fonctionne pas, selon la réponse d'Ashley, si vous avez joint des données à un élément DOM via la fonction data ().

Cela fonctionne comme vous vous en doutez si vous avez ajouté un data-attr réel dans votre HTML, mais jQuery stocke les données en mémoire, de sorte que les résultats que vous obtiendrez ne $('a[data-attribute=true]')seraient pas corrects.

Vous devrez utiliser le plugin de données http://code.google.com/p/jquerypluginsblog/ , utiliser la filtersolution de Dmitri ou faire un $ .each sur tous les éléments et vérifier .data () de manière itérative


Dans mon cas, c'est bien puisque je pré-remplis le champ côté serveur et que je n'ai besoin de le consulter que de cette façon lors du chargement initial. Le filtre est probablement tout aussi rapide (chacun est presque certainement plus lent) mais cela gagne en lisibilité.
Don

11

Il y a un :data()plugin de filtre qui fait exactement cela :)

Quelques exemples basés sur votre question:

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

Les performances ne seront pas extrêmement bonnes par rapport à ce qui est possible , sélectionner $._cacheet saisir les éléments correspondants est de loin le plus rapide, mais beaucoup plus rond et pas très "jQuery-ey" en termes de comment vous y arriver trucs (vous venez généralement du côté de l'élément). De toute façon, je ne suis pas sûr que ce soit le plus rapide, car le processus de passage de l'identifiant unique à l'élément est compliqué en lui-même, en termes de performances.

Le sélecteur de comparaison que vous avez mentionné sera préférable de le faire dans un .filter(), il n'y a pas de support intégré pour cela dans le plugin, bien que vous puissiez l'ajouter sans trop de problèmes.


merci pour la réponse approfondie. Savez-vous si utiliser des data-*attributs HTML5 et les sélectionner serait plus rapide que sélectionner des .data()propriétés? Aussi, une idée où puis-je en savoir plus sur $ ._ cache? J'ai cherché sur Google, mais je ne trouve pas grand-chose.
Tauren

@Tauren - Ma faute ce n'est $.cachepas $._cache, vous pouvez voir comment il est implémenté et utilisé dans jQuery core ici: github.com/jquery/jquery/blob/master/src/data.js#L4 Quand vous appelez, .data()il le stocke en fait comme un objet in $.cache[elementUniqueID], qui est un identifiant attribué au besoin de manière incrémentale à chaque élément, par exemple 1, 2, 3, etc. Cet identifiant grimpant sera exposé dans jQuery 1.4.3 je crois, basé sur les commentaires de git l'autre jour. Je suppose que l'itinéraire HTML 5 serait plus rapide, cela dépend des optimisations de navigateur disponibles (je suis sûr que d'autres seront disponibles).
Nick Craver

7

Vous pouvez définir un data-*attribut sur un orme en utilisant attr(), puis sélectionner en utilisant cet attribut:

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

et maintenant pour cet orme, les deux attr()et data()donneront 123 :

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

Cependant, si vous modifiez la valeur pour qu'elle soit 456 en utilisant attr(), data() sera toujours 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

Donc, si je comprends bien, il semble que vous devriez probablement éviter les mélanges attr()et les data()commandes dans votre code si vous n'êtes pas obligé. Car attr()semble correspondre directement avec le DOM alors qu'il data()interagit avec la «mémoire», bien que sa valeur initiale puisse provenir du DOM. Mais le point clé est que les deux ne sont pas nécessairement synchronisés du tout.

Alors faites attention.

En tout cas, si vous ne changez pas l' data-*attribut dans le DOM ou dans la mémoire, vous n'aurez pas de problème. Dès que vous commencez à modifier les valeurs, des problèmes potentiels peuvent survenir.

Merci à @Clarence Liu pour la réponse de @ Ash, ainsi que pour ce post .



5

Si vous utilisez également jQueryUI, vous obtenez une version (simple) du :datasélecteur qui vérifie la présence d'un élément de données, vous pouvez donc faire quelque chose comme $("div:data(view)"), ou $( this ).closest(":data(view)").

Voir http://api.jqueryui.com/data-selector/ . Je ne sais pas depuis combien de temps ils l'ont, mais c'est là maintenant!


Cela vérifie uniquement la présence de l'élément de données comme vous l'avez dit. Il ne vérifie pas les valeurs (selon la documentation). Bien à savoir à ce sujet
Fractalf

3

Voici un plugin qui simplifie la vie https://github.com/rootical/jQueryDataSelector

Utilisez-le comme ça:

data selector           jQuery selector
  $$('name')              $('[data-name]')
  $$('name', 10)          $('[data-name=10]')
  $$('name', false)       $('[data-name=false]')
  $$('name', null)        $('[data-name]')
  $$('name', {})          Syntax error
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.