Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LitElement SVG children as slot

I am trying to use Lit-Element with a single slot wrapped by a SVG element.

But it seems that the <slot> located in the <svg> does not accept render the given SVG elements.

What does not work in a custom component:

render() {
  return html`
  <svg>
    <slot><circle id="defaultCircle" cx=... cy=...></circle></slot>
  </svg>`
}

Here is an example: https://stackblitz.com/edit/wy6bhj?file=index.html

Any idea why ? Any alternative ?

2019-02-18 COMMENTS

Justin Fagnani recommended to use a <foreignObject> to mix the HTML (that is the slot) with the SVG. Unfortunately, this doesn't work because in the slot it still SVG elements.

2019-02-19 UPDATE

Using the JavaScript expressing inside the render() function, I am trying to use this.children to iterate and add the children in the template. Now, using the inspector, it appears correctly in the DOM but nothing is rendered by the SVG element. I am lost and can't understand why the circle are not rendered.

https://stackblitz.com/edit/wy6bhj-hne7wl?file=my-element.js

2019-02-19 UPADTE2

I finally understood that there is no way I can do it the way a initially wanted. I opted to passe the SVG container as is.

<my-element>
  <svg> ... </svg>
</my-element>

Then the <my-element> uses

const SVG_CONTAINER = this.children[0]
const NODES = SVG_CONTAINERS.children

To compute things. And voilà!

like image 821
Yves Lange Avatar asked Oct 14 '25 07:10

Yves Lange


2 Answers

I don't think you can use <slot> in an SVG

Simplifying your code just to see what works.

Below is a native custom element that allows you to embed inner content. The inner content is an SVG. I added values into the SVG to get it to work.

class MyElement extends HTMLElement {
  constructor() {
    super();
    const s = this.attachShadow({mode:'open'});
    s.innerHTML = `<div>Your SVG</div>
      <div style="border:1px solid blue;width:20px;height:20px;"><slot></slot></div>`;
  }
}
customElements.define('my-element', MyElement);
<my-element>
<svg height="20" width="20">
<circle cx=10 cy=10 r=10 fill=red></circle>
</svg>
</my-element>

Now let's try doing this with the svg and not a div:

class MyElement extends HTMLElement {
  constructor() {
    super();
    const s = this.attachShadow({mode:'open'});
    s.innerHTML = `<div>Your SVG</div>
      <svg height="20" width="20" style="border:1px solid blue;"><slot></slot></svg>`
  }
}
customElements.define('my-element', MyElement);
<my-element>
<circle cx=10 cy=10 r=10 fill=red></circle>
</my-element>

If you dig into the Dev Tools you will see that the SVG refuses to accept a slot tag as a child. I didn't think that SVG allowed for <slot> and not I am sure. I just don't think you can do this the way you want.

like image 94
Intervalia Avatar answered Oct 18 '25 06:10

Intervalia


The svg requirement time to render. Here is an alternative to your question. You can work around by using lifecycle. But ideally, you need to take another approach. Each svg should be competed and svg can have multiple svgs. For example, you can have an svg as a icons. A lit of svgs is an icon set which is your component.

Attached image

like image 26
Kim Do Avatar answered Oct 18 '25 05:10

Kim Do



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!