селектор даних jquery


184

Мені потрібно вибрати елементи на основі значень, що зберігаються в .data()об'єкті елемента . Як мінімум, я б хотів вибрати властивості даних верхнього рівня за допомогою селекторів, можливо так:

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

Або, можливо, селектор буде у форматі звичайного атрибута:

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

Або у форматі атрибутів, але зі специфікатором, щоб вказати, він знаходиться у .data():

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

Я вважав, що реалізація Джеймса Падолсі виглядає просто, але добре. Селектор формати вище дзеркальних методів, показаних на цій сторінці. Також є цей патч Sizzle .

З якоїсь причини я пригадую, що читав назад, що jQuery 1.4 включає підтримку селекторів значень .data()об'єкта jquery . Однак тепер, коли я його шукаю, я не можу його знайти. Можливо, це був просто запит на функцію, який я бачив. Чи є підтримка в цьому, і я просто не бачу цього?

В ідеалі я хотів би підтримувати підвластивості в даних (), використовуючи крапкові позначення. Подобається це:

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

Я також хотів би підтримати декілька селекторів даних, де знаходяться лише елементи із ВСІМ вказаними селекторами даних. Звичайний множинний селектор jquery робить операцію АБО. Наприклад, $('a.big, a.small')вибирає aтеги з класом bigабо small). Я шукаю І, можливо, так:

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

Нарешті, було б чудово, якби оператори порівняння та функції регулярного вибору були доступними на селекторах даних. Так $(a[:artist.id>5000])було б можливо. Я розумію, що, напевно, я міг би зробити багато з цього, використовуючи filter(), але було б непогано мати простий формат вибору.

Які рішення доступні для цього? Чи є Падольсей Джейма найкращим рішенням на даний момент? Я хвилююсь насамперед щодо продуктивності, а також у додаткових функціях, таких як точкове позначення підменю та множинні селектори даних. Чи є інші реалізації, які підтримують ці речі або є в чомусь кращими?


Використовуйте ядро ​​jquery ui api.jqueryui.com/category/ui-core У ньому є вибір : data ()
regisbsb

Відповіді:


101

Я створив новий dataселектор, який повинен дозволяти вам робити вкладені запити та умови AND. Використання:

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

Шаблон:

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

"Оператор" і "Перевірка" не є обов'язковими. Таким чином, якщо у вас є тільки :data(a.b.c)це буде просто перевірити на truthiness з a.b.c.

Доступні оператори ви можете побачити в коді нижче. Серед них те, ~=що дозволяє тестувати регулярні вирівнювання:

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

Я перевірив його з кількома варіаціями, і, здається, він працює досить добре. Я, мабуть, незабаром додам це як репортаж Github (з повним тестовим набором), тому пильнуйте!

Код:

(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: Дуже мило, я знав, що можу на тебе розраховувати! Повернувся до ліжка, але завтра спробую. Чи можна робити операції АБО? Мені це не потрібно, просто цікаво.
Таурен

1
@JP: Також мені цікаво сприймати інші рішення селектора даних, плюси / мінуси ваших проти їхніх. Наприклад, цей плагін: plugins.jquery.com/project/dataSelector .
Таурен

1
Можна робити операції АБО, але найкраще просто мати два селектори, $("a:data(condition),a:data(orCondition)")... це матиме однаковий ефект. Чим більше функцій ви додасте, тим повільніше буде. Якщо логіка складна, тоді використовуйте $(foo).filter(function(){...}).
Джеймс

3
@JP: Ви коли-небудь перетворювали це на проект github? Я запускаю тест за допомогою jQuery 1.5.1, використовуючи селектор $ ("input: data (test> 400)") на вході з атрибутом html5 data-test = "500", але селектор нічого не повертає.
Джеймс Південь

1
@JamesSouth Це тому, що селектор у цій відповіді використовує низький рівень jQuery.data, який не отримує даних, визначених в атрибутах HTML5. Якщо вам потрібна ця функціональність, ви можете змінити jQuery.dataїї $('selector').data, але це швидкість торгівлі.
Шеф

175

На даний момент я вибираю так:

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

Що, здається, працює чудово, але було б добре, якби jQuery зміг вибрати за цим атрибутом без префікса "data-".

Я не перевіряв це з використанням даних, що додаються до елементів за допомогою jQuery динамічно, так що це може бути крахом цього методу.


Це саме те, що я шукав. Sweet
MikeMurko

109
Це насправді не працює, якщо ви додаєте / змінюєте дані через JS через функцію .data (), селектор атрибутів перевіряє лише DOM, JS зберігає .data () в пам'яті
Кларенс Лю

1
Скажіть, будь ласка, як ви отримуєте доступ за атрибутом, якщо ви додаєте через .data ()?
Павлов Максим Вікторович

1
Я б припустив, що інші відповіді, надані в цій темі, є кращим варіантом для будь-якого іншого, крім дуже простого випадку використання.
Еш

Рішення Дмитра мені здається приємним, оскільки воно не покладається на сторонній код.
Еш

83

Ви також можете використовувати просту функцію фільтрації без плагінів. Це не саме те, що ви хочете, але результат той самий:

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

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

2
Це відмінна ідея, я забув, що filterфункція переходу могла прийняти тестову функцію =) дякую
Кларенс Лю

Як я міг зробити "містить" замість того, щоб дорівнювати Тому?
Аліссо

1
Аліссо, замість name.first == "Tom" використовуйте name.first && name.first.indexOF ("Tom")> -1;
Дмитро

24

Я хочу попередити вас, що $('a[data-attribute=true]')не працює, відповідно до відповіді Ешлі, якщо ви додали дані до елемента DOM за допомогою функції data ().

Він працює так, як можна було очікувати, якщо ви додали фактичний attr даних у свій HTML, але jQuery зберігає дані в пам’яті, тому результати, з яких ви отримаєте, $('a[data-attribute=true]')були б невірними.

Вам потрібно буде використовувати плагін даних http://code.google.com/p/jquerypluginsblog/ , використовувати filterрішення Дмитрія або зробити $ .each над усіма елементами і перевірити .data () ітеративно


У моєму випадку це добре, оскільки я попередньо заповнюю поле на стороні сервера і мені потрібно лише порадити його таким чином при початковому завантаженні. Фільтр, ймовірно, так само швидкий (кожен майже напевно повільніше), але це виграє на читанні.
Дон

11

Є :data()плагін для фільтру, який робить саме це :)

Деякі приклади на основі вашого запитання:

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

Продуктивність не буде надзвичайно великою порівняно з можливим , вибір $._cacheта захоплення відповідних елементів - це найшвидший, але набагато більш крутий і не дуже "jQuery-ey" з точки зору того, як ви дістанетесь до речі (зазвичай ви входите зі сторони елемента). Зверху голови я не впевнений, що це все-таки найшвидше, оскільки процес переходу від унікального Id до елемента перекручений сам по собі в плані продуктивності.

Селектор порівняння, про який ви згадали, найкраще робити в програмі .filter(), вбудованої підтримки для цього в плагіні немає, хоча ви можете додати його без особливих проблем.


дякую за ретельну відповідь. Чи знаєте ви, якщо використання data-*атрибутів HTML5 та вибір на них було б швидше, ніж вибір .data()властивостей? Крім того, будь-яка ідея, де я можу дізнатися більше про кеш $ ._? Я гуглив за це, але не знаходжу багато.
Таурен

@Tauren - Моя вина $.cacheне в цьому $._cache, ви можете бачити, як це реалізовано та використовується в ядрі jQuery тут: github.com/jquery/jquery/blob/master/src/data.js#L4 Коли ви називаєте, .data()це фактично зберігає його як об’єкт в $.cache[elementUniqueID], який представляє собою Id, присвоєний у міру необхідності докорінним чином для кожного елемента, наприклад, 1, 2, 3 і т. д. Цей ідентифікатор сходження буде викритий у jQuery 1.4.3 Я вважаю, виходячи з коментарів git днями. Я б припустив, що маршрут HTML 5 буде швидшим, залежно від того, які оптимізації веб-переглядача доступні (я впевнений, що буде доступно більше).
Нік Крейвер

7

Ви можете встановити data-*атрибут на в'язі за допомогою attr(), а потім вибрати, використовуючи цей атрибут:

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

а тепер для цього в'яза і те, attr()і data()дасть 123 :

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

Однак якщо ви зміните значення на 456 за допомогою attr(), воно data() все одно буде 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

Тож, як я розумію, здається, вам, мабуть, слід уникати переплутань attr()та data()команд у вашому коді, якщо цього не потрібно. Оскільки, attr()здається, відповідає безпосередньо DOM, тоді як data()взаємодіє з «пам'яттю», хоча його початкове значення може бути від DOM. Але ключовим моментом є те, що вони зовсім не обов'язково синхронізуються.

Тому просто будьте обережні.

У будь-якому випадку, якщо ви не змінюєте data-*атрибут у DOM або в пам'яті, у вас не виникне проблем. Незабаром ви почнете змінювати значення, коли можливі проблеми.

Завдяки @Clarence Liu на відповідь @ Ash, а також цей пост .



5

Якщо ви також використовуєте jQueryUI, ви отримуєте з ним (просту) версію :dataселектора, яка перевіряє наявність елемента даних, тому ви можете зробити щось на зразок $("div:data(view)"), або $( this ).closest(":data(view)").

Дивіться http://api.jqueryui.com/data-selector/ . Я не знаю, як довго у них це було, але воно є зараз!


Це перевіряє лише наявність даних, як ви сказали. Він не перевіряє значення (згідно з документами). Приємно знати про це , хоча
Fractalf

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.