Web ComponentsでReactのThemeProvider
のようなものを実装したい。
@ionic/coreのthemeの実装を読んだら感銘を受けたのでメモ
まとめ
- Element.closest(selector)でDOM treeを逆向きに検索する
- 実装に困ったら@ionic/coreを読む
実装
theme-provider
attribute変更の際にthemeの変更を適用したいという要求が無いなら、空のコンポーネントで良い
<template id="theme_provider">
<slot></slot>
</template>
customElements.define(
"theme-provider",
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById("theme-provider");
this.template = template.content;
const shadowRoot = this.attachShadow({
mode: "open"
}).appendChild(template.content.cloneNode(true));
}
}
);
子コンポーネント
子コンポーネントは何でもいいですが、各テーマのstylesを用意しておきます
<template id="heading_level1">
<style>
h1.default {
color: #333;
}
h1.christmas {
color: green;
}
</style>
<h1>
<slot></slot>
</h1>
</template>
customElements.define(
"heading-level1",
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById("heading_level1");
this.template = template.content;
const shadowRoot = this.attachShadow({
mode: "open"
}).appendChild(template.content.cloneNode(true));
// theme-providerを探してthemeを登録する
this.themeElement = this.closest('theme-provider');
const theme = this.themeElement ? themeElement.getAttribute('theme') : '';
this.shadowRoot.querySelector("h1").className = theme;
}
}
);
DOMツリーを親の方向に検索してThemeを取得する
DOMツリーの親方向に探索する場合はElement.closest(selector)
const theme = this.closest('theme-provider').getAttribute('theme');
this.shadowRoot.querySelector("h1").className = theme;
classNameをプログラマブルにしたい場合はclsxとかを使うと良いと思う
theme-provider
が変更された場合に反映したい
動的にthemeを入れ替える必要がある場合は、その変更を子コンポーネントに通知したい。
theme-provider
側:変更を外に通知するためにCustomEvent
を使う必要がある。
子コンポーネント側:初回のtheme取得と同じく、親Nodeを遡ってtheme-provider
を見つけ、event listenerを登録しておく。
というdemoはCodepenに置きました。