Skip to content

Implement getElementById on Document and DocumentFragment#618

Closed
airhorns wants to merge 1 commit into
Shopify:mainfrom
airhorns:polyfill-get-element-by-id
Closed

Implement getElementById on Document and DocumentFragment#618
airhorns wants to merge 1 commit into
Shopify:mainfrom
airhorns:polyfill-get-element-by-id

Conversation

@airhorns

@airhorns airhorns commented Jul 15, 2026

Copy link
Copy Markdown

Problem

@remote-dom/polyfill gives ParentNode both querySelector() and querySelectorAll(), but the NonElementParentNode.getElementById() lookup is missing. Inside a remote environment, document.getElementById(id) throws:

TypeError: document.getElementById is not a function

This is a nasty failure mode for host code, because remote code is normally type-checked against lib.dom, which does declare getElementById. So the call is type-clean, passes every static check, and only blows up at runtime in the sandbox. We hit exactly this in Shopify's admin: a script handler in a remote view looked up an element by id to call an imperative method on it, typechecked green, and threw as soon as the handler ran.

Fix

Implement getElementById() on Document and DocumentFragment (the two NonElementParentNode hosts per the DOM spec — deliberately not on Element), delegating to a shared helper in shared.ts. This mirrors how the existing querySelector/querySelectorAll delegate to selectors.ts.

The helper returns the first element in tree order whose id attribute matches, walking the existing CHILD/NEXT links.

It matches the id attribute literally rather than delegating to querySelector('#' + id). An id is a string, not a selector, and the selector tokenizer in selectors.ts has no escape handling — so an id containing ., :, [, or whitespace would parse as a different selector entirely and silently return the wrong element (or nothing). getElementById('a.b') must find <div id="a.b">, not an <a> with class b.

The polyfill gave ParentNode querySelector and querySelectorAll, but the
NonElementParentNode.getElementById lookup was missing, so
document.getElementById(id) threw "document.getElementById is not a function"
inside a remote environment. Host code is typically type-checked against
lib.dom, which does declare the method, so the gap was invisible to every
static check and surfaced only at runtime in the browser.

Return the first element in tree order whose id attribute matches. Match that
attribute literally rather than delegating to querySelector('#' + id): an id is
a string, not a selector, and the selector tokenizer has no escape handling, so
an id containing '.', ':', '[', or whitespace would parse as a different
selector.

Co-Authored-By: Claude <noreply@anthropic.com>
@airhorns

Copy link
Copy Markdown
Author

closing in favour of #620

@airhorns airhorns closed this Jul 16, 2026
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.

1 participant