CSS3 Selector Limitations and Suggestions

Hi,
  While experimenting with XHTML2, I have discovered some limitations 
with CSS.  Based on these I have come up with a few proposals for CSS3.  
Sorry for making this long, but I have put a lot of effort and thought 
into this and tried to be as clear as possible.

-- XHTML 2 Example --

  Throughout this post, all my CSS examples will apply to this XHTML 2 
fragment:

<body>
    <section>
        <h>Heading 1</h>
        <div>
            <section>
                <h>Heading 1.1</h>
            </section>
        </div>
        <section>
            <h>Heading 1.2</h>
            <section>
                <h>Heading 1.2.1</h>
            </section>
        </section>
    </section>
</body>


-- Limitations --

1. There is no easy way to select a specific section level because
   other block elements may appear between, with just one selector.
   eg. In the above example, with the <div> appearing before the first
   second level section (1.1), but not the second (1.2), the following
   cannot be used:

body > section section > h {
    /* Selects both 2nd and 3rd levels
       (1.1, 1.2 and 1.2.1) */
    color: green;
}

body > section > section > h {
    /* Does not select Heading 1.1
       Only selects Heading 1.2 */
    color: red;
}

2. There is no shorthand method for selecting the nth descendant level
   of an element.  eg. shorthand for selecting the 6th level section:
   body > section > section > section > section > section > section
   which also has the problems of the above limitation.


-- Proposals to Solve these Limitations --

  I have come up with two potential solutions.  One using a combinator 
and the other using pseudo classes.  I think the pseudo classes are more 
flexible, but the pseudo classes may be easier to understand, after 
understanding the concept of the combinator.


-- Combinator --

  Just as there are direct adjacent '+' and indirect adjacent '~' 
sibling combinators, I propose there be an indirect descendant 
combinator to pair with the child combinator '>' (since a child element 
is a simply a direct descendant)

  This would differ from the normal descendant selector (white space) in 
that it would only select the first matching descendant element 
regardless of any nested elements between such as the <div> in the above 
example.

  I'll use the symbol '^' to represent this because of it's similarity 
in shape to the child descendant selector '>', and also because it kind 
of represents a simple tree structure like xml (though with only 2 
branches), but the symbol doesn't really matter.

body > section ^ section {
    /* This selects Headings 1.1 and 1.2,
       but NOT Heading 1.2.1 */
}

* Advantages
  Very simple and effective

* Disadvantages
  Not very flexible.  For example: can't select the last, or last of 
type, descendants in the tree.  For example, selecting both sections 1.1 
and 1.2.1.


-- Pseudo Classes --

  Just like there are :nth-child(n) and :nth-of-type(n) pseudo classes 
that apply only to children of the same parent, I think it would be 
useful if there were :nth-descendant pseudo classes as well that apply 
to descendants of the same ancestor, following the document tree.

  There are eight pseudo classes that I'll introduce, which I have 
derived from the :nth-, :nth-last-, :first- and :last-child() and 
-of-type() pseudo classes, and applied to descendants.

  I believe these pseudo classes can only be effective when used with 
the descendant selector (white space).  My use of the term 'descendant 
level' means the number of descendant elements along any given path.  If 
an equivalent term has already been used in CSS, then it should be used 
instead.
eg.
<x>
    <y/>
    <z/>
</x>
  The <x> element only has one descendant level, even though there are 
two children

<x>
    <y>
        <z/>
    </y>
    <z/>
</x>
  This <x> element now has two descendant levels.


- Descendant Pseudo Classes -

* :nth-descendant(an+b)
  The :nth-descendant(an+b) pseudo-class notation represents
  an element that has exactly an+b-1 ancestors in the document tree,
  which are also descendants of any specified ancestors, for a given
  zero or positive integer value of n.  If no ancestors are specified,
  then the root element is used.

* :nth-last-descendant(an+b)
  The :nth-last-descendant(an+b) pseudo-class notation represents
  an element that has exactly an+b-1 descendant levels in the
  document tree, for a given zero or positive integer
  value of n.

* :first-descendant
  Same as :nth-descendant(1), but with a lower specificity.

* :last-descendant
  Same as :nth-last-descendant(1), but with a lower specificity.


- Descendant of Type Pseudo Classes -

* :nth-descendant-of-type(an+b)
  The :nth-descendant-of-type(an+b) pseudo-class notation represents
  an element that has exactly an+b-1 ancestors with the same
  element name in the document tree, which are also descendants of
  any specified ancestors, for a given zero or positive integer
  value of n.  If no ancestors are specified, then the root element
  is used.

* :nth-last-descendant-of-type(an+b)
  The :nth-last-descendant-of-type(an+b) pseudo-class notation
  represents an element that has exactly an+b-1 descendant levels with
  the same element name in the document tree, for a given zero or
  positive integer value of n.

* :first-descendant-of-type
  Same as :nth-descendant-of-type(1), but with a lower
  specificity.

* :last-descendant-of-type
  Same as :nth-last-descendant-of-type(1), but with a lower
  specificity.


  For the :nth-descendant(an+b) pseudo classes, when an+b = 1, it may 
appear to be equivalent to the :first-child or nth-child(an+b) pseudo 
classes, however, these classes will only select one matching child, 
whereas nth-descendant(1) will select all matching children.  I nearly 
made this mistake myself, so I want to make sure everything is as clear 
as possible.

  I have tried to be as accurate as possible with my definitions, but I 
still may have made some minor mistakes.  If in doubt, the following 
examples illustrate their intended meaning.


-- Examples --
  These examples will show general use cases for the elements, and state 
whether or not it is a final solution to the above stated limitation.  
Even though one example is not a solution to this specific problem, does 
not mean it won't be for others.

1. body section:nth-decendant(0n+2) > h
   This selects the <h> elements within all <section> elements
   that have exactly 1 ancestor in the document tree,
   which are descendants of the body element.
   * Selects:
       Heading 1.2
       (body > section > section > h)
   * Does Not Select:
       Heading 1.1 because the extra div is another ancestor
       (body > section > div > section > h)

2. body section:nth-last-decendant(0n+2) > h
   This selects the <h> elements within all <section> elements
   that have exactly 1 descendant level in the document tree,
   * Selects:
       Heading 1.1 because it's section's only descendant is
       the <h> element
       (body > section > div > section > h)
       Heading 1.2.1 for the same reason
       (body > section > section > section > h)

3. body section:nth-decendant-of-type(0n+2) > h
   This selects the <h> elements within all <section> elements
   that have exactly 1 ancestor, of type section, in the
   document tree, which are descendants of the body element.
   * Selects:
       Heading 1.1 because there is only one ancestor of type section
       before it.  The div ancestor is ignored.
       (body > section > div > section > h)
       Heading 1.2
       (body > section > section > h)

4. body section:nth-last-decendant-of-type(0n+2)
   This selects the <h> elements within all <section> elements
   that have exactly 1 descendant level, of type section, in the
   document tree
   element.
   * Selects:
       Heading 1.2 because it has 1 descendant of type section
       for Heading 1.2.1.
       (body > section > section > h)

  The :first- and :last- versions don't need examples since they are 
just shorthand for when an+b = 1.


-- One Final Pseudo Class --

  This is directly related to limitation 2 from the beginning.  A 
shorthand method for specifying repeating selectors like:
   body > section > section > section > section > section > section > h
could be something like

  body > section:rpt(6) > h

Possible definition:
  The rpt(0n+b) pseudo class is a functional notation which copies the
  selector up to and including the previous combinator, and is
  repeated, or multiplied, n times, for any positive, non zero value
  of n.
(If a value of 0 was allowed, it would mean that the selector would be 
removed)

-- Conclusion --
  I think the pseudo classes will add a lot of flexibility to the CSS 
language.  Even though my examples deals specifically with the XHTML2 
example, there is no reason that they cannot be applied to any form of 
XML document.

CYA
...Lachy

Received on Sunday, 7 December 2003 07:03:14 UTC