Core Tools intermediate functions reference

XPath 1.0 Functions Reference

Every function available in browser XPath, grouped by what it does, with the test-automation use case for each and the XPath 2.0 functions you must not reach for.

XPath 1.0 Functions Reference

Browsers, Selenium, Playwright and Appium all evaluate XPath 1.0. That gives you exactly 27 functions. This page lists all of them with the situations where each one earns its place in a locator.

If a function is not on this page, it will throw an “invalid expression” error in your tests. See the XPath versions guide for what lives in 2.0 and 3.1 and how to work around it.

Node-set functions

FunctionReturnsTest automation use
last()number of nodes in the context(//tr)[last()] last row
position()index of the context node//li[position() < 4] first three
count(node-set)how many nodes//ul[count(li) > 5] lists with more than five items
id(string)element with that IDrarely used; //*[@id='x'] is clearer
local-name(node-set?)name without namespace prefix//*[local-name()='svg']
namespace-uri(node-set?)the namespace URI//*[namespace-uri()='http://www.w3.org/2000/svg']
name(node-set?)full qualified name//*[name()='svg:path'] in XML

String functions

FunctionReturnsTest automation use
string(object?)string valuestring(//h1) for assertions
concat(s1, s2, ...)joined stringquotes: concat("It", "'", "s")
starts-with(s, prefix)boolean//input[starts-with(@id, 'user-')] dynamic ids
contains(s, sub)boolean//*[contains(@class, 'btn')]
substring-before(s, sep)text before separatorsubstring-before(@href, '?')
substring-after(s, sep)text after separatorsubstring-after(@id, 'row-')
substring(s, start, len?)slice, 1-basedsubstring(@id, 1, 4)='user'
string-length(s?)length//input[string-length(@value) > 0] non-empty
normalize-space(s?)trimmed, single-spaced//button[normalize-space()='Save']
translate(s, from, to)character mappingcase folding, see below

substring counts from 1, not 0. substring('hello', 1, 2) is he.

translate maps each character in from to the character at the same position in to. Characters in from with no partner in to are deleted. Two idioms:

translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')   lower-case
translate(@data-price, '$,', '')                                               strip $ and commas

Boolean functions

FunctionReturnsTest automation use
boolean(object)boolean conversionboolean(//div[@role='alert']) “is there an alert”
not(boolean)negation//button[not(@disabled)]
true()trueplaceholders and generated expressions
false()falsesame
lang(string)matches xml:lang//p[lang('de')] localisation tests

not() is a function, not an operator. not @disabled is a syntax error.

Number functions

FunctionReturnsTest automation use
number(object?)numeric conversionnumber(//span[@class='qty']) = 3
sum(node-set)totalsum(//td[@class='price']) cart totals
floor(n)round downmaths on positions
ceiling(n)round upsame
round(n)nearest integersame

sum() converts each node’s string value to a number. Any non-numeric value makes the whole result NaN, so strip currency symbols with translate() first or use it only on clean data cells.

Functions that do not exist in XPath 1.0

These are the ones people reach for after reading generic XPath tutorials. None of them work in a browser:

MissingVersionXPath 1.0 workaround
ends-with(s, suffix)2.0substring(s, string-length(s) - string-length('suffix') + 1) = 'suffix'
lower-case(s), upper-case(s)2.0translate() with the alphabet
matches(s, regex)2.0combine contains, starts-with, translate
replace(s, regex, with)2.0translate() for single characters only
string-join(), tokenize()2.0not possible; restructure the locator
exists(), empty()2.0boolean(), not()
compare(), codepoints-to-string()2.0not needed for locators

The ends-with workaround in full, for an id that ends in -email:

//input[substring(@id, string-length(@id) - string-length('-email') + 1) = '-email']

It is ugly. In practice contains(@id, '-email') is usually good enough.

Where each function goes

Functions can appear in predicates, as the whole expression, or nested:

//tr[normalize-space(td[1]) = 'Widget']              inside a predicate
count(//tr[td[3] > 0])                              as the whole expression, returns a number
//td[count(preceding-sibling::td) = 2]              nested in arithmetic
concat(normalize-space(//h1), ' | ', string(//title))  building a string

Selenium and Playwright locators must return elements, so an expression that returns a number, string or boolean will error when used as a locator. Use those forms in DevTools or scraping code instead.

Try It Yourself

Open in Playground →

Next Steps

  1. Position and Indexing - position(), last() and reverse axes
  2. Case-Insensitive Matching - translate() in depth
  3. XPath 1.0 vs 2.0 vs 3.1 - Where the other 100+ functions live