CSS3 Selectors

Recently, i continued porting the DENG1 CSS parser to ECMAScript 4 (AS2, to be specific), and making it fully compliant to the CSS3 specification. The current AS1 version of the parser already has many CSS3 features implemented, but not all of them (like the nth-child(), nth-last-child(), nth-of-type() and nth-last-of-type() pseudo classes for example, that required a special type of argument that wasn’t supported by the parser yet).

Part of the work was commenting changes in the grammar, to know what was added or changed from the CSS2.1 grammar, and to verify if the existing code fully conforms to the CSS3 grammar.

I figured this might be an interesting read for some of you, so here it is. Disclaimer: Please note that the comments might not be accurate or complete. In doubt, please refer directly to the Selectors module of the CSS3 specification at W3C.

Commented grammar for CSS3 Selectors Module

It is now specified that only one pseudo element is allowed in a selector, and that this pseudo element has to occur as the very last element. Pseudo classes can occur anywhere in a simple selector sequence.

selector
  /* there is at least one sequence of simple selectors */
  /* in a selector and the pseudo-elements occur only */
  /* in the last sequence; only one pseudo-element may */
  /* occur */
  : [ simple_selector_sequence combinator ]*
       simple_selector_sequence [ pseudo_element ]?
  ;

New combinators ~ (indirect adjacent combinator) and * (descendant combinator) have been introduced (the * combinator is not contained in the current CSS3 grammar though).

combinator
  /* combinators can be surrounded by white space */
  : S* [ '+' | '>' | '~' | /* empty */ ] S*
  ;

Typeselectors and the universal selector (*) have an optional namespace prefix now. Both the typeselector and the universal selector are optional.

The grammar for negation is missing in the current CSS3 grammar, it should look similar to the rule for pseudo_class, with a function identifier not() and one or more negation_args as function argument. The grammar is incomplete here. Example: a:not([href]).

simple_selector_sequence
  /* the universal selector is optional */
  : [ type_selector | universal ]?
        [ HASH | 
          class | 
          attrib | 
          pseudo_class | 
          negation ]+ |
    type_selector | universal
  ;

type_selector
  : [ namespace_prefix ]? element_name
  ;

element_name
  : IDENT
  ;

A namespace prefix, if used, needs a preceding @namespace rule, where the namespace prefix is associated with a namespace URL. Let’s assume we want to mix SVG and XHTML markup in the same document. Both SVG and XHTML feature the <a> element. We most likely want the <a> element to appear different in the SVG context (i.e. shape appears with a stroke) than in the XHTML context (text appears underlined).

Using the @namespace rule and namespaced prefixes we can write rules that apply either to SVG’s or to XHTML’s <a> element:

@namespace xhtml url(http://www.w3.org/1999/xhtml);
@namespace svg url(http://www.w3.org/2000/svg);
xhtml|a[href] { text-decoration: underlined; }
svg|a[href] { stroke-width: 2px; }
namespace_prefix
  : [ IDENT | '*' ]? '|'
  ;

universal
  : [ namespace_prefix ]? '*'
  ;

class
  : '.' IDENT
  ;

Attribute simple selectors allow an optional namespace prefix for the attribute ident now.

Prefixmatch (^=), suffixmatch ($=) and substringmatch (*=) operators have been introduced.

Example: we want to style <a> elements with the href attribute set to an URL starting with "http://wahlers.com.br" differently than other <a> elements. we are using the prefixmatch operator:

a[href] {
   font-weight: bold;
}
a[href ^= "http://wahlers.com.br"] {
   font-style: italic;
}

All <a> elements with the href attribute set appear bold, <a> elements with a href attribute starting with "http://wahlers.com.br" appear bold and italic.

attrib
  : '[' S* [ namespace_prefix ]? IDENT S*
        [ [ PREFIXMATCH |
            SUFFIXMATCH |
            SUBSTRINGMATCH |
            '=' |
            INCLUDES |
            DASHMATCH ] S* [ IDENT | STRING ] S*
        ]? ']'
  ;

pseudo_class
  /* a pseudo-class is an ident, or a function taking */
  /* an ident or a string or a number or a simple  */
  /* selector (excluding negation and pseudo- */
  /* elements) or a an+b expression for argument */
  : ':' [ IDENT | functional_pseudo ]
  ;

"an+b" expressions have been introduced as argument for functional pseudo classes (select nodes depending on their position). Allowed argument types include string, number, "an+b" expression and negation now, in addition to ident (the only allowed argument type in CSS2.1).

Example: style even and odd rows of a table differently:

tr { background-color: #eee; }
tr:nth-child(2n) { background-color: #ccc; }
functional_pseudo
  : FUNCTION S* [ IDENT | STRING | NUMBER |
      expression | negation_arg ] S* ')'
  ;

expression
  :  [ [ '-' | INTEGER ]? 'n' [ SIGNED_INTEGER ]? ] |
      INTEGER
  ;

negation_arg
  : type_selector |
    universal |
    HASH |
    class |
    attrib |
    pseudo_class
  ;

Pseudo elements are now prefixed by :: (instead of : in CSS2.1) to distinguish between pseudo elements and pseudo classes. For backwards compatibility with CSS2.1 and previous, user agents have to accept the : notation for pseudo elements defined in earlier specs (like :before and :after).

pseudo_element
  : [ ':' ]? ':' IDENT
  ;

One thought on “CSS3 Selectors

Comments are closed.