3 ms·
> .cls[attr="value"] is always slower than [attr="value"].cls Hey, have you instrumented this by chance? I assumed browsers have pretty smart query optimizers
by CharlesW 3y ago
> .cls[attr="value"] is always slower than [attr="value"].cls
Hey, have you instrumented this by chance? I assumed browsers have pretty smart query optimizers by now.
- AlienRobot 3y agoNo, I haven't. But I'm assuming it is based on the "right to left" concept while you are assuming it isn't based on browsers' ability to optimize this. The spec says nothing about it and I have never seen any documentation saying "this case will be optimized." And this is the problem with it, imho. Why would this be optimized but not .cls :has(.foo) .bar? Is :nth-child optimized? Should I do [attr]:nth-child or :nth-child:is([attr])? There are several ways to write the same complex selector and a lack of first-party guidelines about the best way to do it. Edit: to illustrate my point better, it used to be that if you wanted to make a table with alternating row colors you just used a class on each row. Then :nth-child(odd) and :nth-child(even) came out and everyone was like "just use that instead! No more classes in every row!" And now we have .foo-table :nth-child(odd) > td because obviously there's a tbody/thead/tfoot between tr and table and nobody knows if you should do this or .foo-table > :is(thead, tfoot, tbody) > :nth-child(odd) > td or which one is faster or if you should just use .odd > td because HTML TABLES ARE THE REAL EVIL TABLES IN WEB HISTORY. CSS TABLES WERE SAINTS COMPARED TO THIS. Anyway, every browser has different optimizations, and I have 5 different ways to make a row color alternate. This is the problem. When we have actual complex styling that makes use of the most powerful CSS selectors, all common optimization for simple stuff fall apart. So why even use powerful CSS selectors when they have potential to be permanently slow on HTML code that you may not ever be able to change in the future?
- CharlesW 3y ago> …while you are assuming it isn't… I'm honestly just professing ignorance! Along with generally wondering about the state of CSS query optimizers, I think the two have the same specificity, which (in my very-incomplete mental model) makes me think that they're about the same amount of work to resolve. If Nolan stops by maybe he can weigh in. :^)
- Sesse__ 3y agoI cannot speak for Firefox or WebKit, but at least in Chromium, these are going to be pretty much identical. Both are going to be bucketed based on the class (i.e., would only be considered for an element with class="cls", based on a hash table lookup), and then the [attr="value"] would be checked the obvious way. The reason why the right-to-left rule makes sense _between compounds in a complex selector_ (i.e., “the parts that are separated by a space”, for an imprecise term) is that this is the part that applies to the element you are computing style for right now. I.e., say that you have a rule like .a.b span { color: whatever; } This rule will apply to a <span> (the span is the “subject” of the rule) that has an .a.b element somewhere up higher in the tree. If you have made the rule specific enough that it rejects on the subject (say, you're computing style right now for a <h1>, not a <span>), you can skip the entire rule right away. (Most rules do, after all, not match most elements, so rejection is what you want to optimize for.) But once that “span” selector matches on some element, you need to walk the ancestor chain from that element, potentially every single ancestor, to verify that there's no parent that has both classes .a and .b, before you can reject the rule. So rejecting on the subject (save for :has(), which is a story in its own) needs to look at one element, rejecting on something non-subject is much worse. (The Bloom filter would stop early if you don't have at least something with .a and something with .b in your tree, a so-called “fast reject”, but it can't distinguish <div class="a"><div class="b"><span> from <div class="a b"><span>.) The best thing for selector performance _by far_ is to write the subject so that most elements don't even see it, by means of getting it into the right hash table bucket. A selector like “#xyz” is going to be _so_ much faster than “#xyz *”. But of course, most sites are not really style bound; CSS performance is something you should probably look at after you have your HTTP and JavaScript issues under control. (I work on Chromium style performance)
- CharlesW 3y agoVery interesting, thank you!