32

显然,正如我在评论另一个答案时发现的那样,jQuery(而不是它的底层选择器引擎Sizzle)允许您将参数引用到:not()选择器以及:has()选择器。也就是说

$('div:not("span")')
$('span:has("span")')

Selectors 标准中,引号始终代表字符串,而不是选择器或关键字,因此引用参数:not()始终无效。这在选择器 4 中不会改变。

您还可以通过添加不受支持的 CSS 选择器(例如:nth-last-child(1) 导致选择器完全失败)来查看它的非标准语法:

$('div:not("span"):nth-last-child(1)')
$('span:has("span"):nth-last-child(1)')

是否有任何充分的理由(技术或其他方面)允许在此处引用?唯一想到的可能性是:

  • 与 which 的一致性:contains()允许引用和未引用的参数,如旧的 Selectors 规范中所示。除了:contains()接受字符串/关键字,不接受选择器......

  • 与 using 的自定义伪实现保持一致$.expr[':'],它始终允许带引号和不带引号的参数。

  • 移植到其方法对应项的一致性和易用性.not()以及.has()(只需删除或拆分外部引号并将冒号更改为句点?)。

但我找不到任何支持或反对他们的消息来源。事实上,引用选择器参数本身的能力也没有记录在任何地方,引用和不引用参数之间似乎也没有任何区别:

$('div:not(span)')
$('span:has(span)')
4

1 回答 1

30

这不是特定于:not(...):has(...)选择器的——实际上,Sizzle 中的所有伪都允许引用参数。伪参数的模式定义为:

pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)"

可以在91截至sizzle.js的线上找到831c9c48...

让我们为其添加一些缩进,使其更具可读性。不幸的是,这仍然是一个正则表达式,因此“更具可读性”仍然有很多不足之处:

pseudos = (
    ":(" + characterEncoding + ")" +
    "(?:" +
    "\\(" + // literal open-paren
        "(?:" +

                "(['\"])" + // literal open-quote
                    "((?:\\\\.|[^\\\\])*?)" + // handle backslash escaping
                "\\2" + // close-quote

            "|" + // - OR -

                "(" +
                    "[^()[\\]]*" +
                    "|" +
                    "(?:" +
                        "(?:" + attributes + ")" +
                        "|" +
                        "[^:]" +
                        "|" +
                        "\\\\." +
                    ")*" +
                    "|" +
                    ".*" +
                ")" +

        ")" +
    "\\)" + // literal close-paren
    "|" + // ie, 'or nothing'
")"
);

主要的收获是:可以在伪属性中的参数周围使用单引号或双引号。反斜杠转义得到了正确处理,因此任何任意字符串都可以作为参数传入。请注意,“字符串”部分与上述正则表达式中的“选择器”部分在相同的匹配索引中结束;所以,简而言之,这就是它们被平等对待的原因:因为pseudos模式没有区分两者。编辑:从 jQuery 1.8.2 开始,带引号和不带引号的参数更明确地等效。我似乎无法在 jQuery git 存储库中找到此代码 [帮助将不胜感激],但由 google 托管的 1.8.2 版本具有 a0f48b6ad5322b35383ffcb6e2fa779b8a5fcffc 的 sha1sum,具有 "PSEUDO":在线功能4206,它确实明确地检测到“引用”和“未引用”参数之间的差异,并确保它们都在同一个地方结束。此逻辑不区分参数所针对的伪类型(“位置”与否)。

由于 Sizzle 使用 Javascript 字符串来启动选择过程,因此在将参数传递给函数时,“字符串”和“选择器”之间没有区别。进行这种区分是可能的,但据我所知,实际想要的总是很容易从最基本的上下文中确定(即:使用哪种类型的伪),所以没有真正的理由做出区分。(如果有任何我不知道的模棱两可的情况,请在评论中更正 - 我想知道!)

那么,如果字符串和选择器之间缺乏区别仅仅是实现细节,为什么诸如:eq(...)明确拒绝此类选择之类的伪类呢?

答案很简单:它没有,真的。至少,不是 jQuery 1.8.1。[编辑:从 jQuery 1.8.2 开始,它根本没有。“位置”伪参数的参数可以像其他任何东西一样被引用。以下关于 1.8.1 实现细节的注释留作历史好奇]

诸如这样的功能:eq(...)被实现为:

"eq": function( elements, argument, not ) {
    var elem = elements.splice( +argument, 1 );
    return not ? elements : elem;
}

:eq(...)接收参数时,它仍然是一个裸参数的形式(引号和所有)。与 不同:not(...)的是,这个论点没有经过一个compile(...)阶段。无效参数的“拒绝”实际上是由于快捷方式转换 via +argument,这将导致NaN任何带引号的字符串(反过来,它永远不会匹配任何内容)。这是另一个实现细节,尽管在这种情况下是“正确”的行为(再次,据我所知。是否存在此类函数的非数字参数实际上应该匹配的情况?)

编辑:从 jQuery 1.8.2 开始,事物已经进行了一些重构,并且“位置”伪类不再接收“原始”参数。结果,引用的参数现在被接受:eq(...)等。此更改似乎是另一个错误修复的副作用,因为在af8206ff..的更改日志中没有提及对引用参数的支持,该更改旨在修复处理中的错误,:first以及:lastjQuery 错误 #12303。这个提交是使用一个相对简单git bisectphantomjs 脚本找到的。值得注意的是,在e89d06c4..中的 Sizzle 重写之后,Sizzle 不仅会因为选择器而静默失败,例如:eq("3"),它实际上会抛出一个异常。这应该被视为:eq("3")支持不是预期行为的更多证据。

确实有关于自定义过滤器的基本原理,其参数在某些情况下可以被认为是字符串,有时可以被认为是选择器,无论它们表面上看起来像什么,这取决于它们被评估的方法......但是很多都在接近迂腐的。可以说,在调用函数时,至少没有区别会使事情变得更简单,无论它们代表什么,都期望字符串表示。

简而言之,整个情况可以被认为是一个实现细节,其根源在于选择器首先是作为字符串传递的(否则你如何将它们放入 Sizzle 中?)。

于 2012-09-19T22:47:15.920 回答