在 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>
备注:
使用<slot>
元素;但没有它的功能;它可以是任何(未知)元素。
connectedCallback
也会在移动 DOM 节点(例如拖放)时触发;您需要额外的代码来防止错误。 attachShadow
可以移动到constructor
可以重构为不使用阴影DOM;那么所有 SVG 都可以使用 global CSS
设置样式
但是你会得到一个 FOUC 因为 <svg-slots>
'innerHTML' 现在显示为常规 DOM,
不亮DOM(可选reflected/slotted进入阴影DOM)
您可以使用 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 饼图;
参见:
这是一个 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>
备注:
使用
<slot>
元素;但没有它的功能;它可以是任何(未知)元素。connectedCallback
也会在移动 DOM 节点(例如拖放)时触发;您需要额外的代码来防止错误。attachShadow
可以移动到constructor
可以重构为不使用阴影DOM;那么所有 SVG 都可以使用 global CSS
设置样式 但是你会得到一个 FOUC 因为<svg-slots>
'innerHTML' 现在显示为常规 DOM,
不亮DOM(可选reflected/slotted进入阴影DOM)您可以使用
做更多事情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 饼图;
参见: