🧩

HTMLのカスタム要素(とシャドウDOM)を使ったWebコーディング

2022/12/18に公開

Jitoinを開発しているitteと申します。Webエンジニア向けのサービスや記事を公開しています。

今回はHTMLのカスタム要素(Custom Element)について解説します。
カスタム要素を使うとオリジナルのHTMLタグを作ることができます。

カスタム要素の利用例

例えば次のタグは文字色をあらかじめ決めておいたプライマリ色に変えるタグです。

<color-primary>Primary</color-primary>

これだけだと、classを使うのと変わりありませんね。

次の例はどうでしょうか。ランディングページのHTML例です。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>LP</title>
  <style>* { margin: 0; }</style>
</head>
<body>
  <main-visual></main-visual>

  <used-by></used-by>
  <section-concept></section-concept>
  <conversion-type1></conversion-type1>

  <section-point1></section-point1>
  <section-point2></section-point2>
  <section-point3></section-point3>
  <conversion-type1></conversion-type1>

  <section-qa1></section-qa1>
  <section-qa2></section-qa2>
  <section-qa3></section-qa3>
  <conversion-type2></conversion-type2>

  <script src="./script.js"></script>
</body>
</html>

私の英語力は置いておいて、タグを読めばなんとなく、どんなコンテンツがあるか分かります。これはカスタム要素の概念を表したもの・・・ではなく、正しいHTMLです

このHTMLにはランディングページにあるはずの画像や説明文、製品購入ページへのリンクタグが見当たりません。
どこへいったのかというと、最後に読み込んでいる./script.jsの中で生成されています。

カスタム要素をどうやって作るか

愚直にカスタム要素を作ろうとするとJavaScriptの知識が必要になります。しかもその方法が簡単な仕組みで提供されていないのでJavaScriptに慣れていないと難しいかもしれません。

でも、安心してください。

Jitoinを利用すればHTMLとCSSの知識だけでカスタム要素を作ることができます。

https://jitoin.com

Jitoinは、HTMLを既存Webサイトに追加するという、何だかふわっとしたサービスですが、追加する手段としてカスタム要素も提供しています。

これから、Jitoinを使って、カスタム要素を作る方法を解説します。

事前準備

Jitoinに登録する

Jitoinに登録するには、Googleアカウントが必要です。
サイトの右上に『Googleでログイン』ボタンがありますので、そこから利用開始できます。

Google login

Jitoinタグを作る

ログインすると、コンソール画面に移動します。
『新しいタグを作る』ボタンを押すと、すぐにJitoinタグが生成されます。Jitoinタグの説明は後述します。

コンソール

画面下に現れた英数字の羅列は、作ったJitoinタグの名称(変更可)になります。これを押します。

JitoinタグをWebサイトに張り付ける

切り替わった画面で、5行の<script>タグが書かれています。これがJitoinタグです。2I1*******のところはタグによって異なります。

タグをコピー

このJitoinタグをカスタム要素を使いたいページに張り付けます。ローカルのHTMLファイルでも問題ありません。そうすることでJitoinが対象ページにHTMLを追加できるようになります。

HTML例
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <!-- Jitoin Fire Tag -->
  <script type="module">
    import fire from '/js/fire.js';
    fire('websites/2I7*******');
  </script>
</body>
</html>

以上で準備完了です。

カスタム要素を作る

先の画面に『新しいコンポーネントを作る』ボタンがありますので押すと、Jitoinのコンポーネントを作る画面に遷移します。

まずはテンプレートを選ばされますので、『テンプレートなし』を選びます。

テンプレート選択

テンプレートを選んだら、アクションと書かれているところで『カスタム要素』を選んで、仮にmain-visutalと入力します。カスタム要素のタグ名のルールとしてハイフンを1つ以上含むことになっています。

カスタム要素を追加できる

次に、ガイドの左にあるコードマークを押します。

コードマーク

すると次のようにHTMLを編集できるエディタが現れます。このHTMLがカスタム要素の中身になります。これを好きなように書き換えてください。

初期HTML

今回は全部消して、『Hello』と書いてみましょう。

Hello

これでカスタム要素の作成が完了しました。今は下書き状態ですので、公開ボタンを押します。

公開

作ったカスタム要素を使う

先ほど作ったカスタム要素を使ってみます。Jitoinタグを入れたページに次のタグを書くだけです。

<main-visutal></main-visutal>
HTML例
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <main-visutal></main-visutal>

  <!-- Jitoin Fire Tag -->
  <script type="module">
    import fire from '/js/fire.js';
    fire('websites/2I7*******');
  </script>
</body>
</html>

キャッシュの影響ですぐに反映されない場合がありますが、最大2分以内には対象ページにHelloと表示されます。

シャドウDOMのコーディングのポイント

Jitoinでカスタム要素を作ったときは、シャドウDOMというものを用いています。このシャドウDOMでHTML・CSSを書くときはいくつか気を付けるポイントがあります。

シャドウDOMとは

カスタム要素を使ったときに、カスタム要素内部のHTMLは隠蔽されています。先の例でいうと<main-visutal></main-visutal>と書くだけで、『Hello』の文字を書かなくても『Hello』と表示されました。

これはシャドウDOMという、その要素だけの独自のDOM(HTML文書)が存在しており、その中に隠されているのです。

シャドウDOM

シャドウDOMはシャドウと言っておきながら、画面には表示されます。DOM(HTML文書)上では隠されているという点でシャドウという言葉が使われています。

カスタム要素とシャドウDOMは別々の概念で、JavaScriptではそれぞれ個別に利用できるのですが、一緒に利用されることがよくあります。
そして、Jitoinで作るコンポーネントはすべてシャドウDOMを使っていますので、カスタム要素にも必ずシャドウDOMがあります。

以降の説明にあたり、カスタム要素の中のシャドウDOM、すなわちブラウザでJitoinにログインして書くHTMLを内側のHTML(CSS)、作ったカスタム要素を使う側のHTMLを外側のHTML(CSS) と表現することにします。

外側のCSSは適用されない

これはメリットでありデメリットでもあります。

例えば外側のCSSにページ内のすべての要素のマージンを0にするCSSを書いていたとします。

外側のHTML
<!DOCTYPE html>
<html>
<head>

  ...

  <style>
    * {
      margin: 0;
    }
  </style>
</head>

...

  <main-visutal></main-visutal>

...

これはシャドウDOMmain-visutalの内側のHTMLには適用されず、マージンが復活します

もし、内側のHTMLもマージンを0にしたい場合は、内側のCSSで再びマージンを0にする必要があります。

面倒に思うかもしれませんが、逆に言えば、シャドウDOMの外側でどのようなCSSが書かれていたとしても無視してコーディングできます

他にも、シャドウDOMが複数あるときも、互いに別のシャドウDOM(空間)ですので、お互いを気にする必要はありません。

クラス名やidといったCSSセレクタの重複を気にする必要が無いのです。

:host疑似クラス

カスタム要素それ自体にスタイルを当てたいときは、2通りの方法があります。

1つ目は外側のCSSで、カスタム要素をセレクタで指定する方法です。以下の例ではタグ名を指定していますが、クラスやIDでも同様です。

外側のCSS
custom-element {
  color: red;
}

2つ目は内側のCSSで、:host疑似クラスを使う方法です。

内側のCSS
:host {
  color: blue;
}

特筆すべき点として、

カスタム要素自体は外側のDOMに属します。

カスタム要素は外側

これ重要です。

:host疑似クラスは内側のCSSから、外側のHTMLにスタイルを当てることができる特別な疑似クラスとなります。

もし外側のCSSと内側のCSSの両方からカスタム要素にCSSを当てた場合は、外側のCSSが優先されます。
つまり:host疑似クラスを使ったCSSプロパティは、上書きされてしまうことがあるということです。防ぐ方法は次のセクションで説明します。

継承とCSS変数は引き継がれる

シャドウDOMの外側のCSSは、シャドウDOMの内側のHTMLに適用されないと説明しましたが、フォントサイズや文字色といった、継承は内側のHTMLに突き抜けてきます。

ほかにも、定義したCSS変数vwrem<!DOCTYPE>といった、ドキュメント/デバイスによるものも引き継がれます。

そのため、例えばページ全体の文字色をグレーにしていた場合、シャドウDOMの中の文字色もグレーになります。

もし、継承されないようにしたい場合は、次のように:hostallプロパティを使います。

内側のCSS
:host {
  all: initial;
}

ここで思い出していただきたいのは、:hostはシャドウDOMの外側にあるということです。

実は、シャドウDOMにもシャドウルートという<html>タグおよび:root疑似クラスに該当する最上位の要素が存在しますが、困ったことに現状、シャドウルートをCSSセレクタで指定する方法がありません。

シャドウルート

そのため、:rootの代わりとしても:hostが使われています。
これは、シャドウDOMの外側で値を初期化して、初期化された値がシャドウルートを突き抜けて内側に継承しているということになります。

ということは、allプロパティで初期化したとしてもシャドウDOMの外側のCSSで上書きされてしまうと、結局は上書きされた値を継承してしまうということです。

これをどうしても防ぎたいときはシャドウDOMの中に全体を囲むdiv要素を置いて、そこで初期化すると回避できます。

内側のHTML
<div class="root">
  Hello
</div>

<style>
.root {
  all: initial;
}
</style>

カスタム要素の子

最初の例に戻って、カスタム要素に次のように子要素を書いたとします。

外側のHTML
<color-primary>Primary</color-primary>

このとき、『Primary』というテキストは外側のHTMLに書いているのですが、シャドウDOMが存在していると実際に画面に表示されるのは内側のHTMLになります。『Primary』はカスタム要素がJavaScriptで定義されるまでは表示されますが、その後は表示される保証はありません。

では、『Primary』がどう扱われるかというと、カスタム要素の作者にゆだねられています

Jitoinは、これに関していくつかの方法を提供していますが、難しい話になりますのでここでは1つだけ紹介します。その他は別記事で書きますのでお待ちください。

最も簡単な方法として紹介するのは、内側のHTMLに{{ content }}と書く方法です。{{ content }}と書いたところにカスタム要素の子に書かれたHTMLを展開します。

内側のHTML
<span style="color: blue">{{ content }}</span>

注意点として、あくまでHTMLとして展開しているので、外側のCSSは効かず、内側のCSSが適用されます。そのためclassidを使わないHTMLに留めるべきです。

contentの展開

SEOはどうなのか

カスタム要素を導入するにあたって気になるのはSEOへの影響です。

カスタム要素とシャドウDOMに対応している検索エンジン

カスタム要素&シャドウDOMのコンテンツを解析してくれる検索エンジンはいくつかあります。

私が実際にサイトを作って検証したところ、最大手のGoogleは対応していました。Ask、Startpageも対応を確認しています。Bing、Yahooは未対応でした。

なお、Googleは対応しているといっても、私の個人的な感想として、シャドウDOMの内側のHTMLがインデックスされるまでに時間がかかった印象を受けました。最初、シャドウDOMの外側だけインデックスされていたので、内側はインデックスされないのかと思いましたが、後日シャドウDOMの内側もインデックスされました。

勝手な推測ですが、GoogleはJavaScriptを動かさずに解析するBOTと、JavaScriptを動かすBOTが2つ走っているのではないでしょうか。負荷が高くなる後者は、負荷の分だけ動きが鈍いのでしょう。

パフォーマンス

カスタム要素はページが読み込まれた後でJavaScriptで内側のHTMLを描画します。そのため、ページが読み込まれたときに描画されている通常のHTMLよりも表示がどうしても遅くなります。

PageSpeed Insightsでパフォーマンスを上げるには相当頑張らないといけないでしょう。

対応ブラウザ

主要ブラウザをまとめるとこうなります。

ブラウザ 対応年
Chrome 2016年末~
Safari 2017年~
FireFox 2018年末~
Edge 2020年~

ChromeとSafariの対応が早かったので、現在利用されているスマホはほぼ問題ありません。FireFoxとEdgeの対応が遅かったため、古いPCを使っている人には表示できないことがあるかと思います。

スマホではCSSのグリッドレイアウトとほぼ同時期、PCではmax-content系と同時期です。

詳しくはCan I useで確認できます。

https://caniuse.com/mdn-api_customelementregistry

SEOを最重視するページには向いてない

以上のことから、SEOが重要となる企業HPやブログ、ECサイトにはまだおすすめできないことが分かります。

しかし、クローズドサイトや広告からの流入を想定しているランディングページでは十分に利用できます。あるいは、ページ内でSEOが重要でないコンテンツだけカスタム要素にするというのも問題ありません。

他には、次のようにSEOが重要な文章だけシャドウDOMの外に出すという方法があります。

<main-article>
こんにちは
・・・
</main-article>

おわりに

最近ではShopifyの公式テーマもカスタム要素が導入され始めました。今後どんどん普及していくと思います。

Discussion