XPath axes

An axis is the direction a location step travels. Every step has one — child is the default, which is why book and child::book mean the same thing. Change the axis and you change which part of the tree you can see: up to ancestors, down into descendants, sideways to siblings, or off the tree onto attributes and namespaces.

Example

XPath on the Recipes sample:

//ingredient[contains(., 'pepper')]/ancestor::recipe/name

Matches an ingredient deep in the tree, then walks up the ancestor axis to the recipe that contains it and returns its name.

Open it in the Sandbox

All 13 axes

The child, attribute, and descendant-or-self axes have shorthand (nothing, @, and //). The rest have to be spelled out with the axis::nodetest form.

The 13 XPath axes
AxisSelectsExample
child::Direct children (default axis)child::book
parent::Parent nodeparent::catalog
ancestor::All ancestors up to rootancestor::section
ancestor-or-self::Ancestors including selfancestor-or-self::div
descendant::All descendantsdescendant::item
descendant-or-self::Descendants including selfdescendant-or-self::*
following::Everything after closing tagfollowing::chapter
following-sibling::Siblings after current nodefollowing-sibling::p
preceding::Everything before opening tagpreceding::title
preceding-sibling::Siblings before current nodepreceding-sibling::li
self::The current nodeself::node()
attribute::Attributes (shorthand: @)@id
namespace::Namespace nodesnamespace::*

Which axis do I actually need?

Most real expressions use three: child for going down one level, descendant-or-self (//) for going down any number, and attribute (@) for reading attributes.

The reverse axes earn their place when you have already matched something and need its context — ancestor:: to find the container, preceding-sibling:: to read the label before a value, following:: to grab everything after a marker element.

  • Going down one level: child:: — or just the element name.
  • Going down any number of levels: //, which is descendant-or-self::node()/.
  • Going up: parent:: for one level, ancestor:: for all of them.
  • Sideways: preceding-sibling:: and following-sibling:: stay within the same parent; preceding:: and following:: cross the whole document in document order.
  • Off the element tree: attribute:: (@) and namespace::, which XPath 3.1 drops.

Axes are reverse or forward, and that changes indexes

ancestor, ancestor-or-self, preceding, and preceding-sibling are reverse axes: they are numbered outward from the context node, so preceding-sibling::item[1] is the sibling *immediately* before you, not the first one in the document.

Every other axis is a forward axis, numbered in document order. This is the single most common source of surprising index results.

See them live

The Sandbox has an Axes panel: pick any node in the document and it shows how many nodes each axis reaches from there, with the diagram above highlighting the region. Clicking an axis runs it, so you can see the actual node set rather than reasoning about it.

Keep going