What Locator Strategies Does ego-browser Support? A Complete Guide to Element Selection

ego-browser supports 11 distinct locator strategies including raw CSS selectors, XPath, ARIA roles, text matching, and specialized attribute-based locators like test-id, href, and placeholder, all parsed in src/locator-query.ts.

The ego-browser package from the citrolabs/ego-lite repository provides a flexible locator language that lets automation agents address page elements using multiple resolution strategies. Unlike standard browser automation tools that rely solely on CSS or XPath, ego-browser implements a hybrid resolver in src/locator-query.ts that supports semantic accessibility attributes, content-based matching, and internal filtering mechanisms.

CSS and XPath Strategies

The resolver handles standard browser query languages alongside ego-browser's custom prefixes.

Raw CSS Selectors

Any plain CSS selector (e.g., #login button) falls back to querySelectorAll on the document or a supplied root element. This provides full compatibility with standard CSS selector syntax without requiring a prefix.

Explicit CSS

Use the prefix loc=css: to explicitly declare a CSS selector. While functionally identical to raw CSS, this format clarifies intent and integrates with ego-browser's uniform locator syntax.

XPath Expressions

Prefix selectors with xpath= to execute document.evaluate against the DOM. This strategy returns matching nodes via native XPath evaluation, supporting complex traversal queries that exceed CSS selector capabilities.

Attribute-Based Locators

ego-browser provides targeted strategies for matching specific HTML attributes commonly used in testing and accessibility.

Test-ID

The loc=testid: prefix matches elements that have a data-testid attribute equal to the locator value. This is the preferred strategy for resilient test automation that avoids dependence on CSS structure.

Href

Use loc=href: to find <a> elements whose href attribute matches the given path or full URL. This enables link resolution without relying on link text or CSS classes.

Alt and Title

  • loc=alt: matches <img> or <input> elements by their alt attribute.
  • loc=title: resolves any element with a matching title attribute.

Placeholder

The loc=placeholder: prefix targets <input> or <textarea> elements whose placeholder attribute matches the supplied string, useful for identifying form fields by their hint text.

Content and Label Matching

These strategies locate elements based on their visible content or associated labels rather than structural attributes.

Text Matching

Use loc:text: or the shorthand text= to find elements whose visible text contains or exactly matches the supplied string. The resolver performs text normalization to ensure reliable matching across different DOM structures.

Label Association

The loc=label: prefix targets form elements associated with a <label> whose text matches the locator. This resolves the labeled element rather than the label itself, supporting semantic form interaction.

ARIA Role Locators

The role: prefix enables resolution by accessibility semantics. It matches elements by their explicit role attribute or by their implicit DOM role (e.g., <button> automatically resolves as button).

You can optionally filter by accessible name using bracket syntax:

await click('role:button[name=Submit]');

This resolves specifically to buttons with the accessible name "Submit", combining role semantics with content filtering.

Advanced Internal Locators

For complex automation scenarios, ego-browser exposes internal composition mechanisms.

Scope and Filter

The internal:scope: and internal:filter: prefixes allow composable queries that restrict searches to specific DOM subtrees or apply "has-text", "has", and "has-not" constraints. These are typically consumed by higher-level helpers rather than end-user scripts. For example:

await click('internal:scope:{"base":"#main","child":"button.save"}');

This restricts the search for button.save to within the #main container.

Resolver Priority and Implementation

The resolution engine in src/locator-query.ts (lines 47-118) evaluates locator strings through a strict precedence chain:

  1. Internal helpers (internal: prefixes) are checked first.
  2. XPath selectors (starting with xpath=) trigger document.evaluate.
  3. Explicit prefixes (css:, href:, text:, label:, placeholder:, alt:, title:, testid:) generate specialized expressions after stripping the loc= wrapper.
  4. ARIA roles matching the role: grammar trigger role-based resolution.
  5. Fallback to plain CSS querySelectorAll for any unmatched string.

All strategies ultimately produce a JavaScript expression that returns an array of matching DOM elements.

Practical Code Examples

Agents interact with these locators through helper functions defined in src/helpers.ts (click, type, upload, etc.), which forward selector strings to the underlying resolver:

// Click a button using a plain CSS selector
await click('#login button');

// Click a link by its exact href path
await click('loc=href:/settings/account');

// Type into an input identified by its placeholder text
await type('loc=placeholder:Email', 'alice@example.com');

// Click a menu item whose visible text contains "Dashboard"
await click('loc:text:Dashboard');

// Click a button by its ARIA role and name
await click('role:button[name=Submit]');

// Upload a file by selecting an <input type="file> using its data-testid
await upload('loc=testid:profile-photo-upload', './photo.png');

Key Source Files

The locator system spans four primary files in the citrolabs/ego-lite repository:

  • src/locator-query.ts: Central parser that converts locator strings into DOM-query expressions (see lines 47-118 for the strategy routing logic).
  • src/helpers.ts: Public API surface (click, type, upload) that accepts locator strings and delegates to the resolver.
  • src/element-resolver.ts: Calls queryAllExpression and translates returned element arrays into usable handles for further actions.
  • src/driver/locator.ts: Low-level driver that receives selectors, validates them, and routes them to the resolver.

Summary

  • ego-browser supports 11 distinct locator strategies ranging from standard CSS/XPath to semantic attributes, content matching, and internal composition mechanisms.
  • The resolver in src/locator-query.ts prioritizes internal helpers first, then XPath, explicit prefixes, ARIA roles, and finally raw CSS selectors.
  • ARIA role locators support both implicit roles (derived from HTML semantics) and explicit role attributes, with optional accessible name filtering.
  • Content-based locators (text, label, placeholder) enable resilient element selection based on user-visible content rather than implementation details.
  • Internal scope/filter locators provide advanced composability for restricting searches to subtrees and chaining multiple constraints.

Frequently Asked Questions

Does ego-browser support Playwright-style selectors?

Yes. The resolver supports Playwright-style "has-text" shortcuts (e.g., selector:has-text("...")) which are internally transformed into text filters. Additionally, standard CSS and XPath selectors are fully compatible with the system.

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

Both prefixes trigger the same text-matching resolver in src/locator-query.ts. The loc=text: format follows the explicit locator prefix convention used by other attribute strategies, while text= provides a shorthand syntax. Both locate elements whose normalized visible text contains or exactly matches the supplied string.

How does the ARIA role locator handle implicit roles?

The role: prefix checks both explicit role attributes and implicit DOM semantics according to the HTML accessibility mapping specification. For example, an <input type="checkbox"> without an explicit role attribute still resolves when using role:checkbox because the resolver recognizes its implicit ARIA semantics.

Can I combine multiple locator strategies?

Yes, through the internal:scope: and internal:filter: prefixes. These advanced locators enable composable queries that can restrict a search to a specific DOM subtree (scope) or apply constraints like "has-text", "has", or "has-not". These are primarily used internally by higher-level helpers but are available for complex automation scenarios requiring chained conditions.

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 →