skip to content

In Selenium 4, what does the browser actually run for a By.cssSelector find versus a By.xpath find?

level: middleimportance: must knowfreq 74%

answer

  1. Two different matching engines inside the page
  2. One is shared with style-rule matching
  3. The other comes from a DOM XPath API
  4. An ordered node snapshot gets built
  5. querySelectorAll versus evaluate

basics

~20 s

A CSS find makes the browser call querySelectorAll on its native selector engine. An XPath find makes it call evaluate in the separate DOM-XPath evaluator. Both run inside one WebDriver command, so the gap is usually tiny.

solid answer

~50 s

The W3C WebDriver protocol that Selenium 4 speaks defines exactly what each strategy does. `By.cssSelector` makes the remote end call `querySelectorAll()` on the start node, using the browser's own selector engine - the same native code that matches style rules. `By.xpath` makes it call `evaluate()` with `ORDERED_NODE_SNAPSHOT_TYPE` and then walk the snapshot, going through a separate DOM-XPath evaluator that vendors do not tune as hard; Selenium's own docs describe XPath as typically not performance tested by browser vendors. `By.id`, `By.name` and `By.className` are not W3C strategies at all - the Java client carries a CSS equivalent for each (`#id`, `*[name='...']`, `.class`), so they cost what CSS costs. On a review queue of a few hundred rows both paths finish well inside the HTTP round trip that carried the command, so the engine difference is real but rarely the number that matters.

go deeper

for a junior

Be ready to say that Selenium sends the selector to the browser and the browser does the matching. Knowing that CSS and XPath are two different matching engines in the page is enough at this stage.

for a middle

An interviewer expects the mechanics: querySelectorAll for the CSS strategy, evaluate with an ordered node snapshot for XPath, and the fact that By.id and By.className ride the CSS path in the Java client.

for a senior

Show that you measured rather than repeated folklore. Explain that the engine difference is normally swamped by the per-command round trip, and name the specific cases where it stops being swamped.

for a principal

Own the framing. Engine choice is a micro-optimisation and a rule justified by it will not survive measurement, so point the team at command count and expression breadth as the levers that actually move numbers.

## The five strategies the protocol actually defines Selenium 4 speaks the **W3C WebDriver** protocol, and that specification does not leave locator behaviour to each driver's taste: it names the DOM call every strategy must make. The **table of location strategies** carries exactly five keywords - `css selector`, `link text`, `partial link text`, `tag name` and `xpath`. Whatever you write in Java has to arrive at the remote end as one of those five. ## What each strategy runs inside the page The specification's algorithms are short enough to recite in an interview: - **`css selector`** - call `querySelectorAll()` on the start node with your selector and return the matches. That is the browser's **native selector engine**, the same machinery that decides which style rules apply on every restyle. A selector the engine rejects comes back as the `invalid selector` error. - **`xpath`** - call `evaluate()` with the expression, the start node, `null`, `ORDERED_NODE_SNAPSHOT_TYPE` and `null`, then read `snapshotLength` and walk `snapshotItem` from index `0`. The spec notes the snapshot is used to promote operation atomicity. This is the **DOM-XPath evaluator**, a subsystem separate from the CSS engine. - **`tag name`** - call `getElementsByTagName()` on the start node. - **`link text`** - call `querySelectorAll("a")`, then for every anchor compute its **rendered text**, trim the whitespace, and keep the anchors whose text equals your string. `partial link text` is the same sweep with a substring test. That last one is the detail most candidates miss: a link-text find is a full anchor sweep plus a layout-dependent text read per anchor, not a cheap indexed lookup. ## Where By.id and By.className actually go | Java locator | Strategy on the wire | What the page runs | |---|---|---| | `By.cssSelector("#queueRow-4 button.review")` | `css selector` | `querySelectorAll` | | `By.id("queueRow-4")` | carries the CSS form `#queueRow-4` | `querySelectorAll` | | `By.name("applicantEmail")` | carries `*[name='applicantEmail']` | `querySelectorAll` | | `By.className("queue-row")` | carries `.queue-row` | `querySelectorAll` | | `By.tagName("tr")` | `tag name` | `getElementsByTagName` | | `By.xpath("//tr[@data-status='PENDING']")` | `xpath` | `evaluate` plus snapshot walk | | `By.linkText("Review")` | `link text` | `querySelectorAll("a")` plus text compare | `By.id`, `By.name` and `By.className` are not among the five. In the Java client they are built as `PreW3CLocator`s, each constructed with a format string that produces the equivalent CSS selector - `#%s`, `*[name='...']`, `.%s` - with the value CSS-escaped first. So on a scholarship-application review queue, `By.id("queueRow-4")` does exactly the work `By.cssSelector("#queueRow-4")` does. There is no privileged find-by-id fast path at Selenium 4. ## Why the CSS engine is usually the quicker of the two Two reasons, neither of them magic: 1. The CSS engine sits on the hot path of every page load and every restyle, so browser vendors invest heavily in it, including internal indexes by id, class and tag that let a selector skip most of the document. 2. The XPath evaluator exists to implement a DOM API most pages never touch. Selenium's own documentation says XPath selectors are typically not performance tested by browser vendors and tend to be quite slow. It also has to materialise an ordered snapshot before the driver reads a single node out of it. ## Why the difference usually disappears A find is not only a match. `driver.findElement(By.xpath("//tr[@data-status='PENDING']"))` costs, in order: 1. the client serialising `using` and `value` into a JSON body, 2. an HTTP request to the remote end at `POST /session/{id}/element`, 3. the driver invoking the location strategy inside the page, 4. the match itself, 5. an element reference serialised back and deserialised by the client. Step 4 is the only one your choice of engine touches. On a review queue of a few thousand nodes both engines finish that step well before the surrounding request does, which is why teams who rewrite a page object from XPath to CSS and then measure honestly usually find a change too small to see. ## Where the gap does become visible - The expression is pathological: a wildcard descendant scan carrying a string-value predicate. - The page is enormous - tens of thousands of nodes - so even a well-shaped expression walks a lot of DOM. - The same find runs inside a polling wait that re-issues it several times a second for many seconds. - The suite issues finds in the tens of thousands, so a microsecond-scale difference is multiplied enough to notice. Outside those cases, the honest answer to "which is faster" is that both are, by an amount your test cannot feel.

  • Does By.id get a faster code path on the wire than By.cssSelector?
    No. `By.id` is not one of the five W3C strategies. In the Java client it is a `PreW3CLocator` carrying the CSS form `#value`, escaped as needed, so the lookup runs through the same `querySelectorAll` call a `By.cssSelector` would use. It reads better; it does not cost less.
  • Why is By.linkText not the cheap shortcut its name suggests?
    The specification implements it as `querySelectorAll("a")` followed by a comparison of each anchor's rendered text, trimmed. So it visits every anchor in the search context and needs rendered text, which depends on layout. That is more work than one CSS match, not less.
  • Can a CSS selector ever cost more than an XPath expression on the same page?
    Yes. Cost tracks how many nodes an expression makes the engine consider, not which engine runs it. A broad descendant chain such as `div div div span` over a long queue can touch more nodes than an XPath anchored on an id. Engine choice is one factor; expression breadth is usually the bigger one.

Both are doors into the same building. CSS uses the main entrance the browser already keeps polished because every page load goes through it, while XPath uses a side door that works fine but nobody has resurfaced in years.

saying these in an interview costs you the question

  • Claims XPath is always dramatically slower than CSS in every browser
  • Thinks By.id uses a special fast command rather than a CSS selector
  • Believes the driver compiles and caches a locator between find calls
  • Assumes By.linkText is cheap because it matches only a short string
  • Says the two are identical because both finish in one command