# How the `page.locator()` Method Works in Ego‑Lite: A Complete Guide to Element Selection

> Learn how page.locator() in ego-lite provides auto-waiting locators for reliable element interaction. Discover its helper methods and automatic error handling.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-07-31

---

**The `page.locator()` method in ego-lite returns a strict, auto‑waiting locator object that bundles helper methods for element interaction, forwarding calls to underlying Chrome DevTools Protocol (CDP) driver functions while automatically handling retries and transient errors.**

In the **citrolabs/ego-lite** browser automation framework, `page.locator()` serves as the primary entry point for finding and interacting with DOM elements. This method creates a robust abstraction over raw CDP queries, providing Playwright‑style stability through automatic waiting mechanisms implemented in [`src/driver/locator.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/locator.ts). Understanding how this locator system resolves selectors and manages element resolution is essential for writing reliable browser automation scripts.

## The Locator Factory in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)

According to the **citrolabs/ego-lite** source code, the implementation lives in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), where the `createLocator` factory function constructs the locator façade. When you invoke `page.locator()`, it instantiates an object that stores the selector string unchanged in its `selector` property and exposes a rich set of helper methods.

This façade pattern separates the high‑level API from the low‑level driver logic. Rather than executing CDP commands directly, methods like `click()`, `innerText()`, and `evaluate()` forward their calls to specialized driver functions:

```typescript
// Conceptual representation from src/helpers.ts
click: (options = {}) => pointer.click(selector, options),
innerText: () => locator.innerText(selector),
evaluate: (fn, arg) => locator.evaluateLocator(selector, fn, arg)

```

The actual element resolution and interaction logic resides in [`src/driver/locator.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/locator.ts), which handles both standard CSS/XPath queries and accessibility‑role queries via `queryRoleBackendNodeIds` in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts).

## Supported Selector Syntax

The `page.locator()` method accepts several selector forms, which are parsed and resolved according to their prefix or format:

- **CSS selectors**: Standard CSS syntax like `'button.submit'` or `'#nav > ul'`
- **Snapshot references (`@`)**: Direct node references like `'@21'` that resolve to specific backend node IDs
- **`loc=` shortcuts**: Prefixed shortcuts including CSS, XPath, text, and role queries (e.g., `'loc=css:.nav'`, `'loc=role:button[name="Submit"]'`)
- **XPath expressions**: Prefixed with `xpath=` (e.g., `'xpath=//div[@id="main"]'`)

When using role‑based selectors, [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) translates these into accessibility tree queries through the `queryRoleBackendNodeIds` function, enabling semantic element location independent of CSS structure.

## Auto‑Waiting and Retry Mechanics

A key feature of ego‑lite's locators is the **auto‑waiting** behavior. The driver functions `readElement` and `readOptionalElement` in [`src/driver/locator.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/locator.ts) implement retry loops that catch `ElementResolutionError` instances of kind `transient`.

This mechanism ensures that if an element is not immediately available in the DOM, the engine will retry the query until the element appears or the default timeout expires. This provides Playwright‑style stability without manual sleep statements, handling race conditions between page updates and element access automatically.

## Chaining and Narrowing Locators

Locators are immutable and composable. The façade exposes methods that return new locator instances with augmented selectors, enabling expressive query building:

- **`first()`**, **`last()`**, **`nth(index)`**: Narrow to specific elements in a collection using internal selectors like `internal:nth=2`
- **`locator(child)`**: Scope queries to descendants using `internal:scope:{base, child}` syntax
- **`filter(options)`**: Subset elements based on text content or other criteria
- **`getByRole()`**, **`getByText()`**: Semantic shortcuts that construct appropriate `loc=` selectors

Each chained method invokes `createLocator` with a transformed selector string, maintaining the separation between query construction and execution.

## Snapshot‑Aware References

When working with snapshot references (selectors starting with `@`), the locator system optimizes resolution. For example, the `count()` method short‑circuits the normal query process by resolving the handle directly, ensuring that snapshot references always resolve to exactly one element without unnecessary DOM traversal.

## Practical Usage Examples

The following examples demonstrate common patterns when working with `page.locator()` in ego‑lite:

```typescript
// Basic CSS locator – click the submit button
await page.locator('button[type=submit]').click();

// Locate by accessible role with an exact name, then fill a text input
await page
  .locator()
  .getByRole('textbox', { name: 'Email', exact: true })
  .fill('user@example.com');

// Chain selectors: first paragraph inside a specific container
const firstParagraph = await page
  .locator('#article')
  .locator('p')
  .first()
  .innerText();

// Use a snapshot reference (e.g., @21) to retrieve text content
const refText = await page.locator('@21').textContent();

// Filter a set of elements that contain specific text
await page
  .locator('.list-item')
  .filter({ hasText: 'Active' })
  .count();

```

## Summary

- **`page.locator()`** in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) creates a strict, auto‑waiting locator façade via the `createLocator` factory.
- The method accepts CSS selectors, snapshot references (`@`), `loc=` shortcuts, and XPath expressions, storing them unchanged in the `selector` property.
- Underlying driver functions in [`src/driver/locator.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/locator.ts) handle CDP queries and accessibility‑role resolution via [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts).
- **Auto‑waiting** is implemented through retry loops in `readElement` and `readOptionalElement` that catch transient `ElementResolutionError` instances.
- Locators support **method chaining** (`first()`, `nth()`, `filter()`, `locator()`) which returns new instances with augmented internal selectors.
- Snapshot references receive optimized handling where `count()` resolves handles directly rather than performing DOM queries.

## Frequently Asked Questions

### How does `page.locator()` handle elements that aren't immediately present in the DOM?

According to the source code in [`src/driver/locator.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/locator.ts), the method implements auto‑waiting through retry loops in functions like `readElement` and `readOptionalElement`. These functions catch `ElementResolutionError` instances marked as `transient` and retry the query until the element appears or the timeout expires, providing automatic synchronization without manual sleeps.

### What is the difference between `loc=role:` selectors and standard CSS selectors?

Standard CSS selectors query the DOM tree directly, while `loc=role:` selectors trigger accessibility tree queries via `queryRoleBackendNodeIds` in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts). This allows you to locate elements by their semantic role and accessible name rather than CSS attributes, making tests more resilient to layout changes.

### Can I chain `page.locator()` methods to narrow down element selection?

Yes, the locator façade exposes chainable methods like `first()`, `nth(index)`, `last()`, and `filter()` that each return a new locator instance created by `createLocator` in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts). These methods augment the selector string with internal prefixes (e.g., `internal:nth=2`), enabling composable queries that resolve to specific elements within a broader selection.

### Where is the `page.locator()` method signature documented?

The TypeScript signatures and `help()` API documentation for `page.locator` are defined in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts), which catalogs the method's various overloads and supported selector formats for IDE assistance and runtime help systems.