[CSS4 Selectors] :not,:never and limited ancestor

Hello www-style

I would like to make a few comments on the CSS4 selectors draft.

The issues concern "limited ancestor combinator", and :not selector with 
combinators.

The first problem is the well-known is the issue that using combinators within 
:not() selector is not intuitive, while easy to implement, the results are not 
always what you expect.

The underlying confusion comes down to the problem of inverting the ancestor 
combinator.

It is easiest to describe by arguing looking at what happens in bottom-up 
matching. 'A *' means that an element matches if one of the ancestors matches 
'A', or in other words, if an ancestor matches 'A' the expression matches, if 
if does not match 'A', the expression continues to match the rest of the 
ancestors. Inverting the entire combinator inverts the expression to look for 
the first failure.

                   'A'         ':not(A)'
-----------------------------------------
'A *'              match       continue
':not(A) *'        continue    match
':not(A *)'        fail	    continue   
':not(:not(A) *)'  continue	    fail


It is a bit counter-intuitive, but I think it is quite useful. I would give it 
another name though such as :never. I would add another restriction to it 
though, so it only fails until another expression matches, combining a 
negative failure condition to guarantee while looking for the possive 
condition normally specified before the ancestor combinator. This turns out to 
fits right into the limited ancestor combinator problem.

Let us look at some examples:
1. Select only first level of <item> under a <list>
2. Select only elements that is not embedded deeper in another <list>
3. Select only elements that is not embedded under another element with 
attribute 'LANG'.

The way I propose to implement all of these is using a never-selector. I am 
not sure it is best expressed using pseudo-class syntax, but for this example 
I will use that. 

The :never(X) selector fails the selector if the selector-argument is ever 
true in the scope that is matched in. In normal use it is no different from a 
:not(X) selector, but in an indirect combinator such as ancestor or indirect-
sibling it changes the behavior of a failure to mean complete fail, instead of 
'continue matching'. 


1. list item:never(item)
The form x:never(x) might look a bit weird, but could for clarity be written 
as :first(x), it does the same though. It stops the ancestor combinator from 
matching items embedded within items.

2. list *:never(list).
This creates a scope of limited ancestor selectors.

3. [lang|=en] *:never([lang])
This implements the attribute-matching part of :lang(en) selector using the 
new syntax. The clever part here is that the matching part within the :never 
selector does not need to be the same as the selector after the ancestor 
combinator, in this case making it possible to create a scope based on a 
broader matche [lang], than the ancestor match [lang|=en].


One problem might be the syntax though. Another option is to put the 
requirement in beside the combinator. Also the complement requirement 
:never(:not()) can be useful at times. I would call it :always. For instance 
'svg#thisimage :always(svg|*)' could ensure it only matches svg descendants, 
but not those embedded in embedded foreign objects.


I hope you find my comments useful. I look forward to seeing the final results 
of the CSS 4 selector in the future.

Best regards
`Allan Sandfeld

Received on Monday, 13 June 2011 11:49:45 UTC