mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Selector: Make selector lists work with qSA
again
jQuery 3.6.2 started using `CSS.supports( "selector(SELECTOR)" )` before using
`querySelectorAll` on the selector. This was to solve gh-5098 - some selectors,
like `:has()`, now had their parameters parsed in a forgiving way, meaning
that `:has(:fakepseudo)` no longer throws but just returns 0 results, breaking
that jQuery mechanism.
A recent spec change made `CSS.supports( "selector(SELECTOR)" )` always use
non-forgiving parsing, allowing us to use this API for what we've used
`try-catch` before.
To solve the issue on the spec side for older jQuery versions, `:has()`
parameters are no longer using forgiving parsing in the latest spec update
but our new mechanism is more future-proof anyway.
However, the jQuery implementation has a bug - in
`CSS.supports( "selector(SELECTOR)" )`, `SELECTOR` needs to be
a `<complex-selector>` and not a `<complex-selector-list>`. Which means that
selector lists now skip `qSA` and go to the jQuery custom traversal:
```js
CSS.supports("selector(div:valid, span)"); // false
CSS.supports("selector(div:valid)"); // true
CSS.supports("selector(span)"); // true
```
To solve this, this commit wraps the selector list passed to
`CSS.supports( "selector(:is(SELECTOR))" )` with `:is`, making it a single
selector again.
See:
* https://w3c.github.io/csswg-drafts/css-conditional-4/#at-supports-ext
* https://w3c.github.io/csswg-drafts/selectors-4/#typedef-complex-selector
* https://w3c.github.io/csswg-drafts/selectors-4/#typedef-complex-selector-list
Fixes gh-5177
Closes gh-5178
Ref w3c/csswg-drafts#7280
(cherry picked from commit 09d988b774
)
This commit is contained in:
parent
0acbe6433c
commit
848de62542
@ -306,15 +306,20 @@ function find( selector, context, results, seed ) {
|
|||||||
|
|
||||||
// `qSA` may not throw for unrecognized parts using forgiving parsing:
|
// `qSA` may not throw for unrecognized parts using forgiving parsing:
|
||||||
// https://drafts.csswg.org/selectors/#forgiving-selector
|
// https://drafts.csswg.org/selectors/#forgiving-selector
|
||||||
// like the `:has()` pseudo-class:
|
// like the `:is()` pseudo-class:
|
||||||
// https://drafts.csswg.org/selectors/#relational
|
// https://drafts.csswg.org/selectors/#matches
|
||||||
// `CSS.supports` is still expected to return `false` then:
|
// `CSS.supports` is still expected to return `false` then:
|
||||||
// https://drafts.csswg.org/css-conditional-4/#typedef-supports-selector-fn
|
// https://drafts.csswg.org/css-conditional-4/#typedef-supports-selector-fn
|
||||||
// https://drafts.csswg.org/css-conditional-4/#dfn-support-selector
|
// https://drafts.csswg.org/css-conditional-4/#dfn-support-selector
|
||||||
if ( support.cssSupportsSelector &&
|
if ( support.cssSupportsSelector &&
|
||||||
|
|
||||||
|
// `CSS.supports( "selector(...)" )` requires the argument to the
|
||||||
|
// `selector` function to be a `<complex-selector>`, not
|
||||||
|
// a `<complex-selector-list>` which our selector may be. Wrapping with
|
||||||
|
// `:is` works around the issue and is supported by all browsers
|
||||||
|
// we support except for IE which will fail the support test anyway.
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
!CSS.supports( "selector(" + newSelector + ")" ) ) {
|
!CSS.supports( "selector(:is(" + newSelector + "))" ) ) {
|
||||||
|
|
||||||
// Support: IE 9 - 11+
|
// Support: IE 9 - 11+
|
||||||
// Throw to get to the same code path as an error directly in qSA.
|
// Throw to get to the same code path as an error directly in qSA.
|
||||||
@ -570,6 +575,10 @@ function setDocument( node ) {
|
|||||||
return document.querySelectorAll( ":scope" );
|
return document.querySelectorAll( ":scope" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// Support: IE 9 - 11+
|
||||||
|
// IE doesn't support `CSS.supports( "selector(...)" )`; it will throw
|
||||||
|
// in this support test.
|
||||||
|
//
|
||||||
// Support: Chrome 105+, Firefox <106, Safari 15.4+
|
// Support: Chrome 105+, Firefox <106, Safari 15.4+
|
||||||
// Make sure forgiving mode is not used in `CSS.supports( "selector(...)" )`.
|
// Make sure forgiving mode is not used in `CSS.supports( "selector(...)" )`.
|
||||||
//
|
//
|
||||||
@ -749,9 +758,8 @@ function setDocument( node ) {
|
|||||||
// `:has()` uses a forgiving selector list as an argument so our regular
|
// `:has()` uses a forgiving selector list as an argument so our regular
|
||||||
// `try-catch` mechanism fails to catch `:has()` with arguments not supported
|
// `try-catch` mechanism fails to catch `:has()` with arguments not supported
|
||||||
// natively like `:has(:contains("Foo"))`. Where supported & spec-compliant,
|
// natively like `:has(:contains("Foo"))`. Where supported & spec-compliant,
|
||||||
// we now use `CSS.supports("selector(SELECTOR_TO_BE_TESTED)")` but outside
|
// we now use `CSS.supports("selector(:is(SELECTOR_TO_BE_TESTED))")`, but
|
||||||
// that, let's mark `:has` as buggy to always use jQuery traversal for
|
// outside that we mark `:has` as buggy.
|
||||||
// `:has()`.
|
|
||||||
rbuggyQSA.push( ":has" );
|
rbuggyQSA.push( ":has" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,7 +415,7 @@ QUnit.test( "name", function( assert ) {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "comma-separated", function( assert ) {
|
QUnit.test( "comma-separated", function( assert ) {
|
||||||
assert.expect( 4 );
|
assert.expect( 10 );
|
||||||
|
|
||||||
var fixture = jQuery( "<div><h2><span></span></h2><div><p><span></span></p><p></p></div></div>" );
|
var fixture = jQuery( "<div><h2><span></span></h2><div><p><span></span></p><p></p></div></div>" );
|
||||||
|
|
||||||
@ -423,6 +423,26 @@ QUnit.test( "comma-separated", function( assert ) {
|
|||||||
assert.equal( fixture.find( "h2, div p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
assert.equal( fixture.find( "h2, div p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
||||||
assert.equal( fixture.find( "h2 , div p" ).filter( "p" ).length, 2, "has to find two <p>" );
|
assert.equal( fixture.find( "h2 , div p" ).filter( "p" ).length, 2, "has to find two <p>" );
|
||||||
assert.equal( fixture.find( "h2 , div p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
assert.equal( fixture.find( "h2 , div p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
||||||
|
assert.equal( fixture.find( "h2 ,div p" ).filter( "p" ).length, 2, "has to find two <p>" );
|
||||||
|
assert.equal( fixture.find( "h2 ,div p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
||||||
|
assert.equal( fixture.find( "h2,div p" ).filter( "p" ).length, 2, "has to find two <p>" );
|
||||||
|
assert.equal( fixture.find( "h2,div p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
||||||
|
assert.equal( fixture.find( "h2\t,\rdiv p" ).filter( "p" ).length, 2, "has to find two <p>" );
|
||||||
|
assert.equal( fixture.find( "h2\t,\rdiv p" ).filter( "h2" ).length, 1, "has to find one <h2>" );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( "comma-separated, only supported natively (gh-5177)", function( assert ) {
|
||||||
|
assert.expect( 5 );
|
||||||
|
|
||||||
|
var fixture = jQuery( "<div><input/><span></span></div>" );
|
||||||
|
|
||||||
|
fixture.appendTo( "#qunit-fixture" );
|
||||||
|
|
||||||
|
assert.equal( fixture.find( "input:valid, span" ).length, 2, "has to find two elements" );
|
||||||
|
assert.equal( fixture.find( "input:valid , span" ).length, 2, "has to find two elements" );
|
||||||
|
assert.equal( fixture.find( "input:valid ,span" ).length, 2, "has to find two elements" );
|
||||||
|
assert.equal( fixture.find( "input:valid,span" ).length, 2, "has to find two elements" );
|
||||||
|
assert.equal( fixture.find( "input:valid\t,\rspan" ).length, 2, "has to find two elements" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "child and adjacent", function( assert ) {
|
QUnit.test( "child and adjacent", function( assert ) {
|
||||||
|
Loading…
Reference in New Issue
Block a user