メイラボ開発者ブログ

株式会社メイラボのエンジニアブログ

Web Components > Custom Elementsについて

f:id:soraxism:20190313222539j:plain

こんにちは。メイラボのフロントエンドエンジニアの空です。
4回に渡ってお送りしているWeb Componentsについて、第2回目はCustom Elementsについてのお話です。

前回の記事
blog.meilabo.com

Custom Elementsとは

開発者独自の要素を作れる技術です。
プロパティやメソッド、イベントを持たせることも可能で、既存の要素を拡張することもできます。
APIのバージョンにv0とv1が存在し、それぞれ仕様が異なるので注意が必要です。

独自要素の定義方法

<my-button><my-counter>のような、独自要素を生成可能することができます。
ここで注意することは独自要素名には必ずハイフンを含める必要があること。

// HTMLElementを継承し、独自要素を定義
class MyButton extends HTMLElement {
    constructor() {
        super();
    }
}

// 独自要素を登録
customElements.define('my-button', MyButton);

独自要素へのAPI定義

既存のHTML要素には様々なAPIが用意されています。例えばSELECT要素をクリックすると、オプションを選択できるなどもその一部です。
独自要素に対しても同様にメソッドやプロパティを持たせ機能を実装することが可能となっています。

例として次の機能を持った<my-calc>要素を作成してみます。

  • numプロパティで要素が持つ数値を読み書きすることができる
  • sumメソッドで渡された数値の和を求める
// HTMLElementを継承し、独自要素を定義
class MyCalc extends HTMLElement {
  constructor() {
    super();
    this._num = 0;
  }

  get num() {
    return parseInt(this._num);
  }

  set num(val) {
    this._num = parseInt(val);
  }
  // textContent
  sum() {
    this.textContent = this.num + this.num;
  }
}

// 独自要素を登録
customElements.define('my-calc', MyCalc);
// <my-calc>を生成し、bodyへ追加
const calc = new MyCalc();
document.body.appendChild(calc);
// 与えた数値の和を求める
calc.num = 2;
// ブラウザ上に4と表示
calc.sum();

既存要素の拡張

すでに用意されてあるHTML要素を拡張して使用することも可能です。
以下は<button>要素の拡張方法となります。

// HTMLButtonElementを継承した独自要素を定義
class BigButton extends HTMLButtonElement {
  constructor() {
    super();
    this.style.padding = '1em 2em';
    this.style.fontSize = '40px';
  }
}

// 要素登録時に引数として{extends: 'button'}を渡す
customElements.define('big-button', BigButton, {extends: 'button'});
// 要素生成時に引数として{is: 'big-button'}を渡す
const bigButton = document.createElement('button', {is: 'big-button'});
bigButton.textContent = 'Big Button';
document.body.appendChild(bigButton);

JavaScriptを用いずに直接HTMLへ要素を追加する場合は以下のような記述で可能です。

<button is="big-button">Big Button'</button>

Custom Elementsのメソッド

以下のものが用意されており、クラスのメソッドとして実装することで、イベント捕捉 -> 独自処理を行うことができます。

  • constructor()
  • connectedCallback()
    • 要素がDOMに追加された時に呼び出される
  • disconnectedCallback()
    • 要素がDOMから削除された時に呼び出される
  • attributeChangedCallback(attrName, oldVal, newVal)
    • 要素の属性が変更、追加、削除、または置換された時に呼び出される
  • adoptedCallback()
    • 要素が別のdocumentに移動された時に呼び出される
class MyElement extends HTMLElement {
    constructor {
        super();
    }

    connectedCallback() {
        console.log('要素が追加されました');
    } 
}

attributeChangedCallbackの使用方法

前述のattributeChangedCallbackメソッドを使用することで属性変更のイベントを検知できるのですが、使用方法が若干特殊で、静的なobservedAttributesゲッタで検知する属性を定義する必要があります。

<hello-element name="sora"></hello-element>
class HelloElement extends HTMLElement {
  constructor() {
    super();
  }

  // ここで定義した属性が変更された時のみイベント発火
  static get observedAttributes() {
    return ['name'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (attrName === 'name') {
      // コンソールに「Hello sora」と表示
      console.log(`Hello ${newVal}`);
    }
  }
}

customElements.define('hello-element', HelloElement);

まとめ

Custom Elementsを使用することによって、独自の機能を持ったHTML要素を作成することができます。
WebComponentsのShadow DOMと組み合わせることによって、このブログ下部にあるTwitterのボタンも<twiiter-button></twitter-button>だけで実装するようなことも可能です。

次回はShadow DOMに関して解説したいと思います。

参考サイト