skip to content

In Selenium, what changes when you call findElement on a WebElement instead of on the WebDriver?

level: juniorimportance: should knowfreq 58%

answer

  1. Two kinds of object can start a search
  2. The driver is not the only context
  3. One interface, two methods, several implementers
  4. SearchContext declares findElement and findElements
  5. Scoped find posts to element/{id}/element

basics

~10 s

Selenium's WebDriver and WebElement both implement SearchContext, so an element can start a search of its own. A find called on an element sends a find-from-element command and searches only that element's descendants.

solid answer

~40 s

In Selenium 4 the `SearchContext` interface declares `findElement(By)` and `findElements(By)`, and both `WebDriver` and `WebElement` implement it (so does the object `WebElement.getShadowRoot()` returns). Calling the method on the driver posts to `/session/{sessionId}/element`; calling it on an element posts to `/session/{sessionId}/element/{elementId}/element`, naming that element as the search's start node. The remote end then runs the same location strategy — `querySelectorAll`, `getElementsByTagName`, `document.evaluate` — rooted at that node. On a page listing several bus passes, that lets you hold one pass card and search inside it instead of writing one long page-wide selector. The cost is a second round trip, since the card has to be located before the scoped command can name it.

code

java · 11 lines
java
WebDriver driver = new ChromeDriver();
driver.get("https://transit.example/bus-pass/top-up");

WebElement card = driver.findElements(By.cssSelector("#saved-passes li.pass-card")).get(1);

WebElement balance = card.findElement(By.cssSelector("span.balance"));
System.out.println(balance.getText());

card.findElement(By.cssSelector("button.top-up")).click();

driver.quit();

go deeper

for a junior

Be ready to say that an element can be searched from, not only clicked, and that the search then covers what is inside it. Naming SearchContext as the interface the driver and an element share is a strong answer here.

for a middle

Explain that a scoped find is a separate browser command naming the parent element in its URL, and that the browser runs the same location strategy rooted at that node. Mention the extra round trip it costs.

for a senior

Show what the scope does and does not guarantee in a running suite: the parent reference must still resolve when the second command arrives, or the browser rejects it with a stale element reference before it ever looks at the locator.

for a principal

Separate the mechanism from any convention built on top of it. An interviewer expects you to state precisely what element scoping guarantees, so that a team rule about when to use it rests on something checkable rather than on habit.

## The interface both objects share Selenium's Java bindings declare one very small interface, **`SearchContext`**, carrying exactly two methods: `WebElement findElement(By by)` and `List<WebElement> findElements(By by)`. `WebDriver` extends it, `WebElement` extends it, and in **Selenium 4** the object handed back by `WebElement.getShadowRoot()` implements it as well. A `By` object describes *how* to look; the `SearchContext` it is handed describes *where*. That separation is the whole point of the design: an element is a place you can search **from**, not only a thing you can click. So `driver.findElement(By.cssSelector("button.top-up"))` and `card.findElement(By.cssSelector("button.top-up"))` are the same locator applied to two different starting places. The first asks the page. The second asks one bus-pass card. ## What actually travels over the wire The two calls are different commands, not one command with a filter bolted on afterwards. For an element-scoped find: 1. The client sends `POST /session/{sessionId}/element/{elementId}/element` with a body of `{"using": ..., "value": ...}`, where `{elementId}` is the reference the browser handed back when the card itself was located. 2. The remote end resolves that element id **first**. If the node behind it is no longer connected to the active document, the command fails with `stale element reference` and the locator is never evaluated at all. 3. Only then does it run the location strategy, using the resolved node as the **start node**. 4. The matching nodes come back as element references, exactly as they would from a page-wide find. | | `driver.findElement(by)` | `card.findElement(by)` | |---|---|---| | HTTP request | `POST /session/{sessionId}/element` | `POST /session/{sessionId}/element/{elementId}/element` | | Start node | the document | the element named in the URL path | | Commands needed to get there | one | two, because the card must be located first | | Failure when the context is gone | `no such window` | `stale element reference` | The plural forms mirror this precisely: `/element` becomes `/elements`, and `/element/{elementId}/element` becomes `/element/{elementId}/elements`. ## What the browser does with the start node The start node is not a hint the browser may ignore; it is the object the DOM call is made on. The W3C WebDriver specification that Selenium 4 implements defines five location strategies, and each one is rooted at that node: - **`css selector`** calls `querySelectorAll` on the start node. - **`tag name`** calls `getElementsByTagName` on the start node. - **`link text`** and **`partial link text`** collect the `a` elements under the start node and compare their rendered text. - **`xpath`** evaluates the expression with the start node as the context node. For the first four, the browser method itself can only ever hand back descendants of the node it was called on, so the boundary is airtight — no filtering by Selenium is needed or done. XPath is the single exception, because an expression is free to begin at the document root or to walk upwards along an axis. A scoped XPath stays scoped only because you wrote it that way. ## A bus-pass top-up page Suppose the top-up page lists the passes a rider has saved: ```html <ul id="saved-passes"> <li class="pass-card"><h3>Zone 1-2 Monthly</h3> <span class="balance">12.40</span> <button class="top-up">Top up</button></li> <li class="pass-card"><h3>Zone 3 Weekly</h3> <span class="balance">3.10</span> <button class="top-up">Top up</button></li> </ul> ``` Every card carries its own `span.balance` and its own `button.top-up`, so page-wide those locators are ambiguous. Hold the card you mean and the ambiguity disappears without inventing a longer selector: ```java WebElement card = driver.findElements(By.cssSelector("#saved-passes li.pass-card")).get(1); WebElement balance = card.findElement(By.cssSelector("span.balance")); card.findElement(By.cssSelector("button.top-up")).click(); ``` - The locator used inside the card is short and reads like the markup around it. - The restriction is enforced by the browser, not by your code sifting through page-wide results. - Nothing outside the card can match, even though the other card carries the identical class. - The card reference can be reused for several lookups in a row, each one a fresh scoped command. ## What it costs, so you can describe the trade A nested lookup is two browser commands where a single descendant selector on the driver would be one; the Selenium documentation makes the same observation about nested lookups. The second command also carries a precondition the single one does not: the parent reference must still resolve when the command arrives, or the remote end rejects it before it looks at the locator. Neither point makes scoping wrong. They are simply what you should be able to say about it — you are buying a search space you control explicitly, and locators that do not have to encode the whole path from the document root, in exchange for a round trip and a reference whose lifetime you now have to think about.

  • Does scoping a Selenium find to an element reduce the number of commands the client sends?
    No, it adds one. The card has to be located first, so a nested lookup is two commands where a single descendant selector on the driver would be one. What scoping buys is a search space the browser itself restricts and a shorter locator, not fewer round trips.
  • What does a scoped find return if the element you are searching from has already been removed from the page?
    It fails with `stale element reference`, surfaced in Java as `StaleElementReferenceException`. The remote end resolves the element id in the request path before it searches, so a disconnected node stops the command there and the locator is never evaluated.

Searching from an element is like handing someone one ticket wallet and asking them to find the monthly pass in it, rather than sending them into the whole bus station with the same instruction.

saying these in an interview costs you the question

  • Says only the driver can find elements, so every locator must be page-wide
  • Thinks element scoping is a client-side filter over page-wide results
  • Believes a scoped find costs the same single command as a page-wide find
  • Assumes an element can only search its direct children, not deeper descendants
  • Claims WebDriver and WebElement share no interface, so scoping needs a helper