Skip to content

Selector: Unescape class name in filter.CLASS#5843

Open
rootvector2 wants to merge 1 commit into
jquery:mainfrom
rootvector2:filter-class-unescape
Open

Selector: Unescape class name in filter.CLASS#5843
rootvector2 wants to merge 1 commit into
jquery:mainfrom
rootvector2:filter-class-unescape

Conversation

@rootvector2

Copy link
Copy Markdown

Noticed filter.CLASS interpolates the raw token text into the cached
regex, while filter.ID and filter.TAG both run the captured identifier
through unescapeSelector first. So when .foo\41 bar reaches the
seed-based filter path (e.g. .filter() on a multi-element set), it
compares against the literal text foo\41 bar, plus \41 becomes a
JS-regex octal/backreference. Unescape the captured class first and
switch to indexOf, matching the pattern already used by ATTR ~=.

@timmywil timmywil added the Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. label Jun 1, 2026
@timmywil timmywil requested a review from gibson042 June 15, 2026 16:08
@timmywil timmywil added Needs review and removed Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. labels Jun 15, 2026
Comment thread src/selector.js Outdated
Comment on lines +456 to +469
var pattern,
unescapedClass = unescapeSelector( className );

if ( ( pattern = classCache[ unescapedClass + " " ] ) ) {
return pattern;
}
return classCache( unescapedClass, function( elem ) {
var classAttr = typeof elem.className === "string" && elem.className ||
typeof elem.getAttribute !== "undefined" &&
elem.getAttribute( "class" ) ||
"";
return ( " " + classAttr.replace( rwhitespace, " " ) + " " )
.indexOf( " " + unescapedClass + " " ) > -1;
} );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're no longer constructing a RegExp instance, I don't think classCache carries its own weight. Let's drop it.

Suggested change
var pattern,
unescapedClass = unescapeSelector( className );
if ( ( pattern = classCache[ unescapedClass + " " ] ) ) {
return pattern;
}
return classCache( unescapedClass, function( elem ) {
var classAttr = typeof elem.className === "string" && elem.className ||
typeof elem.getAttribute !== "undefined" &&
elem.getAttribute( "class" ) ||
"";
return ( " " + classAttr.replace( rwhitespace, " " ) + " " )
.indexOf( " " + unescapedClass + " " ) > -1;
} );
var unescapedClass = " " + unescapeSelector( className ) + " ";
return function( elem ) {
var classes = getClass( elem );
return ( " " + classes.replace( rwhitespace, " " ) + " " )
.indexOf( unescapedClass ) > -1;
};

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. dropped classCache and its now-unused declaration, and the matcher returns directly. left the class read inline rather than calling getClass since that lives in attributes/classes.js and importing it here would pull the class-manipulation methods into the selector module. can switch to it if you'd prefer.

mgol added a commit to mgol/jquery that referenced this pull request Jun 15, 2026
whatwg/html#12205 made options & optgroups inside a disabled `<select>`
match `:disabled` (and not `:enabled`), even without their own `disabled`
attribute. This broke the "pseudo - :(dis|en)abled, optgroup and option"
test in browsers that implement the change. jQuery's internals are left
as-is for now; the test now just accepts both the old & new behavior.

A separate companion test was added that forces the jQuery selector engine
via `.filter()`; that path keeps the old semantics in every browser.

Ref whatwg/html#12205
Ref jquerygh-5843
mgol added a commit that referenced this pull request Jun 15, 2026
whatwg/html#12205 made options & optgroups inside a disabled `<select>`
match `:disabled` (and not `:enabled`), even without their own `disabled`
attribute. This broke the "pseudo - :(dis|en)abled, optgroup and option"
test in browsers that implement the change. jQuery's internals are left
as-is for now; the test now just accepts both the old & new behavior.

A separate companion test was added that forces the jQuery selector engine
via `.filter()`; that path keeps the old semantics in every browser.

Closes gh-5851
Ref whatwg/html#12205
Ref gh-5843
@rootvector2 rootvector2 force-pushed the filter-class-unescape branch from ba5c8da to 6270476 Compare June 18, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants