为啥诸如 :not() 和 :has() 之类的函数伪函数允许引用参数?

Posted

技术标签:

【中文标题】为啥诸如 :not() 和 :has() 之类的函数伪函数允许引用参数?【英文标题】:Why do functional pseudos such as :not() and :has() allow quoted arguments?为什么诸如 :not() 和 :has() 之类的函数伪函数允许引用参数? 【发布时间】:2012-09-10 15:12:22 【问题描述】:

显然,正如我在评论 another answer 时发现的那样,jQuery(而不是它的底层选择器引擎 Sizzle)允许您将参数引用到 :not() 选择器和 :has() 选择器。 To wit:

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

在Selectors standard 中,引号始终代表字符串,而不是选择器或关键字,因此引用:not() 的参数始终无效。 This will not change in Selectors 4.

您还可以通过添加unsupported CSS selector 如:nth-last-child(1) causing the selector to fail completely 来查看它的非标准语法:

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

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

:contains() 一致,允许引用和未引用的参数,如the old Selectors spec 所示。除了:contains() 接受字符串/关键字,而不是选择器...

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

一致且易于移植到他们的方法对应 .not().has()(只需删除或拆分外部引号并将冒号更改为句点?)。

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

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

【问题讨论】:

这很可能是 Sizzle 的怪癖,而不是 jQuery 本身。 @BoltClock 对不起,我的例子很糟糕,我认为引号的唯一目的是转义。 不是一个答案,但 jQuery 表示他们自己借鉴 CSS 选择器规范,而不是忠实地实现它。也许 John Resig 会停下来回答。 @Explosion Pills:是的。在这种情况下,他们真的应该将主页上的“CSS3 Compliant”更改为“CSS3 Compatible”或类似的东西;) 看看测试套件,我想说这不是应该工作的(不是引用:not:has的单个测试)github.com/jquery/sizzle/blob/master/test/unit/selector.js 【参考方案1】:

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

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

可以在91 of sizzle.js as of 831c9c48...线上找到

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

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

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

            "|" + // - OR -

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

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

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

由于 Sizzle 使用 javascript 字符串来启动选择过程, 参数时“字符串”和“选择器”之间没有区别 被传递给函数。做出这种区分将是 可能,但据我所知,真正想要的总是 很容易从最基本的上下文中确定(即:什么类型的 正在使用伪),因此没有真正的理由使 区别。 (如有歧义,请在 cmets 中更正 我不知道的情况-我想知道!)

那么,如果字符串和选择器之间缺乏区别是 仅仅是实现细节,为什么要显式地使用诸如:eq(...) 之类的伪指令 拒绝这样的选择?

答案很简单:它没有,真的。至少,不像 jQuery 1.8.1。 [edit: 从 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(...) 阶段。的“拒绝” 无效的参数实际上是由于快捷方式转换通过 +argument,对于任何带引号的字符串(在 转,从不匹配任何东西)。这是另一个实现 细节,尽管在这种情况下,一个“正确”的行为 (同样,到目前为止 据我所知。是否存在这样的非数字参数 函数实际上应该匹配?)

编辑: 从 jQuery 1.8.2 开始,事物已经进行了一些重构,并且 “位置”伪类不再接收“原始”参数。因此, :eq(...) 等现在接受引用的参数。这种变化 似乎是另一个错误修复的副作用,因为在 af8206ff.. 的更改日志中没有提到对引用参数的支持,这是为了修复 an error in handling :first and :last, jQuery bug #12303。这个提交是使用git bisect 和a relatively simple phantomjs script 找到的。值得注意的是,在e89d06c4.. 中的 Sizzle 重写之后,Sizzle 不仅会在 :eq("3") 等选择器上静默失败,它实际上还会抛出异常。这应该被视为更多证据表明:eq("3") 支持不是预期行为。

确实有关于自定义过滤器的基本原理,其论点 在某些情况下可以被认为是字符串,有时是 选择器,无论它们表面上看起来像什么,取决于 评估它们的方法......但即将到来 迂腐的。应该说没有区别就足够了 至少在调用函数时让事情变得更简单,没有 无论它们可能代表什么,都期望一个字符串表示。

简而言之,整个情况可以认为是一个实现 细节,并且植根于选择器作为 首先是字符串(否则您如何将它们放入 Sizzle 中?)。

【讨论】:

你没听说过raw strings吗?或者也许只有我和 Python…… +1 花时间格式化正则表达式并尝试解释它(这是一项艰巨的任务)。赏金由你来拿。 我想我真的需要重新经历一遍。你的评论促使我再看一眼,我想我可能读错了,尽管我做了所有的格式化工作。自从 jQuery 1.8.2 发布以来,我还没有真正经历过标记化过程,所以我真的应该确保推理仍然准确。 @YatharthROCK:我不确定提到原始字符串是指什么。你能扩展你的意思吗?如果您的意思是我应该在上面使用"\\(" 等的替代方法:目的是完全模仿原始正则表达式字符串的内容。我对它所做的唯一更改是拆分和重新缩进组件(这样应该很容易找到原始字符串中的对应点)。否则,我可能会使用 PCRE /x 语法来简单地显示等效的正则表达式,而不是“相同的”。 原始字符串是完全按照输入的方式解释的字符串——无需再次转义字符。它们在处理正则表达式时会派上用场。通过[我的评论](***.com/questions/12475595/…,我的意思是如果你使用原始字符串,你就不必转义所有的反斜杠。它看起来会好很多。

以上是关于为啥诸如 :not() 和 :has() 之类的函数伪函数允许引用参数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥jQuery或诸如getElementById之类的DOM方法找不到元素?

为啥jQuery或诸如getElementById之类的DOM方法找不到元素?

为啥jQuery或诸如getElementById之类的DOM方法找不到元素?

为啥jQuery或诸如getElementById之类的DOM方法找不到元素?

为啥jQuery或诸如getElementById之类的DOM方法找不到元素?

为啥隐藏了诸如 index.html 之类的 URL 的“路径”?