🌐 AI搜索 & 代理 主页
Skip to content

Commit a46dfef

Browse files
committed
Selector: Stop relying on CSS.supports( "selector(...)" )
`CSS.supports( "selector(...)" )` has different semantics than selectors passed to `querySelectorAll`. Apart from the fact that the former returns `false` for unrecognized selectors and the latter throws, `qSA` is more forgiving and accepts some invalid selectors, auto-correcting them where needed - for example, mismatched brackers are auto-closed. This behavior difference is breaking for many users. To add to that, a recent CSSWG resolution made `:is()` & `:where()` the only pseudos with forgiving parsing; browsers are in the process of making `:has()` parsing unforgiving. Taking all that into account, we go back to our previous try-catch approach without relying on `CSS.supports( "selector(...)" )`. The only difference is we detect forgiving parsing in `:has()` and mark the selector as buggy. Fixes jquery/jquery#5194 Ref jquery/jquery#5098 Ref jquerygh-486 Ref w3c/csswg-drafts#7676
1 parent ac08bb9 commit a46dfef

File tree

1 file changed

+21
-50
lines changed

1 file changed

+21
-50
lines changed

src/sizzle.js

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -356,27 +356,6 @@ function Sizzle( selector, context, results, seed ) {
356356
}
357357

358358
try {
359-
360-
// `qSA` may not throw for unrecognized parts using forgiving parsing:
361-
// https://drafts.csswg.org/selectors/#forgiving-selector
362-
// like the `:has()` pseudo-class:
363-
// https://drafts.csswg.org/selectors/#relational
364-
// `CSS.supports` is still expected to return `false` then:
365-
// https://drafts.csswg.org/css-conditional-4/#typedef-supports-selector-fn
366-
// https://drafts.csswg.org/css-conditional-4/#dfn-support-selector
367-
if ( support.cssSupportsSelector &&
368-
369-
// eslint-disable-next-line no-undef
370-
!CSS.supports( "selector(:is(" + newSelector + "))" ) ) {
371-
372-
// Support: IE 11+
373-
// Throw to get to the same code path as an error directly in qSA.
374-
// Note: once we only support browser supporting
375-
// `CSS.supports('selector(...)')`, we can most likely drop
376-
// the `try-catch`. IE doesn't implement the API.
377-
throw new Error();
378-
}
379-
380359
push.apply( results,
381360
newContext.querySelectorAll( newSelector )
382361
);
@@ -672,29 +651,22 @@ setDocument = Sizzle.setDocument = function( node ) {
672651
!el.querySelectorAll( ":scope fieldset div" ).length;
673652
} );
674653

675-
// Support: Chrome 105+, Firefox 104+, Safari 15.4+
676-
// Make sure forgiving mode is not used in `CSS.supports( "selector(...)" )`.
654+
// Support: Chrome 105 - 110+, Safari 15.4 - 16.3+
655+
// Make sure forgiving mode is not used in `:has()`.
656+
// `*` is needed as Safari & newer Chrome implemented something in between
657+
// for `:has()` - it throws in `qSA` if it only contains an unsupported
658+
// argument but multiple ones, one of which is supported, are fine.
659+
// We want to play safe in case `:is()` gets the same treatment.
677660
//
678-
// `:is()` uses a forgiving selector list as an argument and is widely
679-
// implemented, so it's a good one to test against.
680-
support.cssSupportsSelector = assert( function() {
681-
/* eslint-disable no-undef */
682-
683-
return CSS.supports( "selector(*)" ) &&
684-
685-
// Support: Firefox 78-81 only
686-
// In old Firefox, `:is()` didn't use forgiving parsing. In that case,
687-
// fail this test as there's no selector to test against that.
688-
// `CSS.supports` uses unforgiving parsing
689-
document.querySelectorAll( ":is(:jqfake)" ) &&
690-
691-
// `*` is needed as Safari & newer Chrome implemented something in between
692-
// for `:has()` - it throws in `qSA` if it only contains an unsupported
693-
// argument but multiple ones, one of which is supported, are fine.
694-
// We want to play safe in case `:is()` gets the same treatment.
695-
!CSS.supports( "selector(:is(*,:jqfake))" );
696-
697-
/* eslint-enable */
661+
// Note that we don't need to detect the complete lack of support for `:has()`
662+
// as then the `qSA` path will throw and fall back to jQuery traversal anyway.
663+
support.cssHas = assert( function() {
664+
try {
665+
document.querySelector( ":has(*,:jqfake)" );
666+
return false;
667+
} catch ( e ) {
668+
return true;
669+
}
698670
} );
699671

700672
/* Attributes
@@ -963,14 +935,13 @@ setDocument = Sizzle.setDocument = function( node ) {
963935
} );
964936
}
965937

966-
if ( !support.cssSupportsSelector ) {
938+
if ( !support.cssHas ) {
967939

968-
// Support: Chrome 105+, Safari 15.4+
969-
// `:has()` uses a forgiving selector list as an argument so our regular
970-
// `try-catch` mechanism fails to catch `:has()` with arguments not supported
971-
// natively like `:has(:contains("Foo"))`. Where supported & spec-compliant,
972-
// we now use `CSS.supports("selector(:is(SELECTOR_TO_BE_TESTED))")`, but
973-
// outside that we mark `:has` as buggy.
940+
// Support: Chrome 105 - 110+, Safari 15.4 - 16.3+
941+
// In some browsers, `:has()` uses a forgiving selector list as an argument,
942+
// so our regular `try-catch` mechanism fails to catch `:has()` with arguments
943+
// not supported natively, like `:has(:contains("Foo"))`. The spec now requires
944+
// `:has()` parsing to be non-forgiving but browsers have not adjusted yet.
974945
rbuggyQSA.push( ":has" );
975946
}
976947

0 commit comments

Comments
 (0)