Hacker News new | ask | show | jobs
by the_other 583 days ago
Is that really how it works in browsers and other rendering engines?

Intuition suggests to me that it wouldn’t start with CSS and then find all the matching DOM nodes. I would expect it started at each DOM node and then found the CSS rules which might apply.

So “I’m adding an A to the tree; what are all the CSS rules with or A or * at the rightmost token; which of that set applies to my current A; apply the rules in that sub set”. Going depth first into the DOM like this should result in skipping redundant CSS, and (as my imagination draws it) reduce DOM traversals.

2 comments

There's three different modes of running a selector in typical browsers:

  (a) Element#matches
  (b) Element#querySelector(All)
  (c) By the engine for updating style and layout

The GP seems to be talking about (b), but even then browsers are checking each element one by one not advancing through the selector state machine in parallel for every element. (There's one exception in the old Cobalt which did advance the state machines IIRC).

(a) and (c) are conceptually very similar except that when doing (c) you're checking many elements at the same time so browsers will do extra upfront costs like filling bloom filters for ancestors or index maps for nth-child.

In TFA they're doing .matches() which I would expect to be slower than a hash map lookup, but for a simple selector like they're doing (just tag name) it shouldn't do much more then:

  (1) Parse the selector, hopefully cache that in an LRU
  (2) Execute the selector state machine against the element 
    (2.1) Compare tagName in the selector

Apparently Nokogiri implements CSS in a very inefficient way though by collecting ancestors and then converting the CSS into xpath and matching that:

https://github.com/sparklemotion/nokogiri/blob/e8d30a71d70b2...

https://github.com/sparklemotion/nokogiri/blob/e8d30a71d70b2...

I'd expect that to be an order of magnitude slower than what a browser does.

In browsers, DOM parsing starts before (all) CSS is loaded and parsed. Also, the sizes of elements in the flow are (by default) dictated by the text content, so it really does not make sense to try to paint a page in a root-to-leaf order.