sveltejs/svelte

Allow slot content in custom elements when shadow root is disabled/false

patricknelson opened this issue · 0 comments

Describe the problem

Since slots are already supported when compiling to custom elements (via shadow DOM) and Svelte can now natively utilize the light DOM (thanks to #8457), one might naturally also want slot support when in the light DOM. This is not currently possible, but would be awesome if Svelte had a way of natively supporting slot content when not using a shadow root.

For example, given ExampleElement.svelte:

<svelte:options
	customElement={{
		tag: 'example-element',
		shadow: 'none',
	}}
/>

<h1>My Heading</h1>
<slot/>

And index.html containing:

<example-element>
	<p>Hello world!</p>
</example-element>

The final rendered version in the light DOM might look something like this:

<example-element>
	<h1>My Heading</h1>
	<p>Hello world!</p>
</example-element>

Note: This is a reopening/variant of #8686 where this was originally reported as a bug.

Describe the proposed solution

Taking inspiration from experimentation already done in svelte-retag, I'd propose two approaches, each of which are necessary depending on how the compiled component is shipped (e.g. esm or umd/iife)

  1. Module + UMD/IIFE: Parse out elements with named slot="..." attributes and populate into Svelte component slots. Use remaining content as "default" content for the default slot.
  2. UMD/IFFE only: Do the above, but also watch for changes using MutationObserver since the custom HTMLElement will connect early while the parser appends new nodes (i.e. slot content) as it moves across the DOM. Practically, this can be accomplished by temporarily (or permanently) isolating rendered component HTML to prevent recursion. svelte-retag accomplishes this by tucking that HTML into a no-op custom element called <svelte-retag> where we can be certain that rendered HTML lives in a separate hierarchy that will not be confused with newly appended child nodes as the parser progresses.

One might also want to ensure they account for changes to slot content beyond document.readyState === 'complete' (svelte-retag doesn't yet do this so I haven't tried it yet). But this is just one possible approach.

Alternatives considered

This concept is already readily demonstrated here in Svelte using light DOM custom elements via svelte-retag.

Importance

would make my life easier