飴屋

JavaScript/Webコンポーネント/コンポーネント状態属性

Webコンポーネントは内部に持つShadow DOMを外部のLight DOMと切り離して考えられるところがいいのですが、コンポーネントの内部状態を外から操作したいときももちろんあるわけです。同じカプセル化の考え方を持つクラス(class)化、オブジェクト指向なんかだったら、publicやpubみたいなキーワードをつけて、外からのアクセスを明示的に許可できますが、Webコンポーネントの場合、属性値(attibute)を使った操作ができそうです。

<fan-menu></fan-menu>
<fan-menu open></fan-menu>
<fan-menu disabled></fan-menu>
<fan-menu color="red"></fan-menu>

こんな感じでメニューが開いていることを示したり、非活性化状態だったり、赤い色をつけたり、Webコンポーネントを操作・変更できます。属性値なのでhasAttributeremoveAttributesetAttributeメソッドで外から操作できますし、Webコンポーネントと紐づいたClassに操作用のメソッドをつけておくこともできそうです。

toggle() {
  if (this.hasAttribute('open')) {
    this.removeAttribute('open');
  } else {
    this.setAttribute('open', '');
  }
}

開閉トグルをつけるならこんな感じでしょうか。

observedAttributesとattributeChangedCallback

こういった属性値を使う場合、事前に宣言するようなメソッドをクラスに生やしておきます。

static get observedAttributes() {
  return ['open', 'disabled', 'color'];
}

属性名を文字列の配列と返します。これをやっておくと、属性値に変化があった時にイベントのようにattributeChangedCallbackが呼び出されるようになります。

attributeChangedCallback(name, oldValue, newValue) {
  console.log(`属性 ${name} が ${oldValue} → ${newValue} に変更された!`);
  if (name === 'open') {
    const isOpen = newValue !== null;
    if (isOpen !== this._isOpen) {
      this._isOpen = isOpen;
      // ここでUI更新
    }
  }
}

属性が新しく追加されたときは、oldValue が null で、属性が削除されたときは、newValue が null になります。クラスの状態(内部の変数)をこれで更新し、UIへの影響も連動して行えるようになりました。

スタイルシート

もちろん、属性の変更に応じたCSS定義による変化も可能です。

:host .items {
  display: none;
}
:host([open]) .items {
  display: block;
}