Ego-Lite Element Resolver Selector Formats: CSS, XPath, ARIA, and Text Syntax

Ego-Lite's element resolver supports six distinct selector formats including raw CSS, XPath expressions, ARIA role queries, text-based lookups, hyperlink references, and numeric snapshot refs, all parsed in element-resolver.ts and resolved via Chrome DevTools Protocol.

The element-resolver.ts module in the citrolabs/ego-lite repository serves as the central hub for DOM element location, transforming declarative selector strings into concrete CDP (Chrome DevTools Protocol) commands. Understanding the full range of ego-lite element resolver selector formats enables developers to write stable, resilient browser automation scripts that withstand DOM structural changes.

Snapshot References (@)

The resolver handles persistent element references through the parseRef function (lines 70-104). When a selector starts with @, the parser extracts the numeric snapshot identifier and queries the internal ref-map (managed in ref-map.ts).

This mechanism stores the backendNodeId from previous DOM snapshots, allowing the resolver to:

  • Locate elements across page navigations using stable backend node identifiers
  • Resolve the correct CDP session when elements exist inside iframes
  • Fall back to role/name lookups if the backend node ID fails
// Click using a snapshot reference from a previous action
await click('@12');

Locator Parsing Syntax

For non-reference selectors, parseLocator (lines 147-188) parses strings into structured locator objects. The parser recognizes specific prefixes that determine the resolution strategy.

CSS Selectors

The css: prefix denotes standard CSS selectors. If no prefix is provided, the resolver falls back to CSS parsing (lines 212-217), optionally accepting an nth index for positional selection.

// Explicit CSS prefix
await click('loc=css:.submit-button');

// Implicit CSS (equivalent behavior)
await click('.submit-button');

// CSS with positional index (zero-based)
await click('loc=css:.item;nth=2');

XPath Expressions

XPath selectors use the xpath: prefix and are evaluated via document.evaluate in the page context. This supports complex traversal logic unavailable in standard CSS selectors.

await click('loc=xpath://div[@class="container"]//button[contains(text(), "Submit")]');

ARIA Role Selectors

The role: prefix triggers accessibility tree queries via Accessibility.getFullAXTree. These selectors support optional name= filters and nth indices (lines 201-211), providing resilient selection based on semantic roles rather than DOM structure.

// Basic role selection
await click('role:button');

// Role with accessible name filter
await click('role:button[name="Close"]');

// nth occurrence of a role
await click('role:button;nth=1');

Text-Based Selectors

Text matching uses text: or text= prefixes, with an optional exact: modifier for precise string matching. The resolver generates JavaScript evaluation code via buildLocatorAllJs (lines 610-632) and filters results through textMatchJs (line 696).

// Fuzzy text match
await click('text=Accept Terms');

// Exact text match
await click('text=exact:Submit');

// nth text occurrence (zero-based index)
await click('text=Next;nth=2');

Attribute Selectors

Several aliases exist for common text-like attributes, all resolving to the generic text-matching kind:

  • label: – Accessible label or associated label text
  • placeholder: – Input placeholder attributes
  • alt: – Image alternative text
  • title: – Element title attributes
  • testid: – data-testid attributes
await click('label:Username');
await click('testid:login-button');

The href: prefix filters anchor elements by URL path or full URL, implemented in hrefElementsJs (line 997). This matches against the pathname or complete href attribute.

await click('href:/settings/account');
await click('href:https://example.com/dashboard');

Resolution Implementation Strategies

After parsing, the resolver dispatches to specialized resolution functions based on the locator kind.

Accessibility Tree Resolution

For role-based locators, findBackendNodeIdByRoleName (starting at line 496) queries Chrome's accessibility tree. This approach remains stable even when DOM classes or structure change, as it relies on semantic ARIA information rather than markup details.

DOM Evaluation

CSS, XPath, and href selectors execute in the page context via Runtime.evaluate CDP commands:

  • CSS uses queryAllExpression (defined in locator-query.ts) with normalized selectors
  • XPath executes through document.evaluate
  • Href selectors filter <a> elements by path matching

Center and Object ID Extraction

Two primary resolution functions provide different output formats:

  • resolveElementCenter (lines 63-146) returns {x, y, sessionId} coordinates using DOM.getBoxModel or getBoundingClientRect()
  • resolveElementObjectId (lines 149-238) returns the CDP objectId via DOM.resolveNode for direct element manipulation

Error Handling

Resolution failures wrap in ElementResolutionError with classification via matchCountKind (lines 46-50):

  • Transient errors: Retryable conditions (e.g., element not yet attached)
  • Permanent errors: Non-recoverable failures (e.g., selector matches zero elements after timeout)

Summary

  • Six syntax families: Snapshot refs, CSS, XPath, ARIA roles, text/attributes, and href selectors
  • Central parsing: parseLocator in element-resolver.ts handles all string-to-object conversion
  • Dual resolution paths: Accessibility tree queries for roles, DOM evaluation for CSS/XPath
  • iframe support: Snapshot references (@N) resolve correct CDP sessions across frame boundaries
  • Stable selectors: ARIA role-based queries resist DOM structural changes compared to CSS selectors

Frequently Asked Questions

What is the difference between text: and text= prefixes?

Both prefixes invoke the same text-matching logic in element-resolver.ts. The colon and equals sign are interchangeable for text selectors, though text= often appears in documentation examples while text: maintains consistency with other prefix-based selectors like css: or xpath:.

How does the resolver handle elements inside iframes?

When using snapshot references (@N), parseRef resolves the correct CDP session including iframe context before attempting backend node ID lookup. For standard locators, the resolver evaluates JavaScript in the appropriate execution context, though explicit frame targeting requires separate page context management.

What happens when a selector matches multiple elements?

The resolver applies an nth index parameter (zero-based) to select specific occurrences. Without an explicit nth value, functions like resolveElementCenter typically return the first match or throw a transient error indicating multiple matches, depending on the calling helper's configuration.

Can I combine multiple selector types in one query?

While the base syntax does not support compound selectors (e.g., combining CSS and text), the ARIA role selector accepts a name= filter that effectively combines role and text criteria. For complex combinations, chain multiple actions or use XPath expressions that can incorporate both structural and text conditions in a single query.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →