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>
对于<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 组件用户编写最少的 语义 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 中的 <circle> 现在是 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>
注意事项:
-
使用<slot> 元素;但不是它的功能;它可以是任何(未知)元素。
-
connectedCallback 在移动 DOM 节点时也会触发(例如拖放);您需要额外的代码来防止错误。 attachShadow可以移动到constructor
-
可以重构为不使用shadowDOM;那么所有的 SVG 都可以使用 global CSS
但是你会得到一个 FOUC,因为 <svg-slots> 'innerHTML' 现在显示为常规 DOM,
不是 lightDOM(可选反射/插入 shadowDOM)
-
您可以通过Unknown HTML Elements 做更多事情
<pie-chart>
<slice size="90" stroke="green">HTML</slice>
<slice size="1" stroke="red">JavaScript</slice>
<slice size="9" stroke="blue">CSS</slice>
</pie-chart>
创建一个完整的 SVG 饼图;
见: