Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Put the positional predicate on the step whose candidates you want to count. XPath positions start at 1, so //catalog/item[3] selects the third item child for each matching catalog. To select the third item in the entire result sequence, parenthesize the complete path: (//catalog/item)[3].

The two meanings of “third element”

XPath does not have one universal counter for a path. A predicate counts the sequence produced at the point where it is attached. That distinction explains why two expressions that look almost identical can return different nodes.

Expression What it selects
//catalog/item[1] The first item child of every matching catalog.
//catalog/item[3] The third item child of every matching catalog.
(//catalog/item)[1] The first item in the complete result sequence.
(//catalog/item)[3] The third item in the complete result sequence.

XPath 1.0, 2.0 and 3.1 all support this positional syntax. The XPath 3.1 Recommendation states that the first item in a sequence is always position 1. Your browser, XML library, scraper or editor may support a different XPath version, so check that host application’s documentation before using version-specific features.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Basic positional predicates

Numeric shorthand

A number in square brackets is shorthand for comparing the context position with that number:

//catalog/item[1]
//catalog/item[2]
//catalog/item[3]

For a document such as:

<catalogs>
  <catalog id="a">
    <item>A1</item>
    <item>A2</item>
    <item>A3</item>
  </catalog>
  <catalog id="b">
    <item>B1</item>
    <item>B2</item>
    <item>B3</item>
  </catalog>
</catalogs>

//catalog/item[3] returns A3 and B3, because the item step is evaluated separately for each catalog context.

Explicit position()

Write the same test explicitly when readability matters or when combining it with other predicates:

//catalog/item[position() = 3]

The numeric form and the position() = 3 form are equivalent for this case. XPath positions are one-based; there is no position zero.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

First, last and previous-to-last

Use last() for the final candidate in the current sequence and subtract from it for positions counted from the end:

//catalog/item[last()]
//catalog/item[last() - 1]

These expressions select the last and second-to-last item child for each matching catalog. If a sequence contains fewer items than requested, that context contributes no node rather than an error.

Rank #2
XPath 2.0 Programmer's Reference
  • Used Book in Good Condition

Local position versus global result position

Why //item[1] can return many nodes

The abbreviation // expands into descendant-or-self and child steps. The predicate belongs to the item child step, so //item[1] means “the first matching item child at each relevant parent,” not “the first item in the document.” If several parents each have an item child, several nodes can match.

Parentheses create a single sequence to index

Wrap the full path before applying the predicate when you need one document-wide result:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
(//item)[1]
(//item)[3]

The path is evaluated first; the outer predicate then sees the complete sequence. This is the reliable pattern for “the third match overall.”

Make the scope obvious in production code

When a selector is maintained by several people, prefer a form that states its intent:

//catalog/item[position() = 3]
(//catalog/item)[position() = 3]

The first is per-catalog; the second is global. A comment or variable name such as thirdItemPerCatalog or thirdItemOverall can prevent later refactoring mistakes.

Combining position with other conditions

Predicates run from left to right

Adjacent predicates are applied sequentially. A later predicate sees the result left by the earlier one:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
//item[@type = 'x'][2]
//item[2][@type = 'x']

The first expression filters to type x items and then selects the second type-x item for each step context. The second selects each context’s second item first and only then checks its type attribute. They are not interchangeable.

Filter first, then number the filtered set

For “the third item that is in stock,” put the attribute test before the position test:

//catalog/item[@stock = 'yes'][3]
//catalog/item[@stock = 'yes'][position() = 3]

For “the third child, provided it is in stock,” reverse the predicates:

//catalog/item[3][@stock = 'yes']

Apply a global position after filtering globally

To obtain the third in-stock item across the entire document, parenthesize the filtered path:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
(//catalog/item[@stock = 'yes'])[3]

Without the parentheses, the position remains local to each parent context.

Use ranges and odd/even positions

XPath 1.0-compatible range tests can be written with numeric comparisons:

//item[position() >= 2 and position() <= 4]
//item[position() mod 2 = 1]

The first selects positions 2 through 4 in each context. The second selects odd positions. Parenthesize the complete path if those ranges must apply to one global result sequence:

(//item)[position() >= 2 and position() <= 4]

Reverse axes: the position can run backward

Axis direction affects how predicate positions are assigned. On a reverse axis, such as preceding, position 1 is the nearest matching node in reverse document order:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
preceding::foo[1]

This selects the nearest preceding foo. Parentheses change which sequence is filtered:

(preceding::foo)[1]

That parenthesized expression applies the predicate to the resulting sequence in document order, so it can select a different node. The final node-set or sequence returned by an axis step is presented in document order even though a reverse axis uses reverse order to assign context positions. Test reverse-axis selectors against a small fixture rather than assuming every axis behaves like child.

A practical method for writing the right selector

  1. Define the population. Decide whether you mean children of each parent, descendants of each context, or one complete result list.
  2. Write and inspect the unnumbered path. Start with //catalog/item or a more specific path and verify that it returns the intended nodes.
  3. Choose the scope. Append [n] for a step-local position; wrap the path as (path)[n] for a global position.
  4. Order filters deliberately. Put attribute or text conditions before the position when numbering only qualifying nodes.
  5. Check direction. For preceding, ancestor and other reverse axes, determine which node is position 1.
  6. Test edge cases. Include zero matches, fewer than n matches, multiple parents, and duplicate attributes.
  7. Confirm host support. XPath 1.0 engines may not provide functions or sequence features introduced later.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common mistakes and fixes

Symptom Likely cause Fix
Several nodes are returned by //item[1]. The predicate is local to each parent context. Use (//item)[1] for the first global match.
The “third available” item is wrong. Position was tested before the availability filter. Use //item[@available='true'][3].
An expression returns nothing although matching nodes exist. The requested position is larger than a context’s sequence, or the predicate order removed the node. Evaluate the unnumbered path, then add predicates one at a time.
The nearest preceding node is not selected. Parentheses changed reverse-axis context ordering. Use preceding::foo[1] for the nearest qualifying node.
Modern syntax fails in an older tool. The host embeds an older XPath implementation. Check its supported version and use XPath 1.0-compatible expressions where necessary.

Verifying selectors in code

Browser developer tools

In browser DevTools, open the Console and evaluate an XPath with $x() where supported:

$x("(//catalog/item)[3]")

Inspect the returned array and confirm both the count and the node’s parent. DevTools helpers are a convenience, not part of the XPath language, so use your application’s own evaluator for final tests.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Automated tests

Keep a minimal XML fixture containing two parents, unequal child counts, and filtered attributes. Assert both the local and global selectors. This catches accidental removal of parentheses during maintenance and makes the intended scope executable documentation.

Or skip the browser setup

If your goal is a screenshot of a page rather than XML-node extraction, ScreenshotNeo provides a one-request capture API. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response reports the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

Performance, reliability and version considerations

  • Make the path as specific as practical. Narrowing by element name, parent and attributes reduces the candidate sequence that the host must evaluate.
  • Do not assume a positional result is stable when the source order can change. If a durable identity exists, prefer an ID or distinctive attribute and use position only as a deliberate fallback.
  • Global expressions such as (//item)[3] require the host to form the complete matching sequence before indexing it; a step-local expression can be cheaper on very large documents, but actual performance depends on the XPath engine.
  • Dynamic HTML may differ from the original response. Evaluate XPath after the application has inserted the nodes you intend to count.
  • XPath 3.1 is a W3C Recommendation from 21 March 2017 and adds maps and arrays, but those features are unnecessary for ordinary element indexing. XPath 2.0’s numeric predicates, left-to-right filtering and reverse-axis rules remain directly relevant to these examples.

Short checklist

  • Remember that the first position is 1.
  • Use step[n] for the nth match in each step context.
  • Use (path)[n] for the nth item in the complete result.
  • Place qualifying predicates before [n] when you want to number only filtered items.
  • Check reverse-axis ordering and host XPath version.

Frequently Asked Questions

Can I use a variable instead of a literal position?

Yes. The exact syntax depends on the host language and XPath version; construct the expression or bind a variable according to that API, then keep the same local-versus-global parentheses rule.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What happens when two nodes share the same position?

Positions are assigned within each predicate context. Separate parent contexts can each have a position 1 or 3, which is why a step-local expression may return multiple nodes.

Does whitespace or indentation affect positional selection?

Only if your path selects text nodes, such as text(). Element paths like //catalog/item[3] count matching element nodes, not formatting whitespace.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.