Chapter 02無料公開

HTML Imports

uhyo
uhyo
2021.12.02に更新

HTML Importsは、あるHTML文書から他の文書の内容を読み込んで使用するための機能です。一時期Google Chromeに実装されましたが、既に廃止されており、役に立ちません。

これは以前(2010年代前半頃)にWeb Components関連仕様の一部として宣伝されており、同期にはShadow DOMやCustom Elements、template要素といった現在も一線で活躍する仕様たちがいますが、HTML Importsは活躍しませんでした[1]

仕様: HTML Imports. W3C Working Draft 25 February 2016

HTML Importsの使用法

HTML Importsは、link要素のrel属性にimportを指定し、href属性にHTMLファイルのURLを指定することで使用します。

<link id="import-link" rel="import" href="./sub.html">

このようなlink要素を表すDOMノードはimportプロパティを持ち、そのimportプロパティはHTML Importsによって読み込まれたHTMLファイルの内容を表すHTMLDocumentオブジェクトを返します。

const link = document.getElementById("import-link");
const importedDocument = link.import;

console.log(importedDocument.querySelector("h1").textContent); // インポートされたHTMLファイル内にあるh1要素のテキストを表示

HTML Importsによりインポートされたドキュメントは、直接内容が画面に表示されるわけではなく、このようにJavaScriptを介して使用することができます。

HTML Importsの使い道

HTML Importsの大きな特徴の一つとして、インポートされたHTML文書(子分書と呼ぶことにしましょう)内にあるscript要素が実行されるということが挙げられます。ちなみに、子文書内のスクリプトであっても、documentは親文書を指すという特性があります[2]。これは、あるHTML文書を単独で開いた場合と、HTML Importsで読み込んだ場合で、スクリプトの挙動が全然異なるということを意味します。子文書は、HTML Importsで読み込まれることを前提に書かれる必要があります。

このようになっている理由としては、HTML Importsの主な使い道としてWeb Componentsの提供が考えられていたからだと考えられます。例えば、次のような子文書を用意しておけば、この文書をHTML Importsで読み込むだけで自動的にCustom Elementが定義され、親文書で使えるようになるでしょう。

<!doctype html>
<template id="my-element-template">
  <h1>My Element</h1>
  <p>This is my element</p>
</template>
<script>
class MyElement extends HTMLElement {
  constructor() {
    super();
    const template = document.currentScript.ownerDocument.getElementById("my-element-template");
    const clone = template.content.cloneNode(true);
    const shadowRoot = this.attachShadow({mode: "open"});
    shadowRoot.appendChild(clone);
  }
}
customElements.define("my-element", MyElement);
</script>

親文書側:

<!doctype html>
<link rel="import" href="my-element.html">
<body>
  <my-element></my-element>
</body>

親要素で<link rel="import">が出現した時点で子文書が読み込まれ、その中の<script>が実行されます。それにより親文書に対してmy-elementというカスタム要素が定義され、使用可能になります。

なぜHTML Importsは廃れたのか

これは多少役に立つ情報になってしまいますが、なぜHTML Importsは廃れてしまったのかもついでに説明します。

一言でまとめると、モダンなモジュール機構に乗っかれなかったからです。モダンなモジュール機構というのは、ES Modulesを前提としたスクリプティング・モジュールシステムのことです。

HTMLでは<script type="module">を使ってモジュールを実行することができます。ここでいうモジュールとは、importexportが使えるJavaScriptプログラムのことです。<script type="module"><script defer>などと同様に実行が遅延されHTML文書のパースを阻害しないという点で、パフォーマンスの観点からモダンな道具のひとつと見なされています。

一方で、<link rel="import">はそのようなモダンなデザインになっていなかったのです。インポートされた子文書がロードされ、その中の<script>が実行されるまで、親文書のロードが止まってしまいます。これは、deferが無い<script>と同様の挙動です。

後続のHTML Modules

HTML Importsの代わりに提案されたHTML Modulesでは、次のようにJavaScript内のimport宣言から子文書を読み込みます。

<script type="module">
  import "./my-element.html";
</script>

こうした場合、importされたHTML文書がやはり読み込まれ、子文書内の<script>が実行されるという点はHTML Importsと共通しています。

ちなみに、HTML ModulesではHTML文書が何をエクスポートするかをES Modulesの構文で宣言することができます[3]

<!-- 子文書 -->
<!doctype html>
<h1>子文書</h1>
<script type="module">
  export const h1 = import.meta.document.querySelector("h1");
</script>
<!-- 親文書 -->
<script type="module">
  import { h1 } from "./child.html";
</script>

こうなると、HTML文書を読み込むというよりはもはや、JavaScriptではなくHTMLでモジュールを書きましたといった風情です。この本の執筆時点ではHTML Modulesはまだ仕様の検討段階であり、import assertionsとの兼ね合いなどまだ変化があるでしょうが、大まかな方向性はこのようになっています。

まとめ

この章では、もう役に立たないHTML Importsを紹介しました。しかし、HTML文書をインポートするという概念自体はまだ生き残っています。今後に期待すると良いかもしれません。

脚注
  1. ただし、後続としてHTML Modulesという仕様が提案されています。これは、HTMLのlink要素ではなく、JavaScriptのimport宣言でHTMLファイルを読み込むという仕様です。 ↩︎

  2. これは子文書が独自のbrowsing contextを持たないことから従います。子文書は独自のdocumentを持ちますが独自のwindowを持たず、windowは親文書のものになります。したがって、グローバル変数のdocumentwindow.documentのことですから、親文書のDocumentオブジェクトが参照されます。自身(子文書)のDocumentを取得するには、document.currentScript.ownerDocumentとします。 ↩︎

  3. import.meta.document は子文書のDocumentを表します。グローバルなdocumentが親文書のDocumentを表すという点はHTML Modulesと変わりません。 ↩︎