Skip to content

fix: element removed from the DOM when directly under the shadow root#422

Merged
jamesnw merged 5 commits into
oddbird:mainfrom
jpzwarte:fix/null-parentelement-within-shadowroot
Jun 29, 2026
Merged

fix: element removed from the DOM when directly under the shadow root#422
jamesnw merged 5 commits into
oddbird:mainfrom
jpzwarte:fix/null-parentelement-within-shadowroot

Conversation

@jpzwarte

@jpzwarte jpzwarte commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Problem

When a position-area target element sits directly inside a shadow root, wrapperForPositionedElement failed to wrap it and instead removed it from the DOM entirely.

The wrapping logic was:

targetEl.parentElement?.insertBefore(wrapperEl, targetEl);
wrapperEl.appendChild(targetEl);

A ShadowRoot is a Node, not an Element, so targetEl.parentElement is null when the target is a direct child of the shadow root. The optional chaining short-circuited the insertBefore, but the following wrapperEl.appendChild(targetEl) still ran — moving the target into a wrapper that was never inserted into the tree. The result: the target disappeared from the rendered DOM.

Fix

Insert the wrapper relative to the target itself rather than going through its parent:

targetEl.insertAdjacentElement('beforebegin', wrapperEl);
wrapperEl.appendChild(targetEl);

insertAdjacentElement('beforebegin', …) places the wrapper just before the target without referencing the parent node, so it works whether the parent is a regular element or a shadow root.

Second fix: position-area mapping styles not reaching shadow roots

Even once wrapped, the target wasn't positioned: the generated position-area mapping stylesheet (which defines the --pa-value-* custom properties on the wrapper) was always appended to document.head, and a <style> there doesn't apply inside a shadow root. So --pa-value-* stayed empty and the wrapper collapsed to its static position.

transformCSS now inserts these polyfill-created styles into each polyfilled root via a new getRootStyleContainer helper (shadow root → that shadow root; document → document.head). The wrapper now resolves its insets and alignment correctly inside a shadow root.

Test page

Updated shadow-dom.html to reproduce the scenario: the target/anchor now live directly under the shadow root (no intermediate position: relative wrapper) and use position-area syntax, which exercises the wrapping code path. The adopted-stylesheets e2e test now asserts against the <polyfill-position-area> wrapper (right edges aligned, target top at anchor bottom).

@netlify

netlify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Deploy Preview for anchor-polyfill ready!

Name Link
🔨 Latest commit fee8993
🔍 Latest deploy log https://app.netlify.com/projects/anchor-polyfill/deploys/6a42946163c379000884ea6c
😎 Deploy Preview https://deploy-preview-422--anchor-polyfill.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Deploy Preview for anchor-position-wpt canceled.

Name Link
🔨 Latest commit fee8993
🔍 Latest deploy log https://app.netlify.com/projects/anchor-position-wpt/deploys/6a4294617722d900081b91ad

@jpzwarte jpzwarte marked this pull request as ready for review June 26, 2026 12:49

@jamesnw jamesnw left a comment

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 looks good! Thanks! Can you fix the tests?

@jpzwarte

Copy link
Copy Markdown
Contributor Author

This looks good! Thanks! Can you fix the tests?

I would like to, but NODE_OPTIONS='--no-experimental-strip-types' playwright test tests/e2e/ hangs. Nothing happens locally. Using node 24.16.0. If i remove the node options, it works, but gives an enum error.

@jamesnw

jamesnw commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

This looks good! Thanks! Can you fix the tests?

I would like to, but NODE_OPTIONS='--no-experimental-strip-types' playwright test tests/e2e/ hangs. Nothing happens locally. Using node 24.16.0. If i remove the node options, it works, but gives an enum error.

There's a specific Node version (v24.11) that works for the pinned version of Playwright that supports polyfillabel browsers. I added a note to CONTRIBUTING.md about running nvm use. That should get tests working locally.

@jpzwarte

Copy link
Copy Markdown
Contributor Author

That should get tests working locally.

Thanks, i fixed the test. And another bug related to shadow-root + position-area.

@jamesnw jamesnw left a comment

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.

LGTM- one idea you can take or leave. Good catch on the document.createElement bug- I'm wondering if you have looked at the other places that's used? That could be handled in a separate PR, though.

Comment thread src/transform.ts Outdated
Co-authored-by: James Stuckey Weber <james@oddbird.net>
@jpzwarte

Copy link
Copy Markdown
Contributor Author

I'm wondering if you have looked at the other places that's used? That could be handled in a separate PR, though.

I haven't; i noticed this one because position-area inside the shadow root was broken. Let's look at it in a separate PR.

@jamesnw jamesnw merged commit f390dcb into oddbird:main Jun 29, 2026
10 checks passed
@jpzwarte jpzwarte deleted the fix/null-parentelement-within-shadowroot branch June 30, 2026 17:25
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.

2 participants