# How elementCenter Calculates Click Coordinates in Ego-Lite

> Learn how elementCenter calculates viewport-relative click coordinates in ego-lite. Discover the process of resolving selectors, scrolling, and finding the element's center.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-07-27

---

**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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/pointer.ts) at line 562, the runtime injects a script that centers the element within the viewport:

```javascript
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:

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

```

As implemented in [`src/driver/pointer.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts) (lines 425-437) and injected into the helper context via [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) at line 87. Here are practical usage patterns:

```javascript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts).
- **Visibility Guarantee**: Centers elements in viewport using `scrollIntoViewIfNeeded` or `scrollIntoView` (line 562 of [`src/driver/pointer.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/pointer.ts).
- **Public API**: Accessible via `page.elementCenter()`, injected through [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) and documented in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/pointer.ts) at lines 562 and 627, while the public API definition exists in [`src/driver/observe.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/observe.ts). The injection into the runtime context occurs in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) at line 87.