Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#74068 nth child specificity #369

Merged
merged 7 commits into from
Nov 7, 2023

Conversation

SStranks
Copy link
Contributor

@SStranks SStranks commented Nov 3, 2023

Fix issue #74068 "[css] Wrong selector specificity values with :nth-child(even) and :nth-child(odd)". This patches the specificity for the nth-child/nth-last-child psuedo-selector.

Debugging the code it seems the scanner/parser treats words 'even', 'odd', and 'n', as NodeType 16 (ElementNameSelector), which contributes to a false specificity.

'div:nth-child(n of li)' should produce a specificity of 0-1-2, but produces 0-1-4 suggesting all three arguments are treated as elements. If the 'n' has an integer prefix the correct specificity is produced however.

Rather than try to unpack the scanner/parser logic, the solution exploits the formula of the psuedo-selector by testing for the presence of " of " within the arguments string, "An+B [of S]?".

The specificity of the :nth-child(An+B [of S]?) pseudo-class is the specificity of a single pseudo-class plus, if S is specified, the specificity of the most specific complex selector in S

Copy link

@nicolasploquin nicolasploquin left a comment

Choose a reason for hiding this comment

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

Code review only, no build to test.
Good job 👍

// Test for presence of complex-selector-list S; find highest specificity
const keyword = text.indexOf(' of ');
if (keyword !== -1) {
const psuedoSelector = element.getChildren();

Choose a reason for hiding this comment

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

Typo - should be named 'pseudoSelector' (e/u inversion)

const selectorText = text.slice(text.indexOf('(') + 1, text.length -1);

// Test for presence of complex-selector-list S; find highest specificity
const keyword = text.indexOf(' of ');
Copy link
Contributor

Choose a reason for hiding this comment

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

Testing for a space before and after of is not clean. There could also be a tab, or even a non word character

ul.one > li:nth-child(-n + 3 of.noted) {
}

would be valid too.

I suggest to jeust use the scanner to find out if there's an of ident in the string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reworked the logic in the latest commit. When the 'An + B [of S]?' syntax is respected then we can just grab the last childElement as it will contain the entire complex-selector list (S).

The edge case where 'n' has no integer prefix (the parser does not recognise it as a binary expression token). Extract out the complex-selector list, and re-parse the selectors. This also handles the different syntax possibilities; added more tests for completeness.

fix(cssParser): parseSelector; optional boolean
test(): additional test for complex-selector list syntax
@aeschli
Copy link
Contributor

aeschli commented Nov 6, 2023

Looks good! If you can run the fomatter on selectorPrinting.ts one more time that would be perfect.

@SStranks
Copy link
Contributor Author

SStranks commented Nov 6, 2023

Looks good! If you can run the fomatter on selectorPrinting.ts one more time that would be perfect.

Amended

@aeschli
Copy link
Contributor

aeschli commented Nov 7, 2023

Thanks a lot!

@aeschli aeschli merged commit f55163f into microsoft:main Nov 7, 2023
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants