プログラミング

Web Components: Shadow DOM に HTML と CSS をセットするいろいろな方法

投稿日:2019年12月17日 更新日:

1. はじめに

Web Components の Shadow DOM を使う場合のファイル構成やコードについて、いろいろなパターンを紹介します(最小限のコードのみです)。

2. Shadow DOM に HTML と CSS をセットするいろいろな方法

パターン1

<template> タグを利用する方法です。

まず、HTMLのどこかに <template> タグを記述ます。

<template id="my-template">
  ...
</template>

JavaScript のコードは以下となります。

customElements.define('my-element',
  class extends HTMLElement {
    constructor() {
      super();
      // id 属性値を使って template を取得する
      var template = document.getElementById('my-template').content;
      // shadowRoot に template を追加します
      const shadowRoot = this.attachShadow({mode: 'open'})
                             .appendChild(template.cloneNode(true));
  }
})

メモ

  • id属性を使うのが無駄な気がします。

パターン2

HTML と CSS を文字列として用意する方法です。

JavaScript コードだけを使います。

customElements.define('my-element',
  class extends HTMLElement {
    constructor() {
      super();
      const shadowRoot = this.attachShadow({ mode: 'open' });
      // テンプレート・リテラルを使って複数行の文字列を生成しています
      shadowRoot.innerHTML = `
<style>
  :host {
    color: #f00;
  }
</style>

<p>Hello, World!</p>
<p>This is a custom element!</p>
`;
  }
})

メモ

  • id 属性は必要ありません。
  • CSS と HTML の文字列は、JavaScript のテンプレート・リテラルを使って記述しています。<template> を使うより、こちらの方法が一般的であるようです。

パターン3

HTML と CSS を1つの JavaScriptファイルにして利用します(文字列としてエクスポートします)。

ということで、HTML と CSS をまとめた JavaScript ファイルを作ります。

export default `
<style>
:host {
  color: blue;
}
</style>
<h2>Title</h2>
<p>Hello!</p>
`;
  • ここでは、このファイル名を template.js とします。

今作成したファイルをインポートして利用する JavaScript コードが以下です。

import template from './template.js';

customElements.define('my-element',
  class extends HTMLElement {
    constructor() {
      super();
      const shadowRoot = this.attachShadow({ mode: 'open' });
      // template は文字列扱いです
      shadowRoot.innerHTML = template;
    }
  }
)

メモ

  • HTML と CSS を JavaScript の文字列として エクスポートするのは、よいやり方ではありません。

パターン4

HTML と CSS を1つの JavaScriptファイルにして利用します(template要素としてエクスポートします)。

まず、HTML と CSS を1つのJavaScriptファイルにまとめて記述します。

const template = document.createElement('template');
template.innerHTML = `
<style>
p {
  color: blue;
}
</style>
<h2>Title</h2>
<p>Hello!</p>
`;
export { template as default };

次がメインとなる JavaScript コードです。

import template from './template.js';

customElements.define('current-date',
  class extends HTMLElement {
    constructor() {
      super();
      const shadowRoot = this.attachShadow({ mode: 'open' });
      // template 要素を利用する
      shadowRoot.appendChild(template.content.cloneNode(true));
    }
  }
)

パターン5

CSS を <style> 要素として利用する方法です。

以下の JavaScript コードとなります。

const style = document.createElement('style');
style.innerHTML = `
  p { color: blue; }
`;

(省略)

customElements.define('my-element',
  class extends HTMLElement {
    constructor() {
      super();

      const shadowRoot = this.attachShadow({ mode: 'open' });
      shadowRoot.innerHTML = `
        <div>
          <p>Hello World</p>
        </div>
       `;

      // Shadow Root に style を追加します
      const clonedStyle = style.cloneNode(true);
      shadowRoot.appendChild(clonedStyle);
    }
  }
)

パターン6

CSS のみ1つのファイル(.css ファイル)にして利用する方法です。

まず、CSS ファイルを作成します(ここでは main.css というファイル名だとします)。

:host {
  display: block;
  contain: content;
}

この CSS ファイルを、Shadow DOM 内の <style> のところで、@import を使って読み込みます。

customElements.define('my-element',
  class extends HTMLElement {
    constructor() {
      super();
      const shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.innerHTML = `
<style>
  @import './main.css';
</style>
<p>Hello, World!</p>
`;
    }
  }
)

この方法であれば、Sass での記述を CSSに変換したファイルを Shadow DOM で利用することができます。

3. カスタムクラスをモジュールとして利用する

ここまで紹介したコードでは、カスタムクラスの定義と customElements.define()の実行を合わせて行っています。

しかし実際は、カスタムクラスの定義は1つのモジュールと見なし、単独の JavaScriptファイルにした方が利用しやすいでしょう。そうすれば、カスタムクラスと、そのクラスを適用する「カスタム要素の名前」を分離することにもなります。

例えば、こんな感じです。

my-module.js

export default class extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
<style>
  :host {
    display: block;
    contain: content;
  }
</style>
<p>Hello, World</p>
`;
  }
}

index.html の抜粋

<!-- カスタム要素に対して利用者側の立場でスタイルをあてる -->
<style>
my-element {
  color: blue;
}
</style>

<!-- カスタム要素 -->
<my-element></my-element>

<script type="module">
// モジュールをインポートしてカスタム要素の定義に利用する
import MyMod from './my-module.js';
window.customElements.define('my-element', MyMod);

// 他に利用するカスタムクラスがあれば、ここでインポートして define()する
</script>

4. おわりに

この先、Web Components が普及していけば、ベストプラクティスが定まってくると思います。現時点(2019年12月)ではまだその段階まできていないようです。

Web Components

Web Components: カスタム属性の利用方法

2019.12.18
Web Components

Web Components: 組み込み要素を拡張する方法

2019.12.18
Web Components

Web Components の Slot について

2019.12.17

📂-プログラミング
-

執筆者:labo


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

web development

Web Development for Beginners を読む:レッスン1

目次1. はじめに2. Web Development for Beginners の進め方3. レッスン1「Introduction to Programming Languages and Too …

Bootstrap

ボタンを押した直後に BootstrapのTooltipを数秒間表示する方法

ウェブページ上のボタンを押した直後に、Bootstrap の Tooltip を一定時間だけ表示する方法を紹介します。 スポンサードリンク 目次1. はじめに2. 参考になる情報3. プログラムの書き …

Web Storage を使ったサンプルページを作りました

Web Storage を使ったサンプルページを作りました。 目次1. スクリーンショット2. デモページ3. 動作4. ソースコード5. 参考情報 1. スクリーンショット スクリーンショット 2. …

Web Programming

IntersectionObserver API を使ったテストページを作成しました

IntersectionObserver API を使ったテストページを作成しました。 IntersectionObserver API は、ウェブページ上のある要素が見える範囲 (viewport) …

web development

Web Development for Beginners を読む:レッスン6と7

目次1. はじめに2. Lesson 6: JavaScript Basics: Making DecisionsA Brief Recap on Booleans(ブール値の簡単な要約)Compar …