SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ

フレームワークに頼らない! フロントエンド技術「Web Components」のAPIを学ぼう

再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ 第2回


  • X ポスト
  • このエントリーをはてなブックマークに追加

シャドウDOM(Shadow DOM)

 シャドウDOMはWeb Componentsにとってカスタム要素と並ぶ重要な側面で、文書構造や振る舞いのカプセル化を司っています(カプセル化については前回を参照)。

 ここで、シャドウDOMの概念について簡単におさらいしておきましょう。シャドウDOMは、通常の文書(window.document)とは切り離され、独立してレンダリングされる、シャドウツリーと呼ばれるDOMツリーを扱うための機能です。ほとんどのケースで、通常のDOMツリーと同じ感覚で操作しても問題ありません。シャドウツリーはいくつでも作ることができ、各シャドウツリーの根本となる要素はシャドウルートと呼ばれます。通常の文書から切り離されたままでは表示ができないので、実用上は通常の文書のツリーのどこかの要素に接続して扱うことになりますが、このとき接続する先の要素をシャドウホストと呼びます。

 それぞれの用語を図にまとめると、図2のように表せます。

図2:シャドウDOMの概要
図2:シャドウDOMの概要

 シャドウツリーと文書ツリーは明確に区切られており、カプセル化されているシャドウツリーの内部でエラーが起きても、文書ツリーを壊すことはありません。

 概念だけだとわかりづらいので、実際にどのように見えるのか確認してみましょう。図3は、リスト5のレンダリング結果をDev Toolsで表示したものです。

図3:Dev Toolsで要素の役割を確認する
図3:Dev Toolsで要素の役割を確認する

 文書ツリー側でシャドウホスト、つまりシャドウツリーと文書ツリーを紐づける役割を担っているのが、カスタム要素であるです。シャドウホストは#shadow-root以下のシャドウツリーと繋がっており、通常のDOMツリーと同じように表示されますが、前述の通り、文書ツリーとシャドウツリーは別々にレンダリングされています。

 さて、ここまで解説したことで、ようやくAPIの解説ができるようになりました。シャドウDOMにとって重要なことは、シャドウホストと紐づいたシャドウルートを生み出し、シャドウツリーを組み上げることです。そのために用意されているのが、カスタム要素のクラスのプロパティとして提供されているattachShadowメソッドです(リスト6)。

[リスト6]カスタム要素にシャドウルートを接続する
    class MyProfile extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: "open" }); // (1)
    
        const para = document.createElement('p');
        this.shadowRoot.appendChild(para); // (2)
      }
    }
    

 (1)を実行することで、シャドウホストであるカスタム要素にシャドウルートが紐付きます。パラメータのmodeはDev Tools等でシャドウルートの内部にアクセスできるかどうかで、openならば可視、closedならば不可視となります。実装を完全に隠したい場合はclosedを選びたくなるところですが、closedにした場合にも比較的容易に内部へアクセスできることが公式ドキュメントで言及されているため、事実上、指定できるパラメータはopen一択となるようです。

 シャドウルートとの紐付けが済むと、カスタム要素のクラスでshadowRootというプロパティが利用できるようになります。(2)のように動的に要素を追加することで、シャドウツリーを構築することができます。shadowRootはElementやDocumentと同じくNodeのインターフェースを持っているため、appendChildのような見慣れたメソッドを利用できます。

 このように、シャドウDOMの仕組みを利用することで、文書ツリーから切り離された文書構造を組み上げ、カスタム要素に紐づけることができます。

HTMLテンプレート(HTML Templates)

 HTMLテンプレートは、再利用可能な文書構造をHTMLファイルの中に記述できる機能です。

 HTMLファイルの中に<template>要素で囲んだ領域を作ると、通常のレンダリングではページに表示されない要素になります。これをJavaScriptから参照したりコピーしたりすることで、文書構造を再利用します。本来はWeb Componentsと関係なく利用できる機能ですが、シャドウDOMの文書構造を定義する用途との相性が非常に良いため、Web Componentsを成立させるための重要な要素のひとつに数えられています。

 また、HTMLテンプレートで作成したテンプレートの一部に後から要素を挿入するための仕組みとして、<slot>という要素が用意されています。

 それでは、実際の使い方を見てみましょう。シャドウDOMの文書構造をテンプレートを利用して定義します(リスト7)。

[リスト7]HTMLテンプレートで定義した文書構造をシャドウDOMに割り当てる
    class MyProfile extends HTMLElement {
      constructor() {
        super();
    
        this.attachShadow({ mode: "open" });
    
        let template = document.getElementById('my-profile'); // (1)
        let templateContent = template.content;
    
        this.shadowRoot.appendChild(templateContent.cloneNode(true)); // (2)
      }
    }
    
    customElements.define("my-profile", MyProfile);
    

 本記事のこれまでの例では、シャドウDOMの文書構造はinnerHTMLを用いて定義していました。しかし、リスト7ではHTML側にテンプレートを用意するため、(1)のように要素を呼び出すだけでOKです。要素をそのまま操作するとテンプレートの定義内容が変わってしまい、他のコピーした先にも影響してしまうため、(2)でcloneNodeを呼び出し、元の要素をコピーしています。

 JavaScript側の文書構造の定義はこれでOKです。

 次は、(1)で呼び出したテンプレートをどのように定義しているか見てみましょう(リスト8)。

[リスト8]HTMLテンプレートを定義する
    <!DOCTYPE html>
    <html lang="ja">
    <body>
      <div>
        <my-profile>
          
          <span slot="fullname">Taro Suzuki</span>
          <span slot="age">15</span>
        </my-profile>
      </div>
      
      <template id="my-profile">
        <div>
          <h1>My Profile</h1>
          
          <div id="fullname">full name: <slot name="fullname">John Doe</slot></div>
          <div id="age">age: <slot name="age">unknown</slot></div>
        </div>
      </template>
      <script src="./template.js"></script>
    </body>
    </html>
    

 リスト7で呼び出していたテンプレートは、(1)に定義したものです。テンプレート内の構造としては、リスト4で定義していたものとあまり変わらないように見えますが、(2)の部分は従来<span>要素だったところを<slot>に置き換えてあります。<slot>は後から別の要素に置き換えることができる特殊な要素です。置き換えられなかった場合は、<slot>の子要素がデフォルト値として使用されます。

 スロットを置き換える要素は、(3)のようにカスタム要素の子要素として記述します。<slot>name属性と、置き換えたい要素のslot属性が一致した場合に、スロットは置き換えられます。

 このように、HTMLテンプレートの機能を利用することで、再利用性と拡張性に優れた文書構造をHTMLファイルの中に記述できます。

まとめ

 Web Componentsを構成する3つの要素にそれぞれ着目して解説を行いました。それぞれが特徴的なので混同して困ることはないのですが、やや煩雑な印象がありますね。次回は、Web Componentsを簡便に扱うためのライブラリを解説します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/16337 2022/09/13 12:03

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング