3

In CSS I need to select all the direct children of an element with the class foobar. The obvious way would be to do this:

.foo > * {
  foo: bar;
}

There are some similar questions on Stack Overflow, but I don't think this question is a duplicate. The question simply asked how to select all direct children of an element. The answer said to use something like what I showed above. But a comment there and a comment elsewhere claim that this approach is extremely inefficient, claiming that the * selector would result in all elements being selected before the child selector took effect:

Be aware that this is an extremely inefficient selector! Css selectors are validated from right to left so by using the asterix (*, aka universal selector) at the end of the selector the browser will select all elements and then starts filtering further (in this case: only the elements that are direct children of the 'body' element). So for small pages this would not be an issue but in larger single page apps it can slow things down...

(Frankly this sounds suspicious to me; are browser selection algorithms really that non-clever?)

So this question is different: how then do I make an equivalent selection that is efficient?

Do I do this? (Note that I updated this option; I like using :root because I don't have to introduce any dummy elements.)

.foo > :not(:root) {
  foo: bar;
}

Or do I do this?

.foo > :nth-child(odd), .foo > :nth-child(event) {
  foo: bar;
}

Are those really more efficient than .foo > *? Do you have a better suggestion?

(In my particular use case, there will only be around two or three children, but the entire document could be large.)

halfer
  • 19,824
  • 17
  • 99
  • 186
Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • Please read the comments I linked to. The complaint was not about the performance of `>`, but about the performance of `*`. – Garret Wilson Aug 17 '18 at 21:25
  • If you can replace '.foo' class with an id, that males Things faster – Poul Bak Aug 17 '18 at 21:27
  • 3
    `:nth-child(1n)` might be a bit more concise. – Paul Abbott Aug 17 '18 at 21:29
  • I don't think think * is slow, because it can use native function, unlike many other selectors. – Poul Bak Aug 17 '18 at 21:30
  • 4
    As a side note, I think the `*` is the most performant here because there is no condition and you will get everyhing (this is what you need), if you will use other selector you will add a condition so the browser will do tests but all of them will be true and you will get all the elements too – Temani Afif Aug 17 '18 at 21:31
  • 1
    What Temani said. `.foo > *` is as efficient as you're going to get. – Alohci Aug 17 '18 at 21:34
  • But see the comment I included. Is that comment, about browser evaluation from right to left, incorrect then? – Garret Wilson Aug 17 '18 at 21:36
  • @GarretWilson You could investigate if the comments about poor performance with * have become obsolete with newer browser versions (maybe since 2012 or thereabouts). – Andrew Morton Aug 17 '18 at 21:38
  • @AndrewMorton An answer here that "that comment is no longer relevant and `.foo > *` is sufficiently efficient" would be a valid answer to this question, if references were provided. :D But note that the comment was made as recently as 2016. – Garret Wilson Aug 17 '18 at 21:41
  • @GarretWilson - Nope. That's how it's done. But any alternative is guaranteed to be worse given the requirement. Selectors are evaluated right-to-left for efficiency. While `.foo > *` requires every element to be visited and checked, each check is extremely simple (just test its parent) and each element needs to be visited just once. – Alohci Aug 17 '18 at 21:42
  • 2
    The "browsers select right to left" statement is outdated. A decade at least. Browsers have gone through a lot of improvements to keep things fast. There's no harm in using `.foo > *`, just like there's no harm in using any CSS selector. Seriously. Even if you **really, really try** you won't get CSS selectors to affect performance. – Facundo Corradini Aug 17 '18 at 22:02
  • 1
    @Facundo Corradini: That statement is not outdated, merely an oversimplification (and it always has been one). As you've said, lots of improvements and optimizations have been made to selector matching algorithms over the years, but fundamentally everything still matches RTL for fast elimination. It's even [canonized in selectors-4](https://drafts.csswg.org/selectors-4/#match-against-element) for some reason, despite originating as an implementation detail. – BoltClock Aug 19 '18 at 16:36

0 Answers0