How elementCenter Calculates Click Coordinates in Ego-Lite

The elementCenter helper determines viewport-relative click coordinates by resolving the target selector, ensuring visibility via scroll operations, and computing the geometric center from the element's bounding box dimensions.

The elementCenter helper is a core utility in the citrolabs/ego-lite browser automation framework that enables precise interaction with DOM elements. According to the source code in package/ego-browser/src/driver/observe.ts, this function implements a three-stage pipeline to convert abstract selectors into concrete {x, y} coordinates suitable for Chrome DevTools Protocol (CDP) based pointer events.

Three-Stage Coordinate Resolution

The helper operates through a coordinated sequence defined across multiple source files, ensuring accurate positioning regardless of viewport state or selector type.

Element Resolution via element-resolver.ts

First, elementCenter resolves the input selector—which may be CSS, XPath, @ref, or loc=—to a concrete DOM node handle. This occurs through the shared element-resolver logic in src/element-resolver.ts. The resolver yields a JavaScript handle compatible with CDP evaluation, abstracting away the differences between selector syntaxes before any geometric calculations occur.

Viewport Alignment with scrollIntoView

Before measuring geometry, the helper ensures the target is visible. In src/driver/pointer.ts at line 562, the runtime injects a script that centers the element within the viewport:

if (typeof this.scrollIntoViewIfNeeded === 'function') {
    this.scrollIntoViewIfNeeded(true);
} else {
    this.scrollIntoView({ block: 'center', inline: 'center' });
}

This guarantees that subsequent coordinate calculations reference visible, viewport-relative positions rather than off-screen locations.

Geometric Center Computation

After scrolling, the helper executes a CDP-eval script (buildSelectorCenterJs) to retrieve the element’s bounding box properties: top, left, width, and height. The center point is computed using standard geometric formulas:

const cx = left + width / 2;
const cy = top + height / 2;

As implemented in src/driver/pointer.ts at line 627, these calculations run asynchronously via Promise.all, performing the top-left corner and center-point extractions in parallel to minimize latency.

CDP Integration and Async Execution

The elementCenter function leverages the Chrome DevTools Protocol to bridge JavaScript evaluation with browser automation. By utilizing CDP script injection for both the scroll operation and the bounding box extraction, the helper maintains precision across navigation events and dynamic page changes. The parallel execution model at line 627 of src/driver/pointer.ts ensures efficient coordination between element resolution and coordinate calculation.

Using elementCenter in Your Code

The public API is exposed as page.elementCenter(selector), documented in src/format.ts (lines 425-437) and injected into the helper context via src/helpers.ts at line 87. Here are practical usage patterns:

// Get centre coordinates of a CSS selector
const centre = await page.elementCenter('#submit-button');
console.log(centre);   // => { x: 342.5, y: 218.0 }

// Direct click usage (internally calls elementCenter)
await page.click('#submit-button');

// Resolve reference-style selectors (e.g., @12)
const refCentre = await page.elementCenter('@12');
await page.click({ selector: '@12' });

Summary

  • Selector Resolution: Converts CSS, XPath, @ref, and loc= selectors to DOM handles via src/element-resolver.ts.
  • Visibility Guarantee: Centers elements in viewport using scrollIntoViewIfNeeded or scrollIntoView (line 562 of src/driver/pointer.ts).
  • Geometric Calculation: Computes center as left + width/2 and top + height/2 from bounding box data.
  • Parallel Execution: Resolves and measures coordinates concurrently using Promise.all at line 627 of src/driver/pointer.ts.
  • Public API: Accessible via page.elementCenter(), injected through src/helpers.ts and documented in src/format.ts.

Frequently Asked Questions

What selector types does elementCenter support?

The helper supports CSS selectors, XPath expressions, reference-style selectors prefixed with @ (e.g., @12), and location-based selectors using the loc= syntax. All types route through the shared resolver in src/element-resolver.ts before coordinate calculation begins.

Why does elementCenter scroll the element before calculating coordinates?

Scrolling ensures the element is within the visible viewport before measurement. This prevents coordinate calculations based on off-screen positions that would result in incorrect click locations or pointer events failing to hit the target element.

How does elementCenter handle elements with complex CSS transforms?

The helper relies on the browser's native getBoundingClientRect() via CDP evaluation to retrieve the final layout geometry. This returns the bounding box after all CSS transforms are applied, ensuring the calculated center point reflects the actual rendered position on screen.

Where is the coordinate calculation logic located in the source code?

The core calculation logic resides in src/driver/pointer.ts at lines 562 and 627, while the public API definition exists in src/driver/observe.ts. The injection into the runtime context occurs in src/helpers.ts at line 87.

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 →