12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

1人フロントエンドAdvent Calendar 2022

Day 4

【Web Componentsを学ぶ】Custom Elements編

Last updated at Posted at 2022-12-04

はじめに

この記事ではWeb Componentsを利用する準備のためにCustom Elementsを紹介する記事です。Web Componentsとは他のコードから独立して利用できる再利用可能なコンポーネント(要素)を指します。
Web Componentを利用するために必要な他の要素としてShadow DOMHTML Templateについての記事も書きましたので併せてご覧ください。また、Web Components自体の実装はこちらの記事で行いました。

Custom Elementsとは

Custom ElementsはJavaScriptに実装されたAPIの一つです。あらかじめ機能を定義した要素をHTMLの標準要素と同じように利用することができます。要素の名前はkebab-caseである必要があり、標準の要素との区別のために必ず2つ以上の単語で構成されている必要があります。
Custom Elementsは二種類の方法で利用することができます。

Autonomous custom element(自律カスタム要素)

標準の要素を利用しない独立した要素を定義する方法です。ReactなどのUIライブラリを利用する方にとって親近感のあるCustom Elementsです。
例えばこのAPIを用いてcustom-elementという要素を定義した場合以下のように利用することができます。

<custom-element></custom-element>

Customized built-in element(カスタム組み込み要素)

標準の要素を拡張するように機能を定義する方法です。定義した要素のis属性に作成したCustom Elementの名前を代入することで利用できます。例えばdiv要素を拡張して以下のように利用することができます。

<div is="custom-element"></div>

Custom Elementsの制御

Custom ElementsはCustomElementRegistryインターフェイスを利用して制御を行います。このインターフェイスにはwindow.customElementsからアクセスすることができます。このプロパティには4つのメソッドがあります。

define

Custom Elementsの定義を行うことができます。
Autonomous custom elementの場合は以下のように定義します。

customElements.define('custom-element', CustomElement);

Customized built-in elementの場合は以下のように定義します。

customElements.define('custom-element', CustomElement, { extends: 'div' });

第一引数はCustom Elementsを判別するための名前を渡します。先述の通りkebab-caseで2つ以上の単語で構成する必要があります。
第二引数はCustom Elementsの機能などを定義した要素のクラスを渡します。こちらについては後ほど紹介します。
第三引数はCustome Elementsを制御するためのオプションをオブジェクトで渡します。現在はextendsしか対応していないので、Customized built-in elementを利用する場合のみ使用する引数となっています。extendsをキーとしてCustom Elementsを組み込む要素をバリューとしたオブジェクトを渡します。Autonomous custom elementの場合は利用しません。

get

定義したCustom Elementsのクラスを取得する関数です。

customElements.get('custom-element')

第一引数にCustom Elementsの名前をとり、存在すればその要素のクラスを返します。存在しなければundefinedです。

upgrade

Custom Elementsの定義を引数の要素に反映させます。

const targetElement = document.createElement('custom-element');

customElements.define('custom-element', CustomElement);

// false
console.log(targetElement instanceof CustomElement)

customElements.upgrade(targetElement);

// true
console.log(targetElement instanceof CustomElement)

上のコードはupgradeによってtargetElementが定義された時点で定義されていないcustom-elementsが定義された要素として扱われるようになっています。

whenDefined

指定した名前の要素が定義されると解決されるPromiseを返すメソッドです。

const defineElement = () => setTimeout(() => {
    console.log(1);
    customElements.define('custom-element', CustomElement);
}, 500);

customElements.whenDefined('custom-element').then(() => console.log(2));
defineElement();

約500ms後に発火してcustom-elementを定義するような関数をwhenDefinedメソッド呼び出したのちに呼び出しました。1の後に2が呼び出されていることから定義が完了した後に呼び出されているのがわかります。
比較のためにCustom Elementsが関係ないsetTimeoutとPromiseの実行順を見てみると、2の後に1が呼び出されているので確かに先ほどの例を用いた説明は正しそうということがわかります(曖昧な言い方をしたのは完全に正しいことはこのコードでは保証されていないからです。)。

const defineElement = () => setTimeout(() => {
    console.log(1);
}, 500);
Promise.resolve().then(() => console.log(2));
defineElement();

Custom Elementsのクラス

Custom Elementsの機能などを実装するためにはHTMLの要素を継承したクラスを作成する必要があります。このクラスは先ほど確認したCustom Elementsの定義(CustomElementRegistry.define)で第二引数として渡されます。クラスを利用するのでES6以降の環境でなければいけないことに注意して下さい。
継承するクラスはAutonomous custom elementを利用する場合はHTMLElementを、Customized built-in elementの場合は組み込む要素のクラス(HTMLDivElementなど)を用います。
基本的な機能の実装はconstructorconnectedCallbackdisconnectedCallbackadoptedCallbackattributeChangedCallbackの5つメソッドに行います。それぞれに割り当てられたライフサイクルのタイミングで実行されます。

constructor

要素が作られるタイミングで発火するメソッドです。利用する場合は内部でsuper()を必ず呼び出す必要があります。

connectedCallback

DOMに追加されるタイミングで発火するメソッドです。

disconnectedCallback

DOMから削除されるタイミングで発火するメソッドです。

adoptedCallback

DOMから移動するタイミングで発火するメソッドです。

attributeChangeCallback

属性が追加、変更、削除されたタイミングで発火するメソッドです。変更を監視する属性は明示的に定義する必要があリます。observedAttributesを用いて定義することができます。

これらのメソッドが指定したライフサイクルで発火されることを確認するためにサンプルを作成しました。adoptedCallbackはなかなか使う機会がないことと、条件の設定が難しいためサンプルでは定義しておりません。attributeChangeCallbackはどんな属性でも変化を追跡しているわけではなく、static get observedAttributesによって返される配列に含まれる名前の属性だけ追跡してくれることに注意して下さい。

See the Pen CustomElementDemo by KokiSakano (@kokisakano) on CodePen.

CustomElementを作成(new)することでconstructorが発火すること、作成したCustomElementをDOMに追加(appendChild)ことでconnectedCallbackが発火すること、作成したCustomElementをDOMから削除(removeChild)することでdisconnectedCallbackが発火すること、属性の追加、変更、削除を監視してattributeChangeCallbackが発火することを確認できます。CustomElementを利用するために先ほど紹介したCustomElementRegistry.defineを利用する必要があることに注意が必要です。

まとめ

Web Componentsを利用するために必要なCustom Elementsを学びました。この機能を用いてHTMLにはない自分で作成した要素を利用できるようになります。

12
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?