Skip to content

Forward focus and scrollIntoView to the host - #627

Open
airhorns wants to merge 2 commits into
Shopify:mainfrom
airhorns:polyfill-imperative-element-methods
Open

airhorns wants to merge 2 commits into
Shopify:mainfrom
airhorns:polyfill-imperative-element-methods

Conversation

@airhorns

Copy link
Copy Markdown
Contributor

What changed

focus() and scrollIntoView() are missing from polyfilled elements, even though Remote DOM already has a method-call channel and both operations can be performed by the rendered host element. Remote code type-checked against lib.dom therefore fails at runtime before it can use that channel.

This adds a general imperative-method hook to @remote-dom/polyfill, implements focus() and scrollIntoView() on the polyfill's shared Element abstraction, and wires the core polyfill hook to the connected RemoteConnection. Options are forwarded unchanged. Standalone or disconnected polyfilled elements keep browser-like no-op behavior when no host hook is available.

The core regression test connects a real RemoteRootElement and RemoteReceiver, registers the host implementation, and verifies both method calls end to end.

These methods live on the shared Element class because that is the current concrete class returned for ordinary and custom HTML elements on main. #626 separately proposes concrete HTML constructor identity.

getBoundingClientRect() is deliberately not included: it is synchronous and returns host layout data, which cannot be faithfully implemented over an asynchronous remote connection.

Validation

  • pnpm exec vitest run (178 tests)
  • pnpm lint
  • pnpm type-check
  • mise exec node@20.20.0 -- pnpm --filter @remote-dom/polyfill --filter @remote-dom/core build

@airhorns
airhorns force-pushed the polyfill-imperative-element-methods branch 2 times, most recently from a9eef6d to 11971c2 Compare July 28, 2026 14:19
Comment thread packages/polyfill/source/Element.ts Outdated
Comment on lines +79 to +83
if (arg === undefined) {
this.callHostMethod('scrollIntoView');
} else {
this.callHostMethod('scrollIntoView', arg);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (arg === undefined) {
this.callHostMethod('scrollIntoView');
} else {
this.callHostMethod('scrollIntoView', arg);
}
this[HOOKS].callMethod?.(this as any, 'scrollIntoView', arg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above — inlined via a rest tuple to preserve arity.

Comment thread packages/polyfill/source/Element.ts Outdated
Comment on lines +71 to +75
if (options === undefined) {
this.callHostMethod('focus');
} else {
this.callHostMethod('focus', options);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (options === undefined) {
this.callHostMethod('focus');
} else {
this.callHostMethod('focus', options);
}
this[HOOKS].callMethod?.(this as any, 'focus', arg);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied, with one tweak: the argument is spread through a rest tuple (focus(...args: [options?: FocusOptions])) so a bare element.focus() reaches the host as callMethod(el, 'focus') rather than callMethod(el, 'focus', undefined) — keeps the arity the host sees identical to what the caller wrote.

Comment thread packages/polyfill/source/Node.ts Outdated
Comment on lines +39 to +42
protected callHostMethod(method: string, ...args: unknown[]) {
this[HOOKS].callMethod?.(this as any, method, ...args);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes the method callable from userland. Callsites must just use this[HOOKS].callMethod?.(this as any, 'scrollIntoView', arg) directly (see other suggestion comments).

Suggested change
protected callHostMethod(method: string, ...args: unknown[]) {
this[HOOKS].callMethod?.(this as any, method, ...args);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 726ae5f — callHostMethod is gone and both call sites invoke this[HOOKS].callMethod?.(this as any, …) directly. Also rebased onto main to pick up the erasable-syntax import changes.

@airhorns
airhorns force-pushed the polyfill-imperative-element-methods branch from 11971c2 to 726ae5f Compare September 1, 2026 17:47

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants