How the `page.locator()` Method Works in Ego‑Lite: A Complete Guide to Element Selection
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. 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
According to the citrolabs/ego-lite source code, the implementation lives in 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:
// 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, which handles both standard CSS/XPath queries and accessibility‑role queries via queryRoleBackendNodeIds in 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 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 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 likeinternal:nth=2locator(child): Scope queries to descendants usinginternal:scope:{base, child}syntaxfilter(options): Subset elements based on text content or other criteriagetByRole(),getByText(): Semantic shortcuts that construct appropriateloc=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:
// 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()insrc/helpers.tscreates a strict, auto‑waiting locator façade via thecreateLocatorfactory.- The method accepts CSS selectors, snapshot references (
@),loc=shortcuts, and XPath expressions, storing them unchanged in theselectorproperty. - Underlying driver functions in
src/driver/locator.tshandle CDP queries and accessibility‑role resolution viasrc/element-resolver.ts. - Auto‑waiting is implemented through retry loops in
readElementandreadOptionalElementthat catch transientElementResolutionErrorinstances. - 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, 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. 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. 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, which catalogs the method's various overloads and supported selector formats for IDE assistance and runtime help systems.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →