- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Sat, 9 Jan 2010 18:12:37 -0600
- To: Nikita Popov <privat@ni-po.com>
- Cc: news <news@terrainformatica.com>, www-style list <www-style@w3.org>
On Sat, Jan 9, 2010 at 2:17 AM, Nikita Popov <privat@ni-po.com> wrote: > Compare with: > .add (#author, #publications) (input, select, textarea) > Clean, short, effective. > Or same with :any, though not :matches, cause :matches makes it complicated > again, because breaking it down to simple rules is impossible (Any ideas on > that?) :any() and :matches() are the exact same thing. They're just different names that have been suggested. You'd just do it as: .add :matches(#author, #publications) :matches(input,select,textarea) This means "match an input, select, or textarea that is a descendant of #author or #publications, which is a descendant of something with class=add. > And now it gets really wrong. If the rendering engines would really do that, > oh my god! > Sure enough the rendering engines would first jump to the ID #publications > (which is fast, cause it's a lookup, not a loop), check that the class is > .special (again a lookup, therefore fast) ans only then check for one of the > children being a form-interaction-element. So, not 3000 checks, but only > 302, as many as with your @set-variant! As Andrew said, CSS engines *do* work like that. They match 'backwards', starting from the end and moving backwards in the selector. There's a very good reason for this - it's really efficient in the most common matching case. The vast majority of CSS matching takes place when the page first loads. In order to make things look good, browsers do 'progressive rendering' - displaying parts of the page that they've downloaded before they've finished downloading the whole thing. These partial bits of pages get fed to the CSS engine to see what rules match against it. Now, think about how an HTML page is written. Take this example: <p><b>foo <span>bar</span></b> <i>baz</i></p> If you walk through that character by character, what happens? First, you notice that a <p> element is opening, but you don't know where it ends yet. Then you see a <b> open, then a <span>. Then the <span> closes, the <b> closes, and an <i> opens. Finally, the <i> closes, as does the <p>. Look at the way CSS works. When you string together a selector, each successive piece is either a child or descendant of what came before, or a following sibling. If you read it in reverse, then each successive element is either a parent/ancestor of what came before, or a previous sibling. Frex, in "p span", if you read it backwards you start with a "span", and then you know for a fact that the "p" has to be either an ancestor or a previous sibling. Now, combine these two. The browser has a rule, "p span" that it wants to match ASAP, so it can tell the rendering engine what to show as fast as possible. As the browser reads the HTML character-by-character, it eventually reaches the </span> tag. At this point it knows precisely where the span is, and more important, *it knows that it has a <b> parent and a <p> grandparent*. Because of this, it can immediately say "Yup, the rule matches." - all the information it needs is right there. That's the secret of browser CSS optimizations - if you start at the end of the selector, then as soon as you find something that matches the last bit, you have *all* of the information you need to check if the rest of the selector matches as well. You don't have to wait for anything, and can report back to the rendering engine very quickly. :has breaks this optimization. You can do, say, "b:has(+i)". This means "match a <b> that has an <i> sibling following after it.". In this case, you would *like* to immediately report to the rendering engine as soon as you hit the </b> tag and know precisely where the <b> is. But you can't - you have to wait until you check the next sibling to see if it's an <i>. This slows things down and complicates stuff significantly. So, the end result of this is that using rules that *start* with an #id isn't particular efficient. You're not allowing the browser to search only a portion of the page - it still has to check that rule against every element in the page, and then later check if the #id is on some ancestor. I still do this plenty, though, because it's obviously helpful - the CSS engine may still have to check the whole page, but I still know that it's *impossible* for it to match outside of the subtree rooted by the #id element, so I can be sure the rules won't get applied somewhere weird. ~TJ
Received on Sunday, 10 January 2010 00:13:10 UTC