Я пишу много плагинов jQuery и настраиваю jQuery-селектора, которые я использую все время, например :focusable
и :closeto
, для предоставления часто используемых фильтров.
например.: focusable выглядит следующим образом
jQuery.extend(jQuery.expr[':'], {
focusable: function (el, index, selector) {
return $(el).is('a, button, :input[type!=hidden], [tabindex]');
};
});
и используется как любой другой селектор:
$(':focusable').css('color', 'red'); // color all focusable elements red
Я заметил, что ни один из доступных селекторов jQuery не может перемещаться назад предков. Полагаю, это потому, что они были разработаны, чтобы следовать основным правилам селектора CSS, которые расширяются.
Возьмем этот пример: который находит метку для ввода с фокусом:
$('input:focus').closest('.form-group').find('.label');
Мне нужен эквивалентный тип сложных селекторов для плагинов, , поэтому было бы полезно предоставить такой селектор как одну строку (чтобы они могли быть предоставлены в качестве параметров плагина).
например. что-то вроде:
$('input:focus < .form-group .label');
или
$('input:focus:closest(.form-group) .label');
Примечание. Предположите, что более сложные операции и что требуется предварительная навигация (я понимаю, что этот конкретный пример может быть выполнен с помощью has
, но это не помогает).
например. он также должен поддерживать это:
options.selector = ':closest(".form-group") .label';
$('input').click(function(){
var label = $(this).find(options.selector);
});
Можно ли расширить селектора jQuery для расширения поведения поиска (а не просто добавить больше логических фильтров)? Как вы расширяете поведение пользовательского поиска?
Обновление:
Кажется, что полный пользовательский селектор (например, <
) не так прост, как добавление псевдоселектора в парсер JQuery Sizzle. В настоящее время я смотрю эту документацию Sizzle, но я нахожу несоответствия с версией jQuery. (например, свойство Sizzle.selectors.order
существует во время выполнения).
Для справки, jQuery сохраняет Sizzle
в свойстве jQuery.find
и Sizzle.selectors
в свойстве jQuery.expr
.
До сих пор я добавил это:
jQuery.expr.match.closest = /^:(?:closest)$/;
jQuery.expr.find.closest = function (match, context, isXML){
console.log("jQuery.expr.find.closest");
};
и назовите его простым тестом: http://jsfiddle.net/z3vwk1ko/2/
но он никогда не попадает в инструкцию console.log, и я все равно получаю "Syntax error, unrecognized expression: unsupported pseudo: closest"
. При трассировке внутри jQuery он пытается применить его как filter
вместо find
, поэтому мне не хватает некоторой ключевой части.
Обновление 2:
Обработка селекторов работает справа налево (см. извлечение из jQuery 1.11.1 ниже), поэтому, если последний аргумент не существует в контексте, он прерывается раньше. Это означает, что в текущем коде jQuery Sizzle не будет происходить переход вверх, если мы хотим найти элемент в другой ветке DOM предка:
// Fetch a seed set for right-to-left matching
i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length;
while (i--) {
token = tokens[i];
// Abort if we hit a combinator
if (Expr.relative[(type = token.type)]) {
break;
}
if ((find = Expr.find[type])) {
// Search, expanding context for leading sibling combinators
if ((seed = find(
token.matches[0].replace(runescape, funescape),
rsibling.test(tokens[0].type) && testContext(context.parentNode) || context
))) {
// If seed is empty or no tokens remain, we can return early
tokens.splice(i, 1);
selector = seed.length && toSelector(tokens);
if (!selector) {
push.apply(results, seed);
return results;
}
break;
}
}
}
Я был удивлен, увидев это, но понимаю теперь, что он сделал механизм правил намного легче писать. Это означает, однако, что мы должны убедиться, что правый конец селектора является настолько конкретным, насколько это возможно, поскольку это оценивается первым. Все, что происходит после этого, - это просто прогрессивная обрезка результирующего набора (я всегда считал, что первые элементы в селекторе должны быть более конкретными для повышения эффективности).