cswork / libraryOpen practice workspace

Articles

XPath Interview Questions: Axes, Predicates, and Reliable Element Selection

Practice XPath interview questions with reverse axes, predicate scope, namespaces, quote-safe literals, and selectors whose match counts are verified.

//li[1] returns A and C from two lists, while (//li)[1] returns only A. A selector can be syntactically valid, match real elements, and still answer a different question from the one you intended. XPath interview questions are often most useful when you predict the exact matched nodes before interacting with them.

Build your answer around context, axis direction, predicate scope, and expected match count. Longer selectors are not inherently more reliable, and an element found by XPath is not automatically visible, enabled, or ready to click.

Evidence boundary: The original HTML fixture and 14 selector assertions were executed with the browser's native document.evaluate. W3C, MDN, and Selenium documentation supports the surrounding rules. These are XPath 1.0-style browser exercises, not a Selenium driver run, employer question report, or assumption that every XPath engine supports the same version.

Use Robust Selenium Code to Extract Text from Two Elements to connect selector reasoning with test diagnosis.

Start with a fixture whose matches you can name

The core fixture has two sections, each containing a two-item list. A and B are siblings in the first list; C and D are siblings in the second.

<section id="one">
  <ul>
    <li id="a" class="item primary">A</li>
    <li id="b" class="item" data-active="true">B</li>
  </ul>
</section>
<section id="two">
  <ul>
    <li id="c" class="itemized">C</li>
    <li id="d" class="item" data-active="true">D</li>
  </ul>
</section>

The W3C XPath 1.0 specification defines location paths in terms of axes, node tests, and predicates evaluated in a context. Positions begin at one. W3C XPath specification

When explaining a selector, name the context node and expected IDs, not just “the first item.” First in which list? First in document order? First preceding sibling? Those are different contracts.

For a production test, add the interaction requirement separately. A unique node with ID b is a selector result. A visible, enabled control representing the current record is an application condition. Proving the first does not establish the second.

Why do parentheses change the result of the first predicate?

The native fixture produced these results:

ExpressionObserved IDs
//li[1]a, c
(//li)[1]a
//li[@data-active="true"][1]b, d
//li[1][@data-active="true"]none

In the first expression, the positional predicate is attached to the li step under each relevant parent. Each list's first li qualifies. Parentheses collect the selected li nodes before the outer positional filter chooses the first in document order.

The last two expressions demonstrate sequential filtering. Filtering active items first, then choosing position one, finds B and D within their respective lists. Choosing the first li first, then requiring it to be active, finds nothing because A and C are not active.

Predict both orders before testing. If someone adds an active item ahead of B, the first-active contract changes its selected node; the first-child-and-active contract follows a different rule. The selector should express the business requirement rather than whichever expression happens to pass today's fixture.

Using a global [1] to suppress duplicates is not a reliability improvement if duplicates signal a wrong population. It can hide a selector that matches both an active dialog and a stale hidden copy. Assert cardinality before deliberately choosing one result.

What changes when the search starts from an element?

The executed fixture evaluated two expressions with section one as context. .//li returned a and b, while //li returned a, b, c, and d. The leading dot makes the descendant search relative to the supplied node; the leading double slash does not mean “descendants of my current element” by itself.

In an interview, start with a stable container such as the relevant dialog, row, or section, then apply a relative path. This narrows the selection contract and makes duplicate labels elsewhere less likely to influence the result.

A helpful counterexample is a page with two Save buttons in separate forms. A document-wide text selector may match both. A relative search under the form for the intended record can distinguish them without relying on their incidental page order.

Scoping does not excuse a weak container locator. If the selected container itself is ambiguous, a precise descendant expression still starts from the wrong place. Verify the container's identity and count first, then verify its descendants.

Also state whether the test is evaluating from the document or from a previously located element. That small distinction can explain why a selector pasted into browser developer tools appears to work while a test using a nested search behaves differently.

Why does a reverse axis select the nearest preceding node?

In the original fixture, preceding-sibling::li[1] from D returned C. It did not return the earliest li in the document; it searched preceding siblings along a reverse axis.

From C, preceding::li[1] returned B, the nearest preceding li in that axis's order. But (preceding::li)[1] returned A after the outer filter operated on the collected node set in document order. These were observed browser results, not hypothetical output.

Explain the axis before the predicate. The preceding axis is broader than preceding-sibling; it can find nodes under earlier parents, while excluding ancestors. A selector meant to inspect the previous item in the same list should not accidentally cross into another list.

For a field label and its associated input, consider whether their actual relationship is sibling, descendant, or a semantic attribute association. Choosing a broad reverse axis because it reaches a nearby element can make the test vulnerable to an unrelated insertion.

The useful interview asset is a drawn node sequence with the context marked. Work outward along the chosen axis, apply the node test, then apply the positional predicate. Only afterward discuss whether that relationship is stable enough for a test.

How do nested text and class tokens create silent misses?

The fixture includes <button id="save">Save <span>draft</span></button>. //button[text()="Save draft"] matched nothing, while //button[normalize-space(.)="Save draft"] matched save. The direct text-node test does not combine nested element text into the expected label. MDN describes how normalize-space trims and collapses XPath whitespace.

The dot uses the element's string value, which includes descendant text. That can be useful, but it can also pull in hidden helper text or an icon label. Verify the actual content and the framework's accessible-name behavior rather than assuming visible text, string value, and accessible name are interchangeable.

For classes, contains(@class,"item") also matches the unintended itemized value on C. The tested token-aware expression returned a, b, and d:

//li[contains(concat(" ",normalize-space(@class)," ")," item ")]

The surrounding spaces distinguish a complete whitespace-delimited class token from a substring. For a simple class requirement, a CSS class selector may be clearer. Use XPath when its structural relationships provide a real benefit, not merely because the task mentions XPath.

Similarly, a partial text match can select Cancel upload and Cancel subscription when only one action is intended. Test a near-match fixture and explain why it must be excluded. Matching today's label is weaker evidence than demonstrating the correct exclusion boundary.

How do you represent a value containing both kinds of quote?

The fixture's data label is O'Reilly "Guide". XPath 1.0 string literals do not use JavaScript-style backslash escapes to insert the same quote delimiter. A value containing both quote types can be assembled with concat. MDN concat reference

This expression matched the intended element in the browser test:

//*[@data-label=concat("O", "'", "Reilly ", '"', "Guide", '"')]

There are two languages to quote correctly when constructing that expression in JavaScript or Java: the host-language string and the XPath literal inside it. Fixing one layer does not fix the other.

Do not concatenate arbitrary input directly between XPath quotes. A label containing a delimiter can make the expression invalid or alter its meaning. Build a tested literal encoder, or choose an element-selection strategy that avoids embedding untrusted values in an XPath expression where appropriate.

A focused encoder test set contains an ordinary label, a single quote, a double quote, both, and an empty value. Then verify that the generated expression finds only the exact intended label. A valid parser result alone does not prove safe or correct selection.

What do namespaces and result types change?

In XML with a default namespace, an unprefixed XPath element name does not automatically mean an element in that namespace. Supply a namespace resolver and a prefix, or use a carefully constrained namespace-aware test. MDN documents document.evaluate and its namespace-resolution and result-type arguments. XPath in JavaScript

The browser fixture's SVG rect was selected with:

//*[local-name()="rect" and
    namespace-uri()="http://www.w3.org/2000/svg"]

The observed ID was rect. Using local-name alone can conflate identically named elements from different namespaces. Include the namespace contract rather than treating the function as a universal escape from understanding XML.

For a browser probe, request a node result type when expecting elements. A count expression returns a number and needs a compatible result type. Snapshot membership and iterator behavior also differ around DOM changes; neither is permission to retain old node references indefinitely after a UI rerender.

In Selenium, a locator used for element interaction must return elements. An XPath ending in an attribute node is not an element locator simply because the browser's XPath engine can evaluate it. Selenium locator documentation

Can XPath cross an iframe or a shadow root?

A selector in the top-level document does not search the document inside an iframe. WebDriver requires switching to the relevant frame, then locating within that context. Return to the intended parent or default context afterward. Selenium frame documentation

Shadow DOM is another separate boundary. Locate the host and use the automation framework's supported shadow-root search API; do not claim that adding more slashes makes ordinary document XPath pierce it. Supported locator strategies inside that context depend on the framework. Selenium element-finding documentation

After a component replaces its DOM nodes, reacquire the element through a validated locator. Waiting on an old reference forever does not establish that the new element is ready. Distinguish a missing match, an ambiguous match, a stale reference, and an interaction-state failure in your explanation.

Selenium recommends stable, predictable locators such as IDs where available. A long absolute path tied to wrapper div positions can be less robust than a short semantic locator. Selenium locator guidance

Turn selection into a testable contract

For each expression, write the expected IDs and cardinality before running it. For the two-list fixture, preserve tests for predicate scope, relative context, reverse axes, text nesting, class tokens, quote handling, and SVG namespaces. Fourteen native assertions passed; that evidence covers the fixture, not every application DOM or automation library.

Add perturbations that challenge your intended relationship: an unrelated item before the lists, a duplicate button outside the container, and nested text inside the target. Explain which changes should leave the selected element unchanged and which should legitimately alter the result.

Sources and Further Reading