jquery data selector

184

Ich muss Elemente basierend auf Werten auswählen, die im .data()Objekt eines Elements gespeichert sind . Zumindest möchte ich Dateneigenschaften der obersten Ebene mithilfe von Selektoren auswählen, möglicherweise wie folgt:

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

Oder vielleicht hat der Selektor das reguläre Attribut-Selektor-Format:

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

Oder im Attributformat, aber mit einem Bezeichner, der angibt, dass es sich um Folgendes handelt .data():

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

Ich habe festgestellt, dass die Implementierung von James Padolsey einfach und dennoch gut aussieht. Die Auswahlformate über den auf dieser Seite gezeigten Spiegelmethoden. Es gibt auch diesen Sizzle-Patch .

Aus irgendeinem Grund erinnere ich mich, dass ich vor einiger Zeit gelesen habe, dass jQuery 1.4 Selektoren für Werte im jquery- .data()Objekt unterstützen würde. Jetzt, wo ich danach suche, kann ich es jedoch nicht finden. Vielleicht war es nur eine Feature-Anfrage, die ich gesehen habe. Gibt es Unterstützung dafür und ich sehe es einfach nicht?

Im Idealfall möchte ich Untereigenschaften in data () mithilfe der Punktnotation unterstützen. So was:

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

Ich möchte auch mehrere Datenselektoren unterstützen, bei denen nur Elemente mit ALLEN angegebenen Datenselektoren gefunden werden. Der reguläre JQuery Multiple Selector führt eine ODER-Verknüpfung aus. Zum Beispiel $('a.big, a.small')wählt aTags mit jeder Klasse bigoder small). Ich suche ein UND, vielleicht so:

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

Schließlich wäre es großartig, wenn Vergleichsoperatoren und Regex-Funktionen für Datenselektoren verfügbar wären. Wäre $(a[:artist.id>5000])also möglich. Mir ist klar, dass ich wahrscheinlich viel davon tun könnte filter(), aber es wäre schön, ein einfaches Auswahlformat zu haben.

Welche Lösungen stehen dafür zur Verfügung? Ist James Padolsey derzeit die beste Lösung? Mein Anliegen ist in erster Linie die Leistung, aber auch die zusätzlichen Funktionen wie die Notation von Sub-Property-Punkten und mehrere Datenselektoren. Gibt es andere Implementierungen, die diese Dinge unterstützen oder in irgendeiner Weise besser sind?

Tauren
quelle
Verwenden Sie die jquery ui core api.jqueryui.com/category/ui-core Es hat einen: data () Selektor
regisbsb

Antworten:

101

Ich habe einen neuen dataSelektor erstellt, mit dem Sie verschachtelte Abfragen und UND-Bedingungen ausführen können. Verwendung:

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

Das Muster ist:

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

"operator" und "check" sind optional. Wenn Sie es also nur haben :data(a.b.c), wird es einfach auf die Richtigkeit von überprüft a.b.c.

Sie können die verfügbaren Operatoren im folgenden Code sehen. Unter anderem ~=können Regex-Tests durchgeführt werden:

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

Ich habe es mit ein paar Variationen getestet und es scheint ziemlich gut zu funktionieren. Ich werde dies wahrscheinlich bald als Github-Repo hinzufügen (mit einer vollständigen Testsuite), also pass auf!

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

    };

}());
James
quelle
@JP: Sehr süß, ich wusste, dass ich auf dich zählen kann! Ich bin jetzt ins Bett gegangen, aber ich werde es morgen ausprobieren. Ist es auch möglich, OP-Operationen durchzuführen? Ich brauche es nicht, nur neugierig.
Tauren
1
@JP: Ich bin auch neugierig auf Ihre Meinung zu den anderen Datenauswahllösungen, Vor- und Nachteile für Ihre und ihre. Zum Beispiel dieses Plugin: plugins.jquery.com/project/dataSelector .
Tauren
1
Es ist möglich, ODER-Operationen durchzuführen, aber es ist möglicherweise am besten, einfach zwei Selektoren zu haben, $("a:data(condition),a:data(orCondition)")... es hat den gleichen Effekt. Je mehr Funktionen Sie hinzufügen, desto langsamer wird es. Wenn die Logik komplex ist, verwenden Sie $(foo).filter(function(){...}).
James
3
@JP: Hast du das jemals in ein Github-Projekt verwandelt? Ich führe einen Test mit jQuery 1.5.1 mit dem Selektor $ ("input: data (test> 400)") für eine Eingabe mit dem HTML5-Attribut data-test = "500" aus, aber der Selektor gibt nichts zurück.
James South
1
@JamesSouth Das liegt daran, dass der Selektor in dieser Antwort die niedrige Ebene verwendet jQuery.data, bei der die in den HTML5-Attributen definierten Daten nicht abgerufen werden. Wenn Sie diese Funktionalität möchten, können Sie ändern jQuery.datazu $('selector').data, aber das ist ein Trade - off für die Geschwindigkeit.
Shef
175

Im Moment wähle ich so aus:

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

Das scheint gut zu funktionieren, aber es wäre schön, wenn jQuery nach diesem Attribut ohne das Präfix 'data-' auswählen könnte.

Ich habe dies nicht mit Daten getestet, die über jQuery dynamisch zu Elementen hinzugefügt wurden, sodass dies der Nachteil dieser Methode sein könnte.

Asche
quelle
Genau das habe ich gesucht. Süß
MikeMurko
109
Dies funktioniert nicht wirklich, wenn Sie Daten über JS über die Funktion .data () anhängen / ändern. Die Attributauswahl überprüft nur das DOM. JS speichert .data () im Speicher
Clarence Liu
1
Könnten Sie bitte angeben, wie Sie nach Attributen zugreifen, wenn Sie über .data () anhängen?
Maxim V. Pavlov
1
Ich würde vorschlagen, dass die anderen Antworten in diesem Thread eine bessere Option für alles andere als einen sehr einfachen Anwendungsfall sind.
Ash
Die Lösung von Dmitri sieht für mich gut aus, da sie nicht auf Code von Drittanbietern basiert.
Ash
83

Sie können auch eine einfache Filterfunktion ohne Plugins verwenden. Dies ist nicht genau das, was Sie wollen, aber das Ergebnis ist das gleiche:

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

$('a').filter(function() {
    return $(this).data('user') && $(this).data('user').name.first === "Tom";
});
Dmitri
quelle
2
Dies ist eine großartige Idee, ich habe vergessen, dass die filterTraversal-Funktion eine Testfunktion akzeptieren kann =) danke
Clarence Liu
Wie könnte ich ein "enthält" machen, anstatt gleich Tom zu sein?
Alisso
1
Alisso, anstelle von name.first == "Tom" verwenden Sie name.first && name.first.indexOF ("Tom")> -1;
Dmitri
24

Ich möchte Sie warnen, dass $('a[data-attribute=true]')dies laut Ashleys Antwort nicht funktioniert, wenn Sie Daten über die Funktion data () an ein DOM-Element angehängt haben.

Es funktioniert wie erwartet, wenn Sie Ihrem HTML-Code einen tatsächlichen Datenattr hinzugefügt haben, aber jQuery speichert die Daten im Speicher, sodass die Ergebnisse, die Sie erhalten $('a[data-attribute=true]'), nicht korrekt sind.

Sie müssen das Daten-Plugin http://code.google.com/p/jquerypluginsblog/ verwenden , die filterLösung von Dmitri verwenden oder alle Elemente mit $ .each überprüfen und iterativ .data () überprüfen

Clarence Liu
quelle
In meinem Fall ist es in Ordnung, da ich das Feld auf der Serverseite vorab ausfülle und es nur beim ersten Laden auf diese Weise konsultieren muss. Der Filter ist wahrscheinlich genauso schnell (jeder ist mit ziemlicher Sicherheit langsamer), aber dies gewinnt an Lesbarkeit.
Don
11

Es gibt ein :data()Filter-Plugin , das genau das tut :)

Einige Beispiele basierend auf Ihrer Frage:

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

Die Leistung wird im Vergleich zu dem, was möglich ist , nicht besonders gut sein. Die Auswahl $._cacheund das Ergreifen der entsprechenden Elemente ist bei weitem die schnellste, aber viel runder und nicht sehr "jQuery-ey" in Bezug auf die Art und Weise, wie Sie dazu kommen Sachen (du kommst normalerweise von der Elementseite herein). Ich bin mir nicht sicher, ob dies sowieso am schnellsten ist, da der Prozess des Übergangs von der eindeutigen ID zum Element in Bezug auf die Leistung in sich selbst verwickelt ist.

Der von Ihnen erwähnte Vergleichswähler eignet sich am besten .filter()für a. Das Plugin bietet keine integrierte Unterstützung dafür, obwohl Sie ihn ohne großen Aufwand hinzufügen können.

Nick Craver
quelle
danke für die gründliche antwort. Wissen Sie, ob die Verwendung von HTML5- data-*Attributen und deren Auswahl schneller ist als die Auswahl von .data()Eigenschaften? Gibt es auch eine Idee, wo ich mehr über $ ._ Cache erfahren kann? Ich habe danach gegoogelt, finde aber nicht viel.
Tauren
@Tauren - Meine Schuld ist es $.cachenicht $._cache, Sie können hier sehen, wie es in jQuery Core implementiert und verwendet wird: github.com/jquery/jquery/blob/master/src/data.js#L4 Wenn Sie es aufrufen, wird .data()es tatsächlich als Objekt gespeichert in $.cache[elementUniqueID], eine ID, die jedem Element nach Bedarf schrittweise zugewiesen wird, z. B. 1, 2, 3 usw. Diese Kletter-ID wird in jQuery 1.4.3 angezeigt, glaube ich, basierend auf den Git-Kommentaren des anderen Tages. Ich würde davon ausgehen, dass die HTML 5-Route schneller ist, abhängig davon, welche Browseroptimierungen verfügbar sind (ich bin sicher, dass weitere verfügbar sein werden).
Nick Craver
7

Sie können ein data-*Attribut für eine Ulme festlegen attr()und dann mit diesem Attribut auswählen:

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

und jetzt für diese Ulme, beide attr()und data()wird 123 ergeben :

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

Wenn Sie jedoch den Wert so ändern, dass er 456 verwendet attr(), data() wird weiterhin 123 angezeigt :

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

So wie ich es verstehe, sollten Sie sich wahrscheinlich von Vermischungen attr()und data()Befehlen in Ihrem Code fernhalten, wenn Sie dies nicht müssen. Denn attr()scheint direkt mit dem DOM zu korrespondieren, während es data()mit dem 'Speicher' interagiert, obwohl sein Anfangswert vom DOM stammen kann. Der entscheidende Punkt ist jedoch, dass die beiden nicht unbedingt synchron sind.

Also sei einfach vorsichtig.

Wenn Sie das data-*Attribut im DOM oder im Speicher nicht ändern, haben Sie auf keinen Fall ein Problem. Sobald Sie anfangen, Werte zu ändern, können potenzielle Probleme auftreten.

Vielen Dank an @Clarence Liu für die Antwort von @ Ash sowie für diesen Beitrag .

D.Tate
quelle
5

Wenn Sie auch jQueryUI verwenden, erhalten Sie eine (einfache) Version des :dataSelektors, die das Vorhandensein eines Datenelements überprüft, sodass Sie so etwas wie $("div:data(view)")oder ausführen können $( this ).closest(":data(view)").

Siehe http://api.jqueryui.com/data-selector/ . Ich weiß nicht, wie lange sie es schon haben, aber es ist jetzt da!

Paul
quelle
Dies prüft nur das Vorhandensein des Datenelements, wie Sie gesagt haben. Die Werte werden nicht überprüft (gemäß den Dokumenten). Schön zu wissen
Fractalf
3

Hier ist ein Plugin, das das Leben vereinfacht: https://github.com/rootical/jQueryDataSelector

Verwenden Sie es so:

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.
quelle