Getting Started beginner predicates fundamentals

Predicates: Filtering with [ ]

Predicates are the square-bracket filters that turn a broad selection into exactly the elements you want. Learn how they evaluate, how they chain, and the classic index trap.

Predicates: Filtering with [ ]

A predicate is anything inside square brackets. It filters the nodes selected by the step before it, keeping only the ones for which the predicate is true.

//button              all buttons
//button[@type='submit']   only buttons whose type attribute equals "submit"

Almost every useful XPath is a location step plus a predicate. Getting predicates right is most of the job.

How a predicate is evaluated

For every node selected by the step, XPath evaluates the predicate with that node as the context node. If the result is true, the node stays.

Three kinds of expressions are common inside predicates:

ExpressionExampleEvaluates to
Comparison[@type='submit']boolean
Existence[@disabled]true if the attribute exists
Number[3]shorthand for [position()=3]

The existence form trips people up. //input[@required] selects inputs that have a required attribute, regardless of its value. It does not test for required="true".

Chaining predicates

You can stack predicates. Each one filters the result of the previous one, and each is evaluated in turn:

//input[@type='text'][@name='email']

For attribute conditions, chaining is the same as using and:

//input[@type='text' and @name='email']

The two forms differ only when a numeric predicate is involved, because position() is recalculated after each filter:

//li[@class='item'][1]     the first li among those with class="item"
//li[1][@class='item']     the first li child of each parent, only if it has class="item"

Read chained predicates left to right and ask “what is the set at this point?”

The index trap

//div[1] does not mean “the first div in the document”. It means “every div that is the first div child of its parent”. If ten parents each contain a div, you get ten results.

To pick the first result of a whole expression, wrap it in parentheses first:

(//div)[1]           the first div in document order
(//div)[last()]      the last div in document order

This is one of the most common mistakes in test suites, and it silently returns the wrong element instead of failing.

Predicates on any step

Predicates are not limited to the final step. They can sit on any step in the path:

//form[@id='checkout']//input[@name='zip']
//table[@data-testid='orders']/tbody/tr[td[normalize-space()='Shipped']]/td[1]

The second example has a predicate that itself contains a path (td[...]). A nested path inside a predicate is true if it selects at least one node, so tr[td[...]] reads as “rows that have a td matching this”.

Boolean predicates you will use daily

//button[not(@disabled)]                     enabled buttons
//a[@href and normalize-space()]             links with an href and visible text
//input[@type='checkbox' and @checked]       checked checkboxes (attribute present)
//div[@role='alert' or @aria-live='polite']  either condition
//li[position() > 1]                         skip the first item
//li[position() mod 2 = 0]                   even items

Robustness notes

Stable: attribute and text predicates that describe what the element is. //button[@data-testid=‘save’]

Fragile: numeric predicates that describe where the element is. //div[3]/button[2] breaks the moment someone adds a banner.

If you must use an index, keep it as close to the target as possible and anchor the path to a stable container first.

Try It Yourself

Open in Playground →

Then change the predicate to [1] and to (...)[1] and compare the counts.

Next Steps

  1. Node Types and Wildcards - What *, node() and text() actually select
  2. Position and Indexing - position(), last() and the reverse-axis surprise
  3. Logic: and, or, not - Combining conditions cleanly