Skip to content

Fix InputText binding with Shadow DOM elements#65629

Open
ilonatommy wants to merge 2 commits intodotnet:mainfrom
ilonatommy:fix/shadow-dom-input-binding-60885
Open

Fix InputText binding with Shadow DOM elements#65629
ilonatommy wants to merge 2 commits intodotnet:mainfrom
ilonatommy:fix/shadow-dom-input-binding-60885

Conversation

@ilonatommy
Copy link
Copy Markdown
Member

@ilonatommy ilonatommy commented Mar 3, 2026

🤖 AI Summary

📋 Automated Fix Report
🔍 Pre-Flight — Context & Validation

Issue: #60885 - [Blazor] InputText binding is not supported well with Shadow DOM elements
Area: area-blazor (src/Components/)

Key Findings

  • When events originate from within a Shadow DOM, event.target points to the shadow host element, not the actual <input> inside the shadow root
  • This causes EventFieldInfo.fromEvent and parseChangeEvent to fail to identify the input element, breaking two-way binding
  • The fix is to use event.composedPath()[0] instead of event.target when event.composed === true
  • Team member @javiercn approved this approach: "If we can scope a change that detects we are inside a shadow DOM and go through the alternative path (composedPath as opposed to event.target) I think we would be fine with that"
  • Reporter @Poppyto provided a working reference implementation at commit b3a2b64

Fix Candidates

# Source Approach Files Changed Notes
1 @Poppyto + @javiercn Use composedPath()[0] when event.composed === true EventFieldInfo.ts, EventTypes.ts Team-approved, minimal change

🧪 Test — Bug Reproduction

Test Result: ✅ TESTS CREATED

Test Type: E2E
Test Type Rationale: Bug is in JavaScript event handling within Shadow DOM — requires browser to reproduce. Team (@javiercn) specifically requested E2E tests.

Tests Written

  • InputEvent_ShadowDom_RespondsOnKeystrokes — Verifies that typing into an input inside a Shadow DOM web component correctly updates Blazor's bound value via oninput event

Test Assets Created

  • BasicTestApp/wwwroot/js/shadowDomInput.js — Web component <shadow-dom-input> wrapping <input> in Shadow DOM
  • BasicTestApp/ShadowDomBindingComponent.razor — Razor component using <shadow-dom-input> with @bind:event="oninput"
  • Registered JS file in index.html and _ServerHost.cshtml
  • Registered component in Index.razor

🚦 Gate — Test Verification & Regression

Gate Result: ⚠️ BLOCKED (pre-existing build errors)

  • TypeScript build (npm run build): ✅ PASSED
  • C# E2E test build: ⚠️ BLOCKED by pre-existing Selenium dependency errors on main (unrelated to this PR)
  • Test will be validated on CI where the full build environment is available

🔧 Fix — Analysis & Comparison

Fix Exploration Summary

Total Attempts: 5 + 2 cross-pollination rounds
Selected Fix: Inline ternary event.composed ? event.composedPath()[0] : event.target

Attempt Results

# Model Approach Result Key Insight
1 claude-sonnet-4.6 Shared getEventTarget() utility function Better DRY, but adds a new file for 1 line of logic
2 claude-opus-4.6 Override event.target via Object.defineProperty Elegant but invasive — modifying native event properties is risky
3 gpt-5.2 Drill into shadowRoot.activeElement recursively Works but activeElement may not match the event source
4 gpt-5.3-codex Scan composedPath() for first bindable form control More robust but overengineered for this case
5 gemini-3-pro-preview Host-first value resolution via duck-typing Respects encapsulation but requires custom elements to mirror values

Comparison

Criterion Fix 1 (Inline) Fix 2 (Utility) Fix 3 (Override) Fix 4 (activeElement) Fix 5 (Scan)
Correctness ⚠️
Simplicity ✅ 2 lines ➖ new file ➖ invasive ➖ complex ➖ complex
Team guidance ✅ matches
Backward compat ⚠️

Recommendation

Fix 1 is the best choice: minimal (2 lines), team-approved, standard composedPath() API, no regression risk.

ilonatommy and others added 2 commits February 4, 2026 16:47
When events originate from within a Shadow DOM, event.target points to the
shadow host element rather than the actual input element inside the shadow
root. This causes EventFieldInfo.fromEvent and parseChangeEvent to fail to
identify the input element, breaking Blazor's two-way binding.

Use event.composedPath()[0] instead of event.target when the event is
composed (event.composed === true) to resolve the actual originating element
from within the Shadow DOM.

- EventFieldInfo.ts: Use composedPath()[0] for composed events in fromEvent
- EventTypes.ts: Use composedPath()[0] for composed events in parseChangeEvent
- E2E test: Add InputEvent_ShadowDom_RespondsOnKeystrokes test with Shadow DOM web component

Fixes dotnet#60885

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ilonatommy ilonatommy requested a review from a team as a code owner March 3, 2026 13:08
Copilot AI review requested due to automatic review settings March 3, 2026 13:08
@github-actions github-actions bot added the area-blazor Includes: Blazor, Razor Components label Mar 3, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime.
To make sure no conflicting changes have occurred, please rerun validation before merging. You can do this by leaving an /azp run comment here (requires commit rights), or by simply closing and reopening.

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants