是否可以创建导航祖先的自定义jQuery选择器?例如a:最近或:父母选择器 [英] Is it possible to create custom jQuery selectors that navigate ancestors? e.g. a :closest or :parents selector

查看:135
本文介绍了是否可以创建导航祖先的自定义jQuery选择器?例如a:最近或:父母选择器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了很多jQuery插件,并且我一直使用自定义jQuery选择器,如:focusable :closeto 提供常用的过滤器。

I write a lot of jQuery plugins and have custom jQuery selectors I use all the time like :focusable and :closeto to provide commonly used filters.

例如。 :focusable看起来像这样

e.g. :focusable looks like this

jQuery.extend(jQuery.expr[':'], {
    focusable: function (el, index, selector) {
        return $(el).is('a, button, :input[type!=hidden], [tabindex]');
    };
});

并且与其他选择器一样使用:

and is used like any other selector:

$(':focusable').css('color', 'red');  // color all focusable elements red

我注意到没有一个可用的jQuery选择器可以导航回祖先。我收集的原因是它们被设计为遵循向下钻取的基本CSS选择器规则。

I notice none of the jQuery selectors available can navigate back up ancestors. I gather that is because they were designed to follow the basic CSS selector rules which drill down.

以此为例:找到输入的标签焦点:

$('input:focus').closest('.form-group').find('.label');

我需要等效类型的插件复杂选择器,所以提供它会很有用这样的选择器作为单个字符串(因此它们可以作为插件的选项提供)。

I need the equivalent type of complex selectors for plugins, so it would be useful to provide such a selector as a single string (so they can be provided as options to the plugin).

例如例如:

$('input:focus < .form-group .label');

$('input:focus:closest(.form-group) .label');

注意:请假设更复杂的操作,并且需要 的祖先导航(我意识到这个特定的例子可以用来完成,但这没有用。)

Note: Please assume more complex operations and that ancestor navigation is required (I realize this particular example can be done with has, but that does not help).

例如它还需要支持:

options.selector = ':closest(".form-group") .label';

$('input').click(function(){
    var label = $(this).find(options.selector);
});

是否可以扩展jQuery选择器以扩展搜索行为(而不仅仅是添加更多布尔值过滤器)?如何扩展自定义搜索行为?

它似乎是一个完整的自定义选择器(比如< )就像在jQuery的Sizzle解析器中添加伪选择器一样简单。我目前正在查看这个 Sizzle文档,但我发现jQuery版本不一致。 (例如,没有 Sizzle.selectors.order 属性在运行时存在。)

It appears a complete custom selector (like <) would not be as easy as adding a pseudo selector to jQuery's Sizzle parser. I am currently looking at this Sizzle documentation, but I am finding inconsistencies with the jQuery version. (e.g. no Sizzle.selectors.order property exists at runtime).

作为参考,jQuery存储<$ c在其 jQuery.find 属性和 Sizzle.selectors 上的$ c> Sizzle code> jQuery.expr property。

For reference, jQuery stores Sizzle on its jQuery.find property and Sizzle.selectors on its jQuery.expr property.

到目前为止,我添加了这个:

so far I have added this:

 jQuery.expr.match.closest = /^:(?:closest)$/;
 jQuery.expr.find.closest = function (match, context, isXML){
     console.log("jQuery.expr.find.closest");
 };

并通过一个简单的测试来调用它: http://jsfiddle.net/z3vwk1ko/2/

and call it with a simple test: http://jsfiddle.net/z3vwk1ko/2/

但它永远不会进入console.log语句而我仍然get 语法错误,无法识别的表达式:unsupported pseudo:nearest。在jQuery中进行跟踪时,它试图将其应用为过滤器而不是 find ,因此我遗漏了一些关键字部分。

but it never gets to the console.log statement and I still get "Syntax error, unrecognized expression: unsupported pseudo: closest". On tracing inside jQuery it is trying to apply it as a filter instead of a find, so I am missing some key part.

选择器的处理工作从右到左(参见下面jQuery 1.11.1的摘录)所以如果上一个参数在上下文中不存在,它会提前中止。这意味着在我们想要在祖先的另一个DOM分支中寻找元素的常见情况下,当前的jQuery Sizzle代码不会向上导航:

The processing for selectors works right-to-left (see extract from jQuery 1.11.1 below) so if the last argument does not existing in the context, it aborts early. This means navigating upwards will not occur with the current jQuery Sizzle code in the common case where we want to look for an element in another DOM branch of an ancestor:

// 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;
        }
    }
}

我很惊讶地看到这个,但现在意识到它使规则引擎更容易编写。但这确实意味着我们需要确保选择器的右端尽可能特定,因为它首先被评估。之后发生的一切只是对结果集进行渐进式修剪(我一直认为选择器中的第一项必须更具体地提高效率)。

I was surprised to see this, but realise now that it made the rule engine much easier to write. It does mean however we need to make sure the right-hand end of a selector is as specific as possible, as that is evaluated first. Everything that happens after that is just progressive pruning of the result set (I had always assumed the first items in the selector had to be more specific to increase efficiency).

推荐答案

基于众多评论,以及为什么这是不可能的的一个详细解释,我想到我想要的目标可以用 $(document).find(),但有一些目标元素的概念。也就是说,在选择器中定位原始查询元素的某种方式。

Based on numerous comments, and one detailed explanation on why this is impossible, it occurred to me that the aim I wanted could be met with a $(document).find(), but with some concept of targeted elements. That is, some way to target the original query elements, within the selector.

为此,我想出了以下内容,:this 选择器,其工作原理如下(无双关语):

To that end I came up with the following, a :this selector, which works like this (no pun intended):

// Find all labels under .level3 classes that have the .starthere class beneath them
$('.starthere').findThis('.level3:has(:this) .label')

这使我们现在可以有效地搜索DOM,然后在单个选择器字符串中搜索到相邻的分支!即它确实这样做(但在一个选择器中):

This allows us to now, effectively, search up the DOM then down into adjacent branches in a single selector string! i.e. it does the same job this does (but in a single selector):

$('.starthere').parents('.level3').find('.label')



步骤:



1 - 添加新的 jQuery.findThis 方法

2 - 如果选择器:此,替换id搜索并从文档搜索

2 - If the selector has :this, substitute an id search and search from document instead

3 - 如果选择器不是c获得:此过程通常使用原始查找

3 - If the selector does not contain a :this process normally using the original find

4 - 使用选择器测试,如 $('。target')。find('。ancestor:has(:this).label')选择祖先内的标签(s)目标元素

4 - Test with selector like $('.target').find('.ancestor:has(:this) .label') to select a label within the ancestor(s) of the targetted element(s)

这是基于注释的修订版本,它不会替换现有的查找并使用生成的唯一ID。 / p>

JSFiddle: http://jsfiddle.net/ TrueBlueAussie / z3vwk1ko / 36 /



This is the revised version, based on comments, that does not replace the existing find and uses a generated unique id.

// 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 属性属性开始(这没有问题,但不像我想的那样整洁)

  • This leaves a blank id attribute on targetted elements that did not have an id attribute to begin with (this causes no problem, but is not as neat as I would like)

由于它的方式从文档中搜索,它只能模拟 parents()而不是 nearest(),但我有一种感觉,我可以使用类似的方法并在此代码中添加:nearest()伪选择器。

Because of the way it has to search from document, it can only emulate parents() and not closest(), but I have a feeling I can use a similar approach and add a :closest() pseudo selector to this code.

下面的第一个版本:

1 - 保存jQuery的查找参考方法

1 - Save jQuery's find method for reference

2 - 替换新的 jQuery.find 方法

2 - Substitute a new jQuery.find method

3 - 如果选择器有:这个,请替换id搜索并从<$ c $搜索c> document 而不是

3 - If the selector has :this, substitute an id search and search from document instead

4 - 如果选择器不包含:这个处理通常使用原始查找

4 - If the selector does not contain a :this process normally using the original find

5 - 使用选择器进行测试,例如 $('。target ')。find('。ancestor:has(:this)')选择目标元素的祖先

5 - Test with selector like $('.target').find('.ancestor:has(:this)') to select the ancestor(s) of the targetted elements

// 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内部的新手:)

This is based on 6 hours slaving over jQuery/Sizzle source code, so be gentle. Always happy to hear of ways to improve this replacement find as I am new to the internals of jQuery :)

它现在意味着我可以解决如何进行原始标签示例的初始问题:

It now means I can solve the initial problem of how to do the original label example:

options.selector = ".form-group:has(:this) .label";

$('input').click(function(){
    var label = $(this).find(options.selector);
});

例如。 http://jsfiddle.net/TrueBlueAussie/z3vwk1ko/25/

这篇关于是否可以创建导航祖先的自定义jQuery选择器?例如a:最近或:父母选择器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆