FW: [selectors] Yet another attempt at trees

Reposted as per request.

Every so often, people try to come up with solutions to the problem of
selecting certain elements at specific levels in the document tree.  These
proposals inevitably end up running foul of some problem.  Despite this, I
have come up with an approach that I hope might meet with approval.

:ancestors(an+b)
:ancestors(an+b,s)
    an+b             is treated the same as with other structural
pseudoclasses, including "even" and "odd".
    s                is any simple identifier except for a pseudo-element,
in the same manner as :not().
    :ancestors(an+b) is a shorthand for :ancestors(an+b,*).

This pseudoclass matches elements that have a number of ancestors equal to
an+b that match the simple selector s.

Note that unlike the other proposals I have seen to date along these lines
:ancestors() as proposed here does not change behavior based on what type
selector it is paired with.  Whether it will match with a particular
element is thus independent of the rest of the selector as is the case with
all other existing pseudoclasses with the exception of the -of-type ones.

Example 1:
    :ancestors(0)
This would match the same element as :root as it is the only element with 0
ancestors.

Example 2:
    h:ancestors(2,section)

This would match 3rd level headings in the current draft of XHTML2 but not
4th or higher level headings as the existing CSS code of:
    section section h
does which requires using another rule of the form:
    section section section h
to set things mostly right.  Mostly I say, because if another style sheet
has a rule for just simply h that would have otherwise applied, this rule
won't restore what that rule would have done.

Example 3:
    div:ancestors(even,div) {background-color:red}
    div:ancestors{odd,div)  {background-color:green}

This would make nested div's alternate between red and green in their
background color with the outermost div (which would have 0 ancestors that
are div's) being red.

Possible objection:
How does this help with finding 3rd level headings in the following?

<body>
<h/>
<section>
<h/>
<navigation>
<h/>

It helps somewhat to simplify the task.  Assuming that body, section, and
navigation can occur in any order and that level headings should only be
advanced when they are encountered, it would take 108 different selectors
with existing CSS to specify rules that apply only to third level headings.
(27 of the form S S S h to set the 3rd and greater level headings and 81
more of the form S S S S h to set back the 4th level and higher level
headings to what they were before, assuming that the user doesn't have his
own style rules for h that get clobbered as a result.)  In contrast, it
would take only 10 different selectors of the form
h:ancestors(x,body):ancestors(y,section):ancestors(z,navigation) where
x+y+z are whole numbers adding up to 3 if this selector were available.

Any further simplification would require implementing something analogous
to the oft proposed :matches().  Simpler to leave the door open, as this
proposal does, for :ancestors(3,:matches(body,section,navigation)) to being
someday usable than to try and duplicate the functionality with
:ancestors(3,body,section,navigation) since doing so would make
:ancestors(n,body,section,navigation) have the same functionality as
:matches(body,section,navigation).

===

Assuming that :ancestors() is adopted as defined here then it would
probably make sense to also adopt
    :nth-child(an+b,s)
    :nth-last-child(an+b,s)
and acknowledge
    E:nth-of-type(an+b)
as a shortcut for E:nth-child(an+b,E) and
    E:nth-last-of-type(an+b)
as one for E:nth-last-child(an+b,E).

Note that since these expanded versions of the -child() pseudoclasses could
be applied to any simple selector and not just the type selector, it would
allow for selectors such as
    li:nth-child(1:focus)
that would apply to the item in a list that had focus and those after it in
the list.  I don't believe that this functionaity can be duplicated with
existing CSS, but I can see the potential usefulness of having different
styling applying to the item before a given list item in contrast to those
after the item.

Adding
    :first-child(s)
and
    :last-child(s)
might also be worthwhile, but could also make things more difficult for
parsers in that they wouldn't be able to categorize first-child or
last-child as either identifier or function without having to check the
context. Since their functionality would be available as:
    :nth-child(1,s)
and
    :nth-last-child(1,s)
I doubt if the slight harmonization of forms is worth the extra parser
complexity that would be required.

===

The two argument version of :ancestors() should have its specificity depend
on as its simple selector argument just as :not(s) does.  However, there
should also be an addition to the pseudoclass count in the specificity. 
This is part so that :ancestors(an+b) and :ancestors(an+b,*) have the same
level of specifity.  Without that extra addition, :ancestors(an+b,*) would
actually have a lower specificity than :ancestors(an+b), which seems
strange to me. The same reasoning applies to two argument versions of
nth-child and nth-last-child if those were to be adopted.

===

What I have proposed here becomes even more useful if :matches() were
available, but that statement applies to much more than just this proposal.

Ernest Cline
ernestcline@mindspring.com

Received on Tuesday, 19 July 2005 18:59:57 UTC