8
2
Today, your task is to write a program (or a function) that accepts a string and outputs (or returns) four integers.
Input
The input string is a CSS3 selector, and can contain basically any Unicode character.
Output
The output represents the CSS specificity of this selector.
The first number is always 0 (because it's used for inline styles, and this exercise doesn't apply to inline styles)
The second number is the number of ids (
#foo
) present in the selector.The third number is the number of classes (
.foo
), attributes ([bar]
) and pseudo-classes present in the selector.The fourth number is the number of elements (
biz
) and pseudo-elements present in the selector.
Notes:
Universal selector (*) isn't counted anywhere
The pseudo-elements
::before
and::after
can also be written with a single ":" (legacy notation)Input can use the
:not(selector)
pseudo-class. The selector inside doesn't count, even if it contains ids, classes, elements, ...)The "bricks" of the selector are separated by combinators (spaces/tabs,
+
,>
,~
, ex:body > div+a ~*
), but they can also be cumulated (ex:div#foo.bar[baz]:hover::before
)You also have to handle CSS escape sequences (
\
followed by 1 to 6 hexadecimal numbers, followed by a space), and escaped special characters (\
followed by any of these:!"#$%&'()*+,-./:;<=>?@[\]^`{|}~
) properly. Those escapes can be part of any brick of the selector (id, class, etc).You don't need to do anything particular if you receive an invalid selector or a CSS4 selector. Don't bother implementing a CSS3 selector validator.
Here are a few links to learn more about CSS specificiy:
- CSS-tricks article: http://css-tricks.com/specifics-on-css-specificity/
- A tool (incomplete) that counts it: http://specificity.keegan.st/
Examples
// Universal * => 0,0,0,0 // ID #id => 0,1,0,0 // Class .class => 0,0,1,0 // Attributes [foo] => 0,0,1,0 [foo="bar"] => 0,0,1,0 [foo~="bar"] => 0,0,1,0 [foo^="bar"] => 0,0,1,0 [foo$="bar"] => 0,0,1,0 [foo*="bar"] => 0,0,1,0 [foo|="bar"] => 0,0,1,0 [ foo = bar ] => 0,0,1,0 [foo = 'bar'] => 0,0,1,0 (NB: brackets [] can contain anything except an unescaped "]") // Pseudo-classes :root => 0,0,1,0 :nth-child(n) => 0,0,1,0 :nth-last-child(n) => 0,0,1,0 :nth-of-type(n) => 0,0,1,0 :nth-last-of-type(n) => 0,0,1,0 :first-child => 0,0,1,0 :last-child => 0,0,1,0 :first-of-type => 0,0,1,0 :last-of-type => 0,0,1,0 :only-child => 0,0,1,0 :only-of-type => 0,0,1,0 :empty => 0,0,1,0 :link => 0,0,1,0 :visited => 0,0,1,0 :active => 0,0,1,0 :hover => 0,0,1,0 :focus => 0,0,1,0 :target => 0,0,1,0 :lang(fr) => 0,0,1,0 :enabled => 0,0,1,0 :disabled => 0,0,1,0 :checked => 0,0,1,0 :not(selector) => 0,0,1,0 (NB: the keyword after ":" can be anything except a pseudo-element) // Elements body => 0,0,0,1 // Pseudo-elements :before => 0,0,0,1 :after => 0,0,0,1 ::before => 0,0,0,1 ::after => 0,0,0,1 ::first-line => 0,0,0,1 ::first-letter => 0,0,0,1 (NB: parenthesis () can contain anything except an unescaped ")" )
(to be continued)
If you have questions or need examples or test data, please ask in the comments.
Shortest code (in bytes) wins.
Good luck!
3Please add examples (ideally covering all the quirks in the notes). – Martin Ender – 2014-11-19T14:11:16.607
2The list of tests looks really good, but some examples that go beyond a single
1
would be great. (Btw, I think this is actually a pretty good challenge, but an exhaustive list of test cases seems vital to make it work well.) – Martin Ender – 2014-11-20T16:26:28.597