селектор даних 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.