为什么函数伪如:not()和:has()允许引用参数? [英] Why do functional pseudos such as :not() and :has() allow quoted arguments?

查看:150
本文介绍了为什么函数伪如:not()和:has()允许引用参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

显然,正如我在评论另一个答案,jQuery(而不是它的底层选择器引擎 Sizzle )允许你引用<$的参数c $ c>:not()选择器以及:has()选择器。 To wit

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

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



您还可以通过添加不支持的CSS选择器,例如:nth-​​last-child(1) 导致选择器完全失败

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

在这里允许引用是否有任何技术或其他理由?想到的唯一可能性是:




  • 的一致性:contains()允许引用和不引用的参数,如旧选择器规范。除:contains()接受字符串/关键字,而不是选择器...


  • 与实现的一致性自定义伪使用 $。expr [':'] ,它总是允许带引号和不带引号的参数。


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




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

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


解决方案

这不是特定于:不是(...):has(...) selectors-实际上,Sizzle中的所有伪
都允许引用参数。 pseudos'参数
的模式定义为:

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

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



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

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

(['\\ \\])+ // literal open-quote
((?:\\\\。| [^ \\\\])*?)+ //处理反斜杠转义
\\2+ // close-quote

|+ // - 或 -

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

)+
\\ )+ // literal close-paren
|+ // ie,'或者没有'

);

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



由于Sizzle使用Javascript字符串启动选择过程,
当参数
传递给函数时,string和selector之间没有区别。做出这种区分可能是
,但据我所知,实际需要的是从最基本的上下文中容易确定的
(即:什么类型的
伪正在使用),所以没有真正的理由让
区别开来。 (如果有任何我不知道的含糊不清的
情况,请在评论中更正 - 我想知道!)



<那么,如果字符串和选择器之间缺乏区别是
仅仅是实现细节,那么为什么伪显示如:eq(...)
拒绝这样的选择?



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



等功能:eq(...)实现为:

 eq:function(elements,参数,而不是){
var elem = elements.splice(+ argument,1);
退货不?要素:元素;
}

:eq(... )接收参数,它仍然是一个裸参数的
形式(引号和全部)。与:not(...)不同,这个
参数不会通过编译(...)阶段。
的拒绝无效参数实际上是由于通过
+参数进行快捷方式转换,这将导致 NaN 对于任何引用的字符串(在
转,从不匹配任何东西)。这是另一个实现
的细节,虽然在这种情况下正确表现一个(同样,据我所知,到目前为止是
。是否存在这样的$ b的非数字参数的情况$ b函数实际上应该匹配?)



编辑:从jQuery 1.8.2开始,事情已经被重构了一些,和
位置伪不再接收原始参数。因此,
引用的参数现在在:eq(...)等中被接受。这个更改
似乎是另一个错误修正的副作用,因为在 af8206ff .. ,旨在修复处理时出错:首先:最后,jQuery bug#12303 。使用 git bisect 相对简单的phantomjs脚本找到此提交。值得注意的是,在 e89d06c4 .. 重写Sizzle之后,Sizzle不仅会失败对于诸如:eq(3)之类的选择器,它实际上会抛出异常。这应该被视为:eq(3)支持不是预期行为的更多证据。



确实存在关于自定义过滤器的基本原理,其参数
在某些情况下可以被认为是字符串,有时候是
选择器,无论它们表面看起来如何,取决于
方法他们被评估了...但是那个迂腐的人正在接近
。应该说,没有区别
至少可以在调用函数时使事情更简单,没有
它们可能代表什么,期望字符串表示。



简而言之,整个情况可以被认为是一个实现
的细节,并且根植于选择器首先作为
字符串传递的事实(如何处理你让他们进入Sizzle?)。


Apparently, as I've discovered while commenting on another answer, jQuery (rather its underlying selector engine Sizzle) lets you quote the argument to the :not() selector as well as the :has() selector. To wit:

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

In the Selectors standard, quotes are always representative of a string and never of a selector or a keyword, so quoting the argument to :not() is always invalid. This will not change in Selectors 4.

You can also see that it's non-standard syntax by adding an unsupported CSS selector such as :nth-last-child(1) causing the selector to fail completely:

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

Is there any good reason, technical or otherwise, for allowing quotes here? The only possibilities that come to mind are:

  • Consistency with :contains() which allows both quoted and unquoted arguments, as seen in the old Selectors spec. Except :contains() accepts strings/keywords, not selectors...

  • Consistency with the implementation of custom pseudos using $.expr[':'], which always allows quoted and unquoted arguments.

  • Consistency and ease of porting to their method counterparts .not() and .has() (just remove or split the outer quotes and change colons to periods?).

But I can't find any sources to support or oppose them. In fact, the ability to quote selector arguments itself isn't documented anywhere either, nor does there appear to be any difference between quoting and not quoting the argument:

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

解决方案

This isn't specific to :not(...) and :has(...) selectors- actually, all pseudos in Sizzle allow for quoted arguments. The pattern for pseudos' arguments is defined as:

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

Which can be found on line 91 of sizzle.js as of 831c9c48...

Let's add some indentation to that, to make it a bit more readable. Unfortunately, this is still a regexp, so "a bit more readable" still leaves a lot to be desired:

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

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

            "|" + // - OR -

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

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

The main take-away from this is: either single or double-quotes can be used around the argument in a pseudo-attribute. Backslash escaping is properly handled, and so any arbitrary string could be passed in as an argument. Note that the "string" part winds up in the same match index as the "selector" part in the above regexp; so, in short, that is why they are treated equally: because the pseudos pattern does not distinguish between the two. edit: as of jQuery 1.8.2, arguments with and without quotes are more-explicitly equivalent. I cannot seem to find this code in the jQuery git repository [help would be appreciated], but the version of 1.8.2 hosted by google, having the sha1sum of a0f48b6ad5322b35383ffcb6e2fa779b8a5fcffc, has a "PSEUDO": function on line 4206, which does explicitly detect a difference between "quoted" and "unquoted" arguments, and ensures they both wind up in the same place. This logic does not distinguish between the type of pseudo ("positional" or not) which the argument is for.

As Sizzle uses Javascript strings to kick off the selection process, there is no distinction between "string" and "selector" when arguments are passed in to functions. Making that kind of distinction would be possible, but as far as I am aware, what is actually desired is always easily determined from the most basic of context (ie: what type of pseudo is being used), so there is no real reason to make the distinction. (please correct in comments if there are any ambiguous situations which I am unaware of- I'd like to know!).

So then, if the lack of distinction between strings and selectors is a mere implementation detail, why do pseudos such as :eq(...) explicitly reject such selections?

The answer is simple: it doesn't, really. At least, not as of jQuery 1.8.1. [edit: as of jQuery 1.8.2, it doesn't at all. The arguments of "positional" pseudos can be quoted just like anything else. The below notes regarding the implementation details of 1.8.1 are left as a historical curiosity]

Functions such as :eq(...) are implemented as:

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

At the time that :eq(...) receives the argument, it is still in the form of a bare argument (quotes and all). Unlike :not(...), this argument doesn't go through a compile(...) phase. The "rejection" of the invalid argument is actually due to the shortcut-casting via +argument, which will result in NaN for any quoted string (which in turn, never matches anything). This is yet another implementation detail, though in this case a "correctly" behaving one (again, as far as I am aware. Are there situations where non-numeric arguments to such functions should in fact match?)

edit: As of jQuery 1.8.2, Things have been refactored somewhat, and "positional" pseudos no-longer receive the "raw" argument. As a result, quoted arguments are now accepted in :eq(...) and the like. This change appears to have been a side-effect of another bugfix, as there is no mention of support for quoted arguments in the changelog for af8206ff.., which was intended to fix an error in handling :first and :last, jQuery bug #12303. This commit was found using git bisect and a relatively simple phantomjs script. It is notable that after the Sizzle rewrite in e89d06c4.., Sizzle would not merely fail silently for selectors such as :eq("3"), it would actually throw an exception. That should be taken as yet more evidence that :eq("3") support is not intended behaviour.

There are indeed rationales regarding custom filters, whose arguments could in some cases be thought of as strings, and sometimes as selectors, no matter what they superficially look like, depending on the method in which they are evaluated... but that much is approaching the pedantic. It should suffice to say that not having a distinction at the least makes things simpler when calling functions which, no matter what they may represent, expect a string representation.

In short, the whole situation can be thought of as an implementation detail, and is rooted in the fact that selectors are passed around as strings in the first place (how else would you get them into Sizzle?).

这篇关于为什么函数伪如:not()和:has()允许引用参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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