skip to content

In Selenium 4, where does a RelativeLocator lookup actually execute, and what does that placement cost?

level: principalimportance: must knowfreq 38%

answer

  1. Not a protocol feature
  2. The browser does the geometry, in script
  3. One execute-script call per lookup
  4. Semantics ship with the client library
  5. Grid needs a custom locator to accept it

basics

~20 s

It runs inside the page. Selenium injects a bundled JavaScript atom with one execute-script call, which resolves the locators and reads every rectangle. So matching semantics are pinned to your client version, not to the browser driver.

solid answer

~40 s

`RelativeBy` overrides `findElements` to obtain a `JavascriptExecutor` and run the bundled `findElements.js` atom, passing the locator serialised as `root` plus `filters`; the root, the anchors, every `getBoundingClientRect()` read and the proximity sort all happen inside that single call. `relative` is not a W3C location strategy, so Java's `RemoteWebDriver` first offers `using: "relative"` through `By.Remotable`, falls back to running the locator itself when the remote end answers `invalid argument`, and caches that choice per locator class; a Selenium server can instead accept the payload through the `RelativeLocatorServerSide` custom locator. Python simply injects the same script every time. The costs are that "above" means whatever your client's atom says, that script execution in the current context is required, and that the work scales with the root locator's match count.

go deeper

for a junior

Recall that relative locators are a Selenium client feature rather than something the browser provides, and that Selenium reads element positions by running JavaScript in the page.

for a middle

Be ready to explain that the whole lookup is one injected script call which resolves the locators and reads every bounding rectangle, so it is one round trip and one snapshot of the layout.

for a senior

An interviewer expects you to know that relative is not a standard location strategy, that the client falls back to script injection when the remote end rejects it, and that a Selenium server registers a custom locator to accept it.

for a principal

Own the consequences of the placement: semantics versioned with the client, a dependency on script execution, cost scaling with the root's match count, and the blast radius of locators whose meaning is a claim about rendered boxes.

## The lookup runs in the page, as an injected script `RelativeBy` overrides `findElements(SearchContext)`. It obtains a `JavascriptExecutor` from the search context and executes the bundled `findElements.js` atom — held in `RelativeLocatorScript` on the Java side — passing the locator serialised as a map of `root` and `filters`. Everything the feature does happens inside that one call: - the root locator is evaluated in the page, using `querySelectorAll`, `getElementsByTagName` or `document.evaluate` depending on the strategy; - each anchor is resolved in the page; - `getBoundingClientRect()` is read for the anchor and for every candidate; - the survivors are sorted by centre distance and returned as element references. So a relative lookup is **one round trip**, not one per candidate, and the geometry is a single snapshot taken at that instant. The `By` you pass is serialised, not sent as an object: `By.id("bid-amount")` goes across as its CSS fallback, `{"css selector": "#bid-amount"}`, because `By.ById` serialises through the `ByCssSelector` it wraps. ## `relative` is not a W3C location strategy The WebDriver specification defines a fixed set of location strategies, and `relative` is not among them. Selenium works around that in two places. | Layer | What it does with `relative` | |---|---| | Java `RemoteWebDriver` | `RelativeBy` implements `By.Remotable` and reports `using: "relative"`. The internal `ElementLocation` helper prefers the remote path for any remotable locator, falls back to running the locator itself when the remote end answers `invalid argument`, and caches the choice per `By` class for the rest of the session. | | Selenium server | `RelativeLocatorServerSide` registers as a `CustomLocator` under the name `relative`, so a server that receives the payload can honour it by running the same atom itself. | Other bindings do not negotiate at all. Python's `find_elements` detects a `RelativeBy`, loads the same `findElements.js` from the package and runs it through `execute_script` every time. ## The constraint that surprises people Because the payload has to survive being turned into JSON, every `By` in the expression must be serialisable. Java's `RelativeLocator` enforces this eagerly with an internal check that looks for a `toJson` method on the locator's class and throws `IllegalArgumentException` — "Locator must be serializable to JSON using a `toJson` method" — when it finds none. A hand-rolled `By` subclass that works perfectly with `driver.findElement` is therefore rejected the moment it is used as a relative root or anchor. ## What the placement costs 1. **The semantics live in your client, not in the driver.** "Above" means whatever the atom bundled with your Selenium client version says it means. Upgrading the client can change matching on an unchanged page and an unchanged browser, and two languages in the same organisation are only consistent because they ship a copy of the same file. 2. **It depends on script execution in the current browsing context.** A relative locator cannot be evaluated where the driver will not run script for you, and it is evaluated against whatever context you are switched into, not against the page as a whole. 3. **The cost scales with the root locator's match count.** The atom reads `getBoundingClientRect()` for every candidate and re-resolves a locator anchor per candidate, so a loose root over a long live-bid ticker does far more layout work than a tight one. 4. **There is no re-evaluation and no readiness notion.** The filters see one snapshot of the rendered layout; nothing inside the mechanism observes the page settling. 5. **Errors surface as script errors.** An anchor locator that matches nothing produces a `no such element` failure raised from inside the injected script, which reads differently from the driver's own find-element failure. ## How a lead should frame it Relative locators are a **client-side convenience layered over ordinary find calls**, not a protocol capability the browser vendors implement. That framing answers most of the questions a team will bring you about them. It explains why behaviour is pinned to the client version rather than the browser; why the Grid needs its own `CustomLocator` registration to accept the payload; why the anchor rules and the 50-pixel `near` default are Selenium's choices rather than a standard; and why a locator that depends on rendered geometry is only as reproducible as the rendering. The practical consequence is about **blast radius**. A CSS or XPath expression that stops matching fails in one place. A proximity expression is a claim about the relationship between two boxes, so anything that moves either box — a new badge on the art-auction card, a wrapped lot title, a different viewport width in one job — changes the answer for every locator anchored on it. Knowing that the whole decision is one injected script reading one set of rectangles is what lets you predict which changes matter and which do not.

  • Why does a custom By subclass fail when used inside RelativeLocator.with()?
    Because the expression has to be serialised to JSON before it is handed to the injected script. Java checks the locator's class for a `toJson` method and throws `IllegalArgumentException` with "Locator must be serializable to JSON using a `toJson` method" when there is none, even though the same locator works fine with an ordinary `driver.findElement` call.
  • What does a relative lookup cost compared with an equivalent CSS query?
    It is still one round trip, but the work inside the page is larger: the root locator is evaluated, then `getBoundingClientRect()` is read for every candidate and a locator anchor is re-resolved per candidate before the survivors are sorted. A tight root keeps that bounded; a loose root over a long live-bid ticker does far more layout work.
  • Does upgrading the Selenium client change which elements a relative locator matches?
    It can. The matching rules live in the `findElements.js` atom bundled with the client, not in the browser driver, so a client upgrade can change results on an unchanged page and an unchanged browser. Selenium 4 has added filters this way, including the straight-axis variants.

saying these in an interview costs you the question

  • Says the browser driver implements the relative strategy natively
  • Claims relative is a W3C WebDriver location strategy
  • Thinks each filter costs its own round trip
  • Assumes the browser version determines the matching rules
  • Believes Grid needs no special handling for the payload