はじめに
WebComponentsを作っているとShadowDom側にデザイン要素を持たないように作ると思います
そんな中で利用するslot
で困ったことがあったのでまとめます
問題
WebComponentsでslot
を何度も使いまわしたいと考えました
inde.html
<SampleElement>
<div slot="test-msg">Hello</slot>
</SmapleElement>
index.tsx
return (
<div>
<slot name="test-msg"></slot>
<slot name="test-msg"></slot>
<slot name="test-msg"></slot>
</div>
)
しかしこのときにHello
が表示されるのは最初のスロットのみでした
すべて表示させるにはどうすればよいのでしょうか?
原因
slotでは同じものをCustomElement側で何度もしようすることはできません
slot名を別名にしてhtmlにかけば利用することができます
index.html
<SampleElement>
<div slot="test-msg-0">Hello</slot>
<div slot="test-msg-1">Hello</slot>
<div slot="test-msg-2">Hello</slot>
</SmapleElement>
index.tsx
return (
<div>
<slot name="test-msg-0"></slot>
<slot name="test-msg-1"></slot>
<slot name="test-msg-2"></slot>
</div>
)
また、slotを同じ名前にすると最初にヒットした位置にすべてのスロットが表示されてしまいます
index.html
<SampleElement>
<div slot="test-msg">Hello</slot>
<div slot="test-msg">Hello</slot>
<div slot="test-msg">Hello</slot>
</SmapleElement>
index.tsx
return (
<div>
<slot name="test-msg"></slot>
<slot name="test-msg"></slot>
<slot name="test-msg"></slot>
</div>
)
このようにすると画面にはHelloHelloHello
と1つ目のスロット位置にでます
解決方法
この仕様を考えるとSlot
のノードをクローンして使うほうが良いのかなと思い以下のように行いました
index.html
<body>
<script src="./src/index.tsx" type="module"></script>
<div>
<sample-element></sample-element>
</div>
<script>
const sampleTag = document.getElementsByTagName("sample-element")[0];
Array.from(new Array(5)).map((v, i) => {
divElement = document.createElement("div")
divElement.setAttribute("slot", `test-msg-${i}`)
divElement.innerHTML = "Hello"
sampleTag.appendChild(divElement)
});
</script>
</body>
index.tsx
return (
<>
<div>Hello</div>
{Array.from(new Array(3)).map((v, i) => {
return <slot name={`test-msg-${i}`}></slot>;
})}
</>
);
手動でHTMLにSlotを3行書いてもよいですが、拡張性を考えてスクリプトで行いまいした
すこし強引ではありますが、これが考えた中では良いのかなと思います
おわりに
もっといい方法がある方はぜひとも教えてほしいです
WebComponent難しいなと改めて感じました
参考