- From: Ian Hickson <ian@hixie.ch>
- Date: Sun, 05 May 2002 19:06:42 +0100
- To: fantasai <fantasai@escape.com>
- CC: www-style@w3.org
fantasai wrote: > Ian Hickson wrote: >> :matches() in this idea is exactly equivalent to :matches() in my >> :proposal, >> after moving any selectors associated with the >> :subject of the argument out >> to the level of the :matches() >> :pseudo, and replacing the subject with a #. > You also wrote: >> /* select all fleas if the document contains an antiflea element */ >> flea:matches(antiflea) > which indicates otherwise, since in the syntax I describe that would > match exactly nothing. They are not equivalent, they are merely > similar. Please reread exactly what I wrote. I stand by my original comment. > why should you restrict the ability to change the subject of a > selector to the argument of the :matches() pseudo-class? If you have > the ability to process it, then let it be used outside--it > simplifies the majority of the cases where one would want to use > such capabilities as we are discussing. Fair point I guess. > Now, suppose we were to implement :matches() as you describe it, > and came upon flea:matches(antiflea). If the element in question > matches 'flea', then we have to jump to the root of the document > and scan for antiflea. But if we had flea:matches(# antiflea), we > would stay where we are and continue exploring flea's > relations--and because we are inside a :matches pseudo, we are > able to check elements _later_ in the tree. Right. This is the source of the performance problems. > None of this behavior applies to selectors outside the :matches() > pseudo--not even the parsing and storing of the argument, which > needs to handle # somehow. How can you say that the code for > handling the main selector could be reused for handling > :matches()'s argument? The processing has similarities, but the > code for handling :matches() is a _superset_ of the normal > selector matching code. Exactly, it's a superset, ss in a decent implementation it'll reuse all the code of the normal selector matching code. > Now, if I had a selector written like this: > > .section > :matches(h1,h2,h3,h4,h5,h6) + $div > code > > The <div> would be handed to the selector matching code, which > would then walk to the <div>'s previous sibling and send that to > be matched against :matches()'s argument, which, being a normal > selector with no additional rules, would be processed with the > same parsing and matching code as the main selector. It's pure > recursion. (If <div> matched against it's prior relations, the > function would then inspect its children for <code>.) Right. But additional code would have to be added to the normal selector matching code to support $ and all it entails. So the same amount of new code needs to be written. >>> 6. A partial implementation of just $ will suffice for most needs. >> I don't really understand this. > > It means, if you implement $ and leave :matches() for later, you will > take care of the majority of the needed capabilities. The variety of > selectors that can be created with just $ and without :matches() may > be limited, but they are the most-used cases. Well, equivalently, in my proposal you can implement :has() and leave the full :matches() and # til later. However, I am thinking long term, and so this isn't really relevant IMHO. >>> 7. Reads better: .section > $div > code >> .section > div:has( > code) >> I don't see that the dollar sign is any more understandable. > > It's not the dollar sign. It's the fact that the whole path through > the tree is on one level. I don't see that the fact that the whole path through the tree is on one level is any more understandable. >>> One can see the selector as mapping unbroken path through the >>> tree, branching only when selectors _need_ to branch off. >>> .section > :matches(h1,h2,h3,h4,h5,h6) + $div > code > > Look at this selector: > .section > :matches(h1,h2,h3,h4,h5,h6) + $div > code > > If you visually group each sequence of simple selectors into a generic box, > you can easily see the entire pattern we are trying to match: > > [1] > [2] + {3} > [4] > ^ > The pattern only branches off through the :matches() pseudo if it's > necessary (i.e. you have two or more right-side patterns to match > against) or if one, as the author, prefers to read it that way. In > your model, it always branches off. I don't see how that is particularily an advantage or disadvantage, but if you find it more readable that way, fair enough. >> You forgot the following disadvantages: >> >> 1. Changes one of the basic concepts of CSS, that the subject of the >> selector is the last simple selector in the chain. >> >> 2. Can only have one description on the right hand side, unless you >> use the :matches() form, at which point the dollar syntax is >> redundant. >> >> 3. removes the potential use of a dollar sign from the language for >> future extensions. >> >> 4. Ambiguous: what does it mean if there are two $s in a selector? > > It means the selector is invalid. > I think the reason you put that in for :subject was because there was no way > to handle multiple reverse relationships, and if you tried by using :subject > twice, you'd wind up with an ambiguous, undefined selector. No, I put it in because anything that makes it easier to have invalid selectors is a bad idea IMHO. > And because of this, it allows you to write > .section > :matches(h1,h2,h3,h4,h5,h6) + div > which, as you have said, cannot be converted into your :matches() form. > > Note that it is not only easier to write than specifying all six > variations in full, Interesting. > but it is more efficient--you only match against the div once, > instead of six times. A decent implementation of a selector matching algorithm would have already optimised for this and thus it would not be an issue. *time passes, during which Ian tries to rewrite his proposal to include the $ idea* It just seems so wrong! Look at the following selectors, and tell me how they should be interpreted. (Note that for each one, the $ could equally well be replaced by :subject or the (...) form, which are merely syntactic equivalents for the same thing.) $h1 > $h1 $h1::after + h2 $h1::first-line > span $::before + ::after $span * ::after Sorry, but every time I see the $ (or :subject, or (...) form) I just cringe and get the "wrong answer!" alarm ringing in my head. Taking your comments into account, here is an updated version of my proposal. :matches() and :has() The :matches() psuedo-class takes a comma separated list of selectors to match against the element. The pseudo-class matches the element if one of its argument selectors matches the node. The special symbol '#' in the argument is a placeholder that matches the node that the pseudo-class is being tested on. If it is not used then the last sequence of simple selectors in the selector chain has to match the element. In other words, A:matches([B] [C]) ...is exactly equivalent to: A[C]:matches([B] #) Note that if both the :matches() argument and the sequence of simple selectors have non-univorsal type selectors, then the argument cannot match. For example: A:matches(B C) ...can never match. Using the # form avoids the possibility of making this mistake, as it cannot be used with other simple selectors. (Note "#[foo]" is not valid.) If the # form appears anywhere except the end of an argument, then the document must be walked looking for matching elements. For instance, A:matches(# ~ B) ...would mean examining all subsequent siblings of A elements for B elements. Authors are cautioned that this may can be a costly process and are therefore advised to avoid using this form where possible. The :has() pseudo-class is an alias for :matches(), as follows: :has(FOO1, FOO2, ..., FOOn) ...is exactly equivalent to :matches(# FOO1, # FOO2, ..., # FOOn) ...in terms of interpretation as a selector. Note that the FOOs can start with a combinator, if they don't then the descendant combinator is assumed. This is the same as the way we have 'odd' and 'even' as equivalents to '2n+1' and '2n' for the :nth-child() pseudo-classes. It makes the simple case simple. Note that the arguments for :matches() and :has() cannot have pseudo-elements (as these don't ever match real elements). Examples: /* select gardens in a village containing at least one pot plant */ village garden:has(plant[type=pot]) /* select gardens in a village containing at least one infected plant */ village garden:has(plant > flea) /* select gardens in a village containing at least two plants */ village garden:has(plant ~ plant) /* select gardens containing at least two infected plants */ village garden:has(plant:has(> flea) ~ plant > flea) /* select the first line of all plants that are before infected plants */ plant:has(+ plant > flea)::first-line /* select all clean plants that are before infected plants */ plant:not(:has(> flea)):has(+ plant > flea) /* select all fleas if the document contains an antiflea element */ :root:matches(antiflea, # antiflea) flea -- Ian Hickson ``The inability of a user agent to implement part of this specification due to the limitations of a particular device (e.g., non interactive user agents will probably not implement dynamic pseudo-classes because they make no sense without interactivity) does not imply non-conformance.'' -- Selectors, Sec13
Received on Sunday, 5 May 2002 14:06:46 UTC