Селектор данных jquery

184

Мне нужно выбрать элементы на основе значений, хранящихся в .data()объекте элемента . Как минимум, я хотел бы выбрать свойства данных верхнего уровня с помощью селекторов, возможно, так:

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

Или, возможно, селектор будет в обычном формате селектора атрибутов:

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

Или в формате атрибута, но с указателем, указывающим, что он находится в .data():

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

Я обнаружил, что реализация Джеймса Падолси выглядит просто, но хорошо. Селекторные форматы выше методов зеркала показаны на этой странице. Есть также этот патч Sizzle .

По какой-то причине я вспоминаю, как читал некоторое время назад, что jQuery 1.4 будет включать поддержку селекторов для значений в jquery. .data() объекте . Однако теперь, когда я ищу это, я не могу найти это. Может быть, это был просто запрос функции, который я видел. Есть ли поддержка для этого, и я просто не вижу этого?

В идеале, я хотел бы поддерживать под-свойства в data () с использованием точечной нотации. Как это:

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

Я также хотел бы поддерживать несколько селекторов данных, где находятся только элементы со ВСЕМИ указанными селекторами данных. Обычный множественный селектор jquery выполняет операцию ИЛИ. Например, $('a.big, a.small')выбирает aтеги с классом bigили small). Я ищу AND, возможно, вот так:

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

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

Какие решения доступны для этого? Является ли Jame's Padolsey лучшим решением на данный момент? В первую очередь меня беспокоит производительность, а также дополнительные функции, такие как точечная нотация под-свойства и несколько селекторов данных. Существуют ли другие реализации, которые поддерживают эти вещи или лучше в некотором роде?

Таурен
источник
Используйте ядро ​​пользовательского интерфейса jquery 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 () в памяти
Clarence Liu
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 == "Том" используйте name.first && name.first.indexOF ("Том")> -1;
Дмитрий
24

Я хочу предупредить вас, что $('a[data-attribute=true]') , согласно ответу Эшли это не работает, если вы прикрепили данные к элементу DOM через функцию data ().

Он работает так, как вы ожидаете, если вы добавите фактический 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-эй» с точки зрения того, как вы добираетесь до вещи (вы обычно входите со стороны элемента). Что касается моей головы, я не уверен, что в любом случае это самый быстрый процесс, поскольку процесс перехода от уникального идентификатора к элементу запутан сам по себе с точки зрения производительности.

Упомянутый вами селектор сравнения будет лучше всего сделать в a .filter(), в плагине нет встроенной поддержки для этого, хотя вы можете добавить его без особых проблем.

Ник Крейвер
источник
спасибо за подробный ответ. Знаете ли вы, если использование data-*атрибутов HTML5 и их выбор будет быстрее, чем выбор по .data()свойствам? Кроме того, есть идеи, где я могу узнать больше о кэше $ ._? Я гуглил за это, но особо ничего не нахожу.
Таурен
@Tauren - Я виноват, что это $.cacheне так $._cache, вы можете увидеть, как он реализован и используется в ядре jQuery, здесь: github.com/jquery/jquery/blob/master/src/data.js#L4 Когда вы вызываете, .data()он фактически хранит его как объект in $.cache[elementUniqueID], который является идентификатором, назначаемым при необходимости инкриминирующим образом каждому элементу, например, 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, а также на этот пост .

D.Tate
источник
5

Если вы также используете jQueryUI, вы получаете (простую) версию :dataселектора, которая проверяет наличие элемента данных, поэтому вы можете сделать что-то вроде $("div:data(view)")или $( this ).closest(":data(view)").

Смотрите http://api.jqueryui.com/data-selector/ . Я не знаю, как долго у них это было, но оно есть сейчас!

Павел
источник
Это только проверяет наличие элемента данных, как вы сказали. Он не проверяет значения (в соответствии с документами). Приятно знать об этом, хотя
Фрактальф
3

Вот плагин, который упрощает жизнь https://github.com/rootical/jQueryDataSelector

Используйте это так:

data selector           jQuery selector
  $$('name')              $('[data-name]')
  $$('name', 10)          $('[data-name=10]')
  $$('name', false)       $('[data-name=false]')
  $$('name', null)        $('[data-name]')
  $$('name', {})          Syntax error
Рутика В.
источник