Web Components を実装しました。Custom elements と Shadow DOM を使いました。

更新 公開)

# [Web Components](https://developer.mozilla.org/docs/Web/Web_Components) を実装した
```js
customElements.define("my-element", class extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({mode: "open"});
    }
    attributeChangedCallback() {
        this.#_attributeChangedCallback();
    }
    connectedCallback() {
        this.#_connectedCallback();
    }
});
```

## [Custom elements](https://developer.mozilla.org/docs/Web/Web_Components/Using_custom_elements)

### [`CustomElementRegistry.define`](https://developer.mozilla.org/docs/Web/API/CustomElementRegistry/define)

新しい要素が登録できます。

ここでは `my-element` 要素を登録しています。([要素名には多少の制限があるようです](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name))

### [The lifecycle callbacks](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks)

要素のライフサイクルに応じて起動できます。
```js
#attr;
#attr_old;
static get observedAttributes() {
    return ["my-attr"];
}
attributeChangedCallback(name, oldValue, newValue) {
    if ("my-attr" !== name) {
        return;
    }
    this.#attr = newValue;
    this.#attr_old = oldValue;
}

#connected = false;
connectedCallback() {
    if (this.#connected) {
        return;
    }
    this.#_init();
    (new MutationObserver(this.#_init)).observe(this, this.#_options);
    this.#connected = true;
}
```

#### `attributeChangedCallback`

`observedAttributes` で指定した属性が追加、削除、変更されるたびに実行されます。

ここでは `my-attr` 属性を指定しています。古い値 `oldValue` と新しい値 `newValue` をそれぞれ `#attr_old` プロパティと `#attr` プロパティに設定します。

#### `connectedCallback`

要素が [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) に追加されるたびに実行されます。

ここでは最初に追加されたときに [`MutationObserver.observe`](https://developer.mozilla.org/docs/Web/API/MutationObserver/observe) を呼び出しています。要素が変更されたときにコールバック関数 `this.#_init` を呼び出します。

また*最初*に [`Node.childNodes`](https://developer.mozilla.org/docs/Web/API/Node/childNodes) 等を使いたいときは `constructor` よりも `connectedCallback` で使うようにしています。`constructor` ではエラーを吐くことがあったので。

## [Shadow DOM](https://developer.mozilla.org/docs/Web/Web_Components/Using_shadow_DOM)
```js
this.attachShadow({mode: "open"}).appendChild(document.createElement("style")).textContent = `:host {
    display: block;
}`;

const style = document.createElement("link");
style.rel = "stylesheet";
style.href = "style.css";
this.shadowRoot.appendChild(style);
```

### [`Element.attachShadow`](https://developer.mozilla.org/docs/Web/API/Element/attachShadow)
Shadow DOM が追加できます。[`Element.shadowRoot`][Element.shadowRoot] への参照を返します。

### [`Element.shadowRoot`][Element.shadowRoot]
[Element.shadowRoot]: https://developer.mozilla.org/docs/Web/API/Element/shadowRoot

[`ShadowRoot`](https://developer.mozilla.org/docs/Web/API/ShadowRoot) オブジェクトです。

ここでは [Shadow DOM にスタイルシートを追加しています](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM#styling_the_shadow_dom)。

Shadow DOM は(基本的に)外のスタイリングに影響しない、されないようです。

-   [継承](https://developer.mozilla.org/docs/Web/CSS/inheritance)はするようです
-   [CSS custom properties](https://developer.mozilla.org/docs/Web/CSS/Using_CSS_custom_properties)も継承するようです

#### [`:host`](https://developer.mozilla.org/docs/Web/CSS/:host) 擬似クラス

Shadow host が選択できます。

ここでは `my-element` 要素のスタイルに `display: block` を加えています。

#### [`::part`](https://developer.mozilla.org/docs/Web/CSS/::part) 擬似要素

Shadow tree の内の要素が選択できます。

無効なフォームです。ページの再読み込み、又は外部サービスをお試しください。