在 Web 组件中将一个 SVG 放入另一个 SVG 中

Slotting a SVG inside another SVG in a Web Component

这是一个 CodePen:https://codepen.io/neezer/pen/VwbZNYB

我想在自定义 Web 组件中将一个 SVG 插入另一个 SVG 中,但每次我都会看到一个空白屏幕。在上面的示例中,您可以注释掉所有 JS,并看到应该出现在我的自定义 Web 组件中的红色方块。我知道我的浏览器支持 <slot>,因为 MDN examples 有效。

我做错了什么?

foreignObject 仅在 <svg-slot>Web Component
user 时有效 包括一个有效 SVG:

<svg-slot>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" width="500" height="500">
    <circle cx="50%" cy="50%" r="15%" fill="green"></circle>
  </svg>
</svg-slot>

对于 <template>:

<template>
  <style>svg{width:40vw}</style>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" width="500" height="500">
    <circle cx="50%" cy="50%" r="25%" fill="red"></circle>
    <foreignObject x="0" y="0" width="100%" height="100%">
      <slot></slot>
    </foreignObject>
  </svg>
</template>

所需的 <svg-slot> Web 组件代码:

  customElements.define('svg-slot', class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode:'open'})
          .append(document.querySelector('template').content.cloneNode(true));
    }
  });

但是...

您希望您的 Web 组件用户编写 minimal semantic HTML:

<svg-slots>
  <circle slot="foo" cx="50%" cy="50%" r="15%" fill="green"></circle>
  <circle slot="bar" cx="50%" cy="50%" r= "5%" fill="gold"></circle>
</svg-slots>

这需要在 Web 组件的 connectedCallback 中做一些额外的工作

因为 <circle>lightDOM 现在是 Unknown HTML Elements.

所以你需要一个额外的步骤来将 lightDOM 中的所有内容转换为 SVG(正确的 SVG 命名空间)

然后可以将其注入 shadowDOM

中的(模板)SVG

<svg-slots>
  <circle slot="foo" cx="50%" cy="50%" r="30%" fill="green"></circle>
  <circle slot="bar" cx="50%" cy="50%" r="10%" fill="gold"></circle>
</svg-slots>

<template>
  <style>svg{ height:180px }</style>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" width="500" height="500">
    <circle cx="50%" cy="50%" r="50%" fill="red"></circle>
    <slot name="foo"></slot>
    <slot name="bar"></slot>
  </svg>
</template>

<script>
  customElements.define('svg-slots', class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode:'open'})
          .append(document.querySelector('template').content.cloneNode(true));
      setTimeout(()=>{ // make sure innerHTML is parsed
        let svg = document.createElementNS("http://www.w3.org/2000/svg","svg");
        svg.innerHTML = this.innerHTML;
        svg.querySelectorAll("*")
           .forEach(el =>
                    this
                      .shadowRoot
                      .querySelector(`slot[name="${el.getAttribute("slot")}"]`)
                      ?.replaceWith(el)
            )
      })
    }
  });
</script>

备注: