Skip to content

fix(runtime): add addEventListener support for slot elements in scope components#6281

Merged
christian-bromann merged 2 commits intomainfrom
cb/slot-event-handler
Jun 10, 2025
Merged

fix(runtime): add addEventListener support for slot elements in scope components#6281
christian-bromann merged 2 commits intomainfrom
cb/slot-event-handler

Conversation

@christian-bromann
Copy link
Copy Markdown
Member

What is the current behavior?

GitHub Issue Number: #6269

When using onSlotchange event listeners on <slot> elements in scoped components, the application crashes with:

TypeError: el.addEventListener is not a function

This occurs because in scoped components, Stencil uses a slot polyfill that replaces <slot> elements with text/comment nodes (since native shadow DOM is not used). These nodes don't have native addEventListener methods, causing runtime errors when the framework attempts to attach event listeners.

What is the new behavior?

  • onSlotchange event listeners now work correctly on <slot> elements in scoped components
  • The slot polyfill now includes custom addEventListener, removeEventListener, and dispatchEvent methods for polyfilled slot nodes
  • Event listeners are attached without errors and function as expected
  • slotchange events can be dispatched and handled properly
  • The fix maintains backward compatibility and doesn't affect shadow DOM components

Technical Details:

  • Enhanced patchSlotNode() to add event listener polyfill methods to slot reference nodes
  • Fixed timing issue by patching slot nodes immediately after creation, before event listener attachment
  • Added defensive checks to prevent overwriting existing methods or double-patching

Documentation

No documentation changes required - this restores expected functionality for existing APIs.

Does this introduce a breaking change?

  • Yes
  • No

This is a bug fix that restores expected functionality. No migration is required.

Testing

Unit Tests Added:

  • ✅ Test that components with onSlotchange render without errors
  • ✅ Test that slotchange events are properly dispatched and handled
  • ✅ Test that event handlers can be called multiple times
  • ✅ Test that component state updates correctly when events fire
  • ✅ End-to-end verification of the event listener polyfill functionality

Manual Testing:

Test Command:

npm run test.jest -- src/runtime/test/scoped.spec.tsx

All tests pass with comprehensive coverage of the event listener polyfill.

Other information

Before this fix:

// This would crash with "addEventListener is not a function"
@Component({ tag: 'my-component', scoped: true })
class MyComponent {
  render() {
    return <slot onSlotchange={this.handleSlotChange}></slot>;
  }
}

After this fix:

// This now works correctly
@Component({ tag: 'my-component', scoped: true })  
class MyComponent {
  handleSlotChange = (event: Event) => {
    // Event handler executes properly
    console.log('Slot content changed!', event);
  };
  
  render() {
    return <slot onSlotchange={this.handleSlotChange}></slot>;
  }
}

Key Files Modified:

  • src/runtime/slot-polyfill-utils.ts - Added event listener polyfill
  • src/runtime/vdom/vdom-render.ts - Fixed timing of slot patching
  • src/runtime/test/scoped.spec.tsx - Added comprehensive tests

This fix ensures that scoped components have feature parity with shadow DOM components for slot event handling.

…d components

Fixes issue where onSlotchange event listeners would fail with "addEventListener is not a function"
error when used on <slot> elements in scoped components.

**Root Cause:**
In scoped components, slots are polyfilled using text/comment nodes since native shadow DOM
is not used. These nodes lack native addEventListener methods, causing runtime errors when
event listeners are attached.

**Solution:**
- Enhanced patchSlotNode() to add custom addEventListener, removeEventListener, and
  dispatchEvent methods to polyfilled slot nodes
- Fixed timing issue by calling patchSlotNode() immediately after slot reference node
  creation, before updateElement() attempts to attach event listeners
- Added defensive checks to avoid overwriting existing methods or double-patching nodes

**Changes:**
- src/runtime/slot-polyfill-utils.ts: Added event listener polyfill methods with proper
  TypeScript types
- src/runtime/vdom/vdom-render.ts: Moved slot patching to occur before event listener
  attachment
- src/runtime/test/scoped.spec.tsx: Added comprehensive tests verifying event listener
  functionality works end-to-end

**Testing:**
Added tests that verify:
- Event listeners can be attached without errors
- slotchange events are properly dispatched and handled
- Multiple events work correctly
- Component state updates as expected

Closes #6269
@christian-bromann christian-bromann changed the title fix(runtime): add addEventListener support for slot elements in scope… fix(runtime): add addEventListener support for slot elements in scope components Jun 9, 2025
@christian-bromann christian-bromann merged commit 32f66bd into main Jun 10, 2025
72 checks passed
@christian-bromann christian-bromann deleted the cb/slot-event-handler branch June 10, 2025 19:14
@johnjenkins
Copy link
Copy Markdown
Contributor

johnjenkins commented Jun 11, 2025

@christian-bromann I think this is just an issue with MockDoc as the native dom can happily attach / remove event listeners from text / comment nodes - which is how it works in the browser in the first place.

image

Perhaps this code can be moved to mock-doc

@christian-bromann
Copy link
Copy Markdown
Member Author

Great catch @johnjenkins , mind taking a look at #6287?

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.

bug: TypeError: el.addEventListener is not a function in Stencil Core

2 participants