SVG elements not displaying when passed as slot content
Describe the bug
If you have an SVG component that allows you to pass in child content as a slot like so...
<svg width="150" height="50">
<slot></slot>
</svg>
...no content will display if you pass in content wrapped in an anchor tag like so...
<script>
import Svg from './Svg.svelte';
</script>
<Svg>
<a href="https://example.com">
<path d="M0,0L50,50Z" stroke="#000" stroke-width="2"></path>
</a>
</Svg>
However, if you don't use an SVG component and simply write plain SVG, everything displays fine.
<svg width="150" height="50">
<a href="https://example.com">
<path d="M0,0L50,50Z" stroke="#000" stroke-width="2"></path>
</a>
</svg>
It seems that something is breaking when the child content gets sent through the slot.
Reproduction
https://svelte.dev/repl/08270639a2154ea18192cbdf30861da8?version=3.49.0
Logs
No response
System Info
REPL
Severity
blocking all usage of svelte
Very similar to https://github.com/sveltejs/svelte/issues/7450 and https://github.com/sveltejs/svelte/issues/7563 (basically a duplicate I would say)
Array.from(document.querySelectorAll('a')).map(e => e.namespaceURI)
0: "http://www.w3.org/2000/svg" <=============
1: "http://www.w3.org/1999/xhtml"
If you can't add <svelte:options namespace="svg"> into all nested components you can use this action :
<script lang="ts">
/** Ensure all "a" elements inside an SVG node belong to the correct namespace */
function ensureSVGA(node: SVGSVGElement) {
const namespaceSVG = 'http://www.w3.org/2000/svg'
const links = node.querySelectorAll<HTMLLinkElement>('a')
for (const link of links) {
if (link.namespaceURI === namespaceSVG) continue
const a = document.createElementNS(namespaceSVG, 'a')
for (const { name, value } of link.attributes) {
a.setAttribute(name, value)
}
a.append(...link.children)
link.insertAdjacentElement('beforebegin', a)
link.remove()
}
}
</script>
<svg use:ensureSVGA>
<slot />
</svg>
I've found a similar problem when loading SVG defs via a svelte:fragment when the svg element is also a slot and the parent element is an HTML div.
Here's the repro: https://svelte.dev/repl/2647caa253d648428dfd96b8d1d6b974?version=3.55.0
If you comment out the main wrapper div, svelte is able to detect the SVG namspace and the gradient shows up. If you wrap the whole thing in a div, it will interpret <linearGradient> an HTML tag. I tried adding <svelte:options namespace="svg"/> to the Svg.svelte component but that has no effect.
@mhkeller can you <svelte:options namespace="svg"/> in nested component? https://svelte.dev/repl/822371299ddf401885091c66715c66f7?version=3.55.0
Yes that it as a viable workaround for now – similar to adding the xmlns attribute inline to the linearGradient tag. It would still be nice if Svelte detected that the children of the defs object were svg elements, though.