是否可以创建导航祖先的自定义jQuery选择器? 例如a:最近或:父母选择器
我编写了很多jQuery插件,并且我一直使用自定义jQuery选择器:focusable
和:closeto
来提供常用的filter。
例如: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选择器来扩展搜索行为(而不仅仅是添加更多的布尔filter)? 如何扩展自定义搜索行为?
更新:
看起来完整的自定义选择器(比如<
)就像在jQuery的Sizzle解析器中添加伪选择器一样简单。 我目前正在查看这个Sizzle文档 ,但我发现jQuery版本不一致。 (例如,运行时不存在Sizzle.selectors.order
属性)。
作为参考,jQuery在其jQuery.find
属性上存储Sizzle
,在其jQuery.expr
属性上存储Sizzle.selectors
。
到目前为止我添加了这个:
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摘录),因此如果上一个参数在上下文中不存在,则它会提前中止。 这意味着在我们想要在祖先的另一个DOM分支中寻找元素的常见情况下,当前的jQuery Sizzle代码不会向上导航:
// 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; } } }
我很惊讶地看到这一点,但现在意识到它使规则引擎更容易编写。 但这确实意味着我们需要确保选择器的右端尽可能具体 ,因为首先要对其进行评估。 之后发生的一切只是对结果集进行渐进式修剪(我一直认为选择器中的第一项必须更具体,以提高效率)。
基于众多评论,以及为什么这是不可能的一个详细解释,我想到我想要的目标可以用$(document).find()
,但是有一些目标元素的概念。 也就是说,在选择器中以某种方式定位原始查询元素。
为此我想出了以下内容,a :this
选择器,它的工作原理如此(没有双关语):
// Find all labels under .level3 classes that have the .starthere class beneath them $('.starthere').findThis('.level3:has(:this) .label')
这使我们现在可以有效地搜索DOM,然后在单个选择器字符串中向下搜索相邻的分支! 即它做同样的工作(但在一个选择器中):
$('.starthere').parents('.level3').find('.label')
脚步:
1 – 添加一个新的jQuery.findThis
方法
2 – 如果选择器具有:this
,则替换id搜索并从document
搜索
3 – 如果选择器不包含:this
过程通常使用原始find
4 – 使用$('.target').find('.ancestor:has(:this) .label')
等选择器进行测试$('.target').find('.ancestor:has(:this) .label')
在目标元素的祖先中选择一个标签
这是基于注释的修订版本,它不会替换现有查找并使用生成的唯一ID。
JSFiddle: http : //jsfiddle.net/TrueBlueAussie/z3vwk1ko/36/
// Add findThis method to jQuery (with a custom :this check) jQuery.fn.findThis = function (selector) { // If we have a :this selector if (selector.indexOf(':this') > 0) { var ret = $(); for (var i = 0; i < this.length; i++) { var el = this[i]; var id = el.id; // If not id already, put in a temp (unique) id el.id = 'id'+ new Date().getTime(); var selector2 = selector.replace(':this', '#' + el.id); ret = ret.add(jQuery(selector2, document)); // restore any original id el.id = id; } ret.selector = selector; return ret; } // do a normal find instead return this.find(selector); } // Test case $(function () { $('.starthere').findThis('.level3:has(:this) .label').css({ color: 'red' }); });
已知的问题:
-
这会在没有
id
属性的目标元素上留下一个空白的id
属性(这没有问题,但不像我想的那样整洁) -
由于它必须从文档搜索的方式,它只能模拟
parents()
而不是closest()
,但我有一种感觉,我可以使用类似的方法,并为此代码添加:closest()
伪选择器。
以下第一个版本:
1 - 保存jQuery的find
方法以供参考
2 - 替换一个新的jQuery.find
方法
3 - 如果选择器具有:this
,则替换id搜索并从document
搜索
4 - 如果选择器不包含:this
过程通常使用原始find
5 - 使用选择器测试,如$('.target').find('.ancestor:has(:this)')
选择目标元素的祖先
JSFiddle: http : //jsfiddle.net/TrueBlueAussie/z3vwk1ko/24/
// Save the original jQuery find we are replacing jQuery.fn.findorig = jQuery.fn.find // Replace jQuery find with a custom :this hook jQuery.fn.find = function (selector) { // If we have a :this selector if (selector.indexOf(':this') > 0) { var self = this; var ret = $(); for (var i = 0; i < this.length; i++) { // Save any existing id on the targetted element var id = self[i].id; if (!id) { // If not id already, put in a temp (unique) one self[i].id = 'findme123'; } var selector2 = selector.replace(':this', '#findme123'); ret = ret.add(jQuery(selector2, document)); // restore any original id self[i].id = id; } ret.selector = selector; return ret; } return this.findorig(selector); } // Test case $(function () { $('.starthere').find('.level3:has(:this)').css({ color: 'red' }); });
这是基于jQuery / Sizzle源代码的6小时奴役,所以要温和 。 总是很高兴听到改进这个替换的方法,因为我是jQuery的内部新手:)
它现在意味着我可以解决如何做原始标签示例的初始问题:
options.selector = ".form-group:has(:this) .label"; $('input').click(function(){ var label = $(this).find(options.selector); });
例如http://jsfiddle.net/TrueBlueAussie/z3vwk1ko/25/
是不可能的,我会说明原因
要创建自定义选择器,您应该需要以下内容:
jQuery.extend(jQuery.expr[':'], { focusable: function (el, index, selector) { return $(el).is('a, button, :input[type!=hidden], [tabindex]'); }; })
选择器逻辑允许您从当前选择中选择元素的子集 ,例如,如果$(“div”)选择页面上的所有div,则以下两个示例将选择所有没有子元素的div:
$("div:empty") $("div").filter(":empty")
根据jQuery允许你编写自定义选择器的方式,没有办法返回原始集之外的元素,你只能在被问到时为集合中的每个元素选择true或false。 例如,“:contains”选择器以这种方式在2.1.1版本的jQuery上实现:
function ( text ) { return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }
但是……(也许你想先看一下最后一个编辑)也许,你会发现给定元素的所有祖先的选择器是有用的,例如,想象一下这个场景:
Lorem ipsum sir amet dolor...
并且,你想要涉及myelementid的div父元素,element1和所有祖先等等,你可以使用“has”选择器,这样:
$("*:has('#myelementid')")
第一个选择器将返回所有元素,通过“has”过滤将返回所有具有#myelementid作为后代,ergo,#myelementid的所有祖先的元素。 如果你担心性能(也许你应该),你需要用更具体的东西替换’*’,也许你正在寻找div
$("div:has('#myelementid')")
或者对于给定的class级
$(".agivenclass:has('#myelementid')")
我希望这有帮助
编辑
您可以使用jquery的parents()“selector”,因为它返回元素的所有祖先,甚至包括文档的根元素(即html标记),这样:
$('#myelementid').parents()
如果要过滤某些元素(例如div),可以使用:
$('#myelementid').parents(null, "div")
或这个:
$('#myelementid').parents().filter("div")