Selector syntax

Locators in jab-rpa use a CSS-selector-like syntax to describe elements in the Java accessibility tree. The selector is a single string passed to JabDriver.locator().

Quick examples

# By role
driver.locator("push_button")

# By role and attribute
driver.locator("push_button[name='Clear']")

# By state pseudo-class
driver.locator(":require-state(enabled):require-state(focusable)")

# Chaining (descendant combinator)
driver.locator("dialog").locator("push_button")

Role matching

The role is written as the first identifier in the selector. Spaces in the JAB role name are replaced with underscores and the comparison is case-insensitive.

JAB role Selector
push button push_button
check box check_box
radio button radio_button
page tab page_tab

Write * to explicitly match any role (equivalent to omitting the role). A selector with no role (or with *) matches elements of any role.

State pseudo-classes

Use :require-state() to require that the element has a given state, and :exclude-state() to exclude elements with a given state. Multiple states are AND-ed together.

driver.locator(":require-state(enabled)")                   # any enabled element
driver.locator(":require-state(enabled):require-state(focusable)") # enabled AND focusable
driver.locator(":exclude-state(disabled)")                  # not disabled

States are lowercased and spaces replaced with underscores, matching the same transformation applied to roles.

Attribute selectors

String attributes

[ name  =  "value" ]    # exact match
[ name ~= "word"   ]    # contains word (whitespace-separated)
[ name ^= "prefix" ]    # starts with
[ name $= "suffix" ]    # ends with
[ name *= "substr" ]    # contains substring

Supported string attribute names: name, description, states, states_en_us, text, actions.

Flags — add before the closing ]:

Flag Meaning
i Case-insensitive
r Interpret value as regex
driver.locator("[name='clear' i]")          # case-insensitive
driver.locator("[name='button[0-9]+' r]")   # regex

Integer attributes

[ x == 100 ]   # equal
[ x != 100 ]   # not equal
[ x < 100]     # less than
[ x <= 100]    # less or equal
[ x > 100]     # greater than
[ x >= 100]    # greater or equal

Supported integer attribute names: x, y, width, height, children_count, depth.

Boolean attributes

[accessible_action]    # element has accessible actions
[accessible_text]      # element supports AccessibleText
[accessible_selection] # element supports accessible selection

Pseudo-classes

Selector Description
:scope Matches the reference element (used inside :has())
:not(selector) Inverse — elements not matching the inner selector (relative selectors not allowed)
:has(selector) Elements containing a matching descendant — supports relative selectors (>, +, ~) and bare :scope for flexible tree traversal
:nth-child(n) Match by 1-indexed position in parent
:nth-last-child(n) Match by 1-indexed position from end of parent
:require-state(s) Element has the given state name
:exclude-state(s) Element does not have the given state name
driver.locator("push_button:not([name='Cancel'])")
driver.locator("dialog:has(push_button[name='Confirm'])")
driver.locator("page_tab:nth-child(1)")  # first tab
driver.locator("push_button:require-state(enabled)") # enabled push button

Combinators

Separate compound selectors with a combinator to navigate the tree hierarchy.

Combinator Symbol Meaning
Child > Direct child
Descendant (space) Any descendant
Next sibling + Immediately following sibling
Subsequent sibling ~ Any following sibling
driver.locator("dialog > push_button")         # direct child
driver.locator("dialog push_button")           # any descendant
driver.locator("label + text_field")           # next sibling
driver.locator("label ~ text_field")           # any subsequent sibling

Relative selectors

A combinator at the start of a selector (before any compound) makes it a relative selector. Relative selectors are only valid inside :has() — they are rejected at the top level.

driver.locator("dialog:has(> push_button)")        # immediate child
driver.locator("dialog:has(+ label)")              # adjacent sibling
driver.locator("dialog:has(~ text_field)")         # any following sibling
driver.locator("dialog:has( push_button)")         # any descendant (space)

You can also use :scope explicitly inside :has() to refer to the context element:

driver.locator("dialog:has(:scope > push_button)")  # same as > push_button

Alternatives

Separate multiple selectors with , — an element matching any of the alternatives is returned.

driver.locator("push_button, check_box, radio_button")

Chaining via .locator()

Calling .locator() on an existing Locator concatenates the selectors with a space (the descendant combinator):

# Chaining (descendant combinator)
driver.locator("dialog").locator("push_button")

# Wildcard (matches any role)
driver.locator("*")
driver.locator("*[name='Clear']")

# Relative selectors inside :has()
driver.locator("dialog:has(> push_button)")  # immediate child
driver.locator("dialog:has(+ label)")        # adjacent sibling

This is useful for building queries step by step:

container = driver.locator("dialog")
btn = container.locator("push_button[name='Confirm']")