【问题标题】:Slotting a SVG inside another SVG in a Web Component在 Web 组件的另一个 SVG 中插入一个 SVG
【发布时间】:2021-06-27 23:05:26
【问题描述】:

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

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

我做错了什么?

【问题讨论】:

  • 是一种 HTML 元素,而不是 SVG 元素。您需要将其插入到 foreignObject 中才能使其工作,但请注意 SVG 具有可能满足您需求的 元素。
  • @Kaiido 这正是我所缺少的。如果您将评论作为解决方案发表,我会很乐意接受。我不能将<use> 用于我的用例,但<foreignObject> 工作得很好。

标签: javascript svg web-component native-web-component


【解决方案1】:

foreignObject 仅在<svg-slot>Web 组件的用户 时有效
包含一个有效 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>

对于&lt;template&gt;

<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>

需要&lt;svg-slot&gt; Web 组件代码:

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

但是……

您希望您的 Web 组件用户编写最少的 语义 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 中做一些额外的工作

因为 lightDOM 中的 &lt;circle&gt; 现在是 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>

注意事项:

【讨论】:

  • 感谢您的完整回答。在我的例子中,开槽元素是额外的自定义元素,所有这些元素都定义了有效的 SVG(使用xmlns),所以foreignObject 仍然是我的首选解决方案。我为手头的问题提炼了我的例子。不过,很高兴了解这些其他选项!
  • foreignObject 的 bug 比蚁丘还多
猜你喜欢
  • 2014-05-21
  • 2021-08-03
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-30
  • 2011-04-03
相关资源
最近更新 更多