# How to Use Snapshot References (backendNodeId) for Element Interaction in Ego Lite

> Learn how Ego Lite uses backendNodeId snapshot references for fast element interaction, bypassing slow DOM queries for efficient automation.

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

---

**Ego Lite leverages numeric backendNodeId identifiers from Chrome’s Accessibility (AX) tree to create stable snapshot references (e.g., `@21`) that enable direct, high-performance element interaction without expensive DOM queries.**

The citrolabs/ego-lite browser automation framework captures DOM snapshots as lightweight numeric references tied to Chrome's internal node identifiers. This approach eliminates the fragility of CSS selectors by storing **backendNodeId** values—the same identifiers Chrome DevTools Protocol (CDP) uses to address elements directly—enabling faster, more stable automation scripts.

## Understanding Snapshot References and backendNodeId

When Ego Lite captures a page snapshot via `Accessibility.getFullAXTree`, it generates **snapshot references** that map human-readable strings like `@21` to Chrome's internal **backendNodeId** integers. These identifiers point directly to nodes in the browser's accessibility tree, providing a stable addressing mechanism that survives DOM mutations until the element is removed or recreated.

The reference lifecycle follows four distinct phases:

1. **Snapshot Capture** – The runtime invokes `Accessibility.getFullAXTree` to build the AX tree and extract `backendDOMNodeId` values from each node.
2. **Reference Storage** – The `RefMap` class in [`src/ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-map.ts) persists mappings between reference strings (e.g., `@21`), `backendNodeId` integers, element roles, and accessibility names.
3. **Direct Resolution** – When a helper receives a reference like `@21`, the `parseRef` function (lines 44-53 in [`src/ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-map.ts)) extracts the numeric identifier for lookup.
4. **Stale Recovery** – If the stored `backendNodeId` no longer exists, the system falls back to role/name-based lookup via `findBackendNodeIdByRoleName`.

## Resolving Elements from References

The [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) file contains the core logic for transforming snapshot references into actionable element handles. Two primary functions handle different interaction requirements:

**resolveElementCenter** (lines 63-104): Converts a reference into screen coordinates by first looking up the stored `backendNodeId`, then calling `DOM.getBoxModel` via CDP to calculate the element's center point. If the node is stale, it triggers `findBackendNodeIdByRoleName` to refresh the mapping.

**resolveElementObjectId** (lines 149-202): Returns a CDP runtime object ID required for event dispatch. This function queries the `RefMap` for the `backendNodeId`, then executes `DOM.resolveNode` to obtain a live object reference, falling back to accessibility tree traversal on failure.

Both functions rely on `parseRef` to normalize input formats including `@N`, `ref=N`, or plain numeric strings.

## Practical Implementation Examples

The following examples demonstrate how to interact with elements using snapshot references in Ego Lite scripts. These helpers are automatically injected into the runtime environment.

### Clicking Elements by Reference

Use the `click` helper to dispatch mouse events via stored backend node IDs:

```javascript
// Target element from previous snapshot with reference @21
const { click } = ego;
await click('@21');

```

Internally, `click` invokes `resolveElementObjectId`, which parses `@21` through `parseRef`, retrieves the entry from `RefMap`, and calls `DOM.resolveNode` with the stored `backendNodeId`. The resulting object ID is passed to `Input.dispatchMouseEvent` to execute the click.

### Retrieving Element Coordinates

Extract precise center coordinates for visual validation or custom event dispatch:

```javascript
const { getCenter } = ego;
const { x, y } = await getCenter('@42');
console.log(`Element center at (${x}, ${y})`);

```

This flows through `resolveElementCenter`, which uses `DOM.getBoxModel` on the stored `backendNodeId`, then calculates the center via `boxModelCenter` (lines 53-68 in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts)).

### Handling Stale References

When page navigation or DOM updates invalidate stored identifiers, force a fresh snapshot:

```javascript
// Reference @33 may be stale after previous actions
await ego.waitForRef('@33');  // Triggers new snapshot if needed
await ego.click('@33');       // Safe execution on fresh backendNodeId

```

The `waitForRef` method guarantees that `RefMap` contains a valid entry before proceeding, preventing errors from stale `backendNodeId` values.

### Manual Reference Management

For custom tooling scenarios, manually inject references into the map:

```javascript
// Directly register a backendNodeId with custom reference '99'
ego.refMap.add('99', 123456, 'button', 'Submit');
await ego.click('ref=99');

```

The `RefMap.add` method (lines 8-10 in [`src/ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-map.ts)) stores the mapping immediately, allowing subsequent helper calls to resolve the reference through standard CDP methods.

## Key Source Files in citrolabs/ego-lite

The snapshot reference system spans four critical files in the repository:

- **[`src/ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-map.ts)**: Implements the `RefMap` class and `parseRef` utility for reference string normalization and storage.
- **[`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts)**: Contains `resolveElementCenter`, `resolveElementObjectId`, and `findBackendNodeIdByRoleName` for CDP-based element resolution.
- **[`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)**: Exposes the public API (`click`, `getCenter`, `waitForRef`) that orchestrates resolution and interaction.
- **[`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts)**: Manages snapshot creation via `Accessibility.getFullAXTree` and populates the `RefMap` with fresh `backendNodeId` values.

## Summary

- **Snapshot references** in Ego Lite map strings like `@21` to Chrome's internal `backendNodeId` integers from the accessibility tree.
- The **RefMap** class ([`src/ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-map.ts)) persists these mappings, while **parseRef** normalizes input formats.
- **Element resolution** occurs through CDP methods `DOM.getBoxModel` and `DOM.resolveNode`, providing direct access without DOM querying.
- **Automatic fallback** to role/name-based lookup occurs when `backendNodeId` values become stale, ensuring robust test execution.
- References remain valid across multiple actions until the DOM node is recreated or removed, offering superior performance compared to CSS selectors.

## Frequently Asked Questions

### What is a backendNodeId in Chrome DevTools Protocol?

The **backendNodeId** is a numeric identifier assigned to each node in Chrome's internal accessibility (AX) tree representation. Unlike DOM IDs, these integers are stable for the lifetime of the node and allow direct addressing via CDP methods like `DOM.resolveNode` and `DOM.getBoxModel` without requiring CSS selector evaluation or JavaScript execution in the target page.

### How does Ego Lite handle stale snapshot references?

When a stored `backendNodeId` no longer resolves (typically due to DOM removal or re-creation), Ego Lite triggers **fallback resolution** via `findBackendNodeIdByRoleName` in [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) (lines 96-124). This function re-queries the full AX tree using `Accessibility.getFullAXTree`, then matches nodes by their accessibility role and name to locate the new `backendDOMNodeId`, effectively refreshing the reference mapping.

### What performance benefits do snapshot references provide?

Snapshot references eliminate the runtime overhead of CSS selector parsing, DOM traversal, and repeated accessibility tree queries during multi-step interactions. Because **backendNodeId** values provide direct handles to internal browser structures, subsequent actions like clicks and coordinate lookups execute via lightweight CDP calls rather than expensive DOM operations, significantly reducing latency in automation sequences.

### Can snapshot references persist across page navigations?

No, **backendNodeId** values are tied to specific document lifecycles and invalidate when navigating to new pages. Ego Lite provides the `waitForRef` helper to regenerate snapshots after navigation, ensuring fresh references for the new document context. The framework automatically detects stale identifiers and can refresh them using role/name matching when possible, though best practice involves re-capturing snapshots after significant DOM changes.