HTMLはなぜ単体で「include」機能をサポートできないのか?
(frontendmasters.com)- 複数ページで同じheaderを繰り返したくないという要望は基本的なものだが、HTMLにはこれを直接処理するネイティブincludeタグがない
- 開発者はJavaScriptの
fetch、サーバー指示子、静的サイトジェネレーター、テンプレート言語、バックエンド言語、Web Componentsのような回避策で同じ問題を解決してきた <iframe>は純粋なHTMLに近い方法ではあるが、性能・アクセシビリティ・使い勝手の面でこの用途には合わず、全体的に不自然- CSSはCSSをimportでき、JavaScriptはJavaScriptをimportできるのに、HTMLはHTMLをimportできず、Webプラットフォームの一貫性が揺らいで見える
- preload scannerへの影響、非同期読み込みによるレイアウトの揺れ、ネスト・循環include、リクエスト増加、ドメイン制限、需要不足などが標準化の壁として残っている
繰り返されるHTML断片を再利用したいという基本的な要望
index.html、about.html、contact.htmlの3ページに同じheaderを入れる必要がある状況で問題が顕在化する- 同じコードを3回コピーするより、headerを一度作って複数ページでincludeしたくなるのは自然なこと
- ページが数千に増えれば、これは単なる利便性ではなくコード重複の防止の問題になる
すでに存在するさまざまな解決策
- HTML断片を取得して挿入する方法は、すでにさまざまなツールやレイヤーで可能
- JavaScriptは
fetchでHTMLを取得し、insertAdjacentElementで挿入できる - 古くからあるWebサーバー指示子であるServer Side Includesもある
- Jekyll includeのような静的サイトジェネレーター機能で処理できる
- gulp-includeのようなタスクランナーも使える
- Handlebars partialsのようなテンプレート言語は、たいていinclude機能を提供する
- PHPの
includeのように、バックエンド言語がHTMLを動的に生成することもできる - include専用のWeb Component方式も存在する
- JavaScriptは
<iframe>は技術的には純粋なHTMLだけで別のHTMLを取得する方法だが、この用途では性能・アクセシビリティ・使い勝手の問題が大きい- 強力な検索/置換機能を信じて、include自体を使わないという選択肢もある
肝心のHTML自体にはない
- 上記の方法はいずれも「HTMLタグ1つでHTMLを取得し、この位置に入れる」という方式ではない
<img>が画像を取得してその位置に入れるように、HTMLには「このHTMLを取得してここに入れる」という直接的な宣言型タグがない- ShopTalk ShowでもJake Archibald、Dave Rupertと同じ問いをめぐって議論が続いた
Webプラットフォームの既存の流れとずれる点
- Web標準とブラウザは、開発者がすでに繰り返し解決していたことをプラットフォーム機能として取り込む場合が多い
- 日付処理のためにサードパーティJavaScriptを使っていた流れにはTemporalが登場した
- フレームワークで処理していたページ遷移の要望にはView Transition APIが対応する
- 要素を安全に配置するために使われていたライブラリ領域にはCSS anchor positioningが入ってきた
- ほぼすべてのWebサイトがHTML断片の再利用を必要とし、それぞれ異なる非標準ツールを使っている状況では、HTML includeの不在は例外的な空白のように見える
HTML includeが標準になりにくい理由
- ブラウザとエコシステムの観点から、HTML includeにはいくつもの負担が伴う
- preload scannerを壊し、Web性能に悪影響を与える可能性がある
- 非同期方式でなければならないなら、読み込み中に画面が揺れたり跳ねたりする体験が生じる可能性がある
- HTMLの単純さや純粋さを損なう複雑性が生まれる可能性がある
- ネストしたincludeや循環includeの処理が難しい可能性がある
- Webホスティング事業者がリクエスト数の増加を理由に反対する可能性がある
- 画像・CSS・JavaScriptと異なり、HTMLは別ドメインから取得する際により厳格な制限が必要になる可能性がある
- このリストにない別の問題がある可能性もある
- 実際には、この機能への需要が大きくない可能性もある
- 確定的な答えというより、HTMLがなぜHTMLを直接includeできないのかを検討する理由探しに近い
1件のコメント
Hacker Newsの意見
HTMLは歴史的に SGML の応用であり、SGMLではincludeが可能だった
新しい「entity」を定義でき、「system」entityを作れば、後で参照して置換させることができた。SGMLは複雑だったため、HTMLを単純化しようとするさまざまな試みがあり、その過程でこの機能も外された
そのタグは、別のHTMLページを含めたり埋め込んだりするもののように見える。埋め込まれたHTMLページ: https://www.w3schools.com/tags/tag_object.asp
https://en.wikipedia.org/wiki/Billion_laughs_attack
90年代後半に入り込んだラビットホールで、いまだに抜け出せていない
Analog Science FictionのWebサイトのウェブマスターをしていたが、同じヘッダーとサイドバーを持つ静的ページを大量に作っていて気が狂いそうだった。調べているうちにApacheの サーバーサイドinclude を見つけ、DRYという言葉を知る前からDRYにできた。iframeで十分だという話もあるが、十分ではない。iframeはコンテンツのサイズに合わせて伸びず、サーバー側の解決策にはサーバーが必要になる。なぜ単純なクライアント側の方法があってはいけないのか分からないし、Web開発の多くの苛立ちを直している今、検討に値する問いだと思う
90年代半ばに友人と「Web stuff」を作り始めたときも、DRYの概念は自然に理解できた。当時のダイヤルアップISPはユーザーのWebスペースで
.htaccessの使用を禁止していなかったので、サーバーサイドincludeを有効にできたし、後にはCGIを有効にする方法も見つけた。Webサーバーのマシンを探索するために、Perlで初歩的なWebシェルまで書いた静的HTMLの動的importのような中核機能をHTMLに補ってくれる 10KBの小さなライブラリ だ
https://caniuse.com/iframe-seamless
フレームでずっと気になっていたのは、賢く振る舞いすぎる点だった。右クリックして再読み込みしたときに、フレームのHTMLだけを再読み込みしてほしかったわけではない。別個にキャッシュしたい意図は分かるが、フレームとキャッシュは別々の問題を解くべきなのに、両者を混ぜてしまったせいでどちらも中途半端になった。HTML includeは、できるだけ愚直に動作すべきだと思う。include位置にincludeテキストを貼り付け、ブラウザがその結果のテキストを受け取る方式でよい。すべてのページで同じナビゲーションを別途キャッシュしたいなら、キャッシュ属性を追加してその問題を独立に解けばよい。includeにもっと多くのことをさせるべきだと説得することはできるかもしれないが、愚直な動作 はバグではなく機能だ
この機能提案は HTML Imports と呼ばれ、Web Componentsの取り組みの一部として作られた
“HTML Imports are a way to include and reuse HTML documents in other HTML documents” という説明があり、計画文書は https://www.w3.org/TR/html-imports/ にある
しかし、こうした理由は実際には何も説明していない非理由に近い。この機能は20年にわたって求められ続けており、スクリプトやバックエンドエンジンなどで作られたあらゆるshim実装があるのに、需要が低いとは信じがたい。ベンダーの拒否も、なぜ既にあった実装まで取り下げるほど拒否したのかを説明していない。「セキュリティへの影響」も妙で、すでにscriptタグでクロスオリジンのHTMLを取得して
document.write()できるからだ。スクリプトがdocument.write()するのは問題なく、同じことをするHTMLタグはなぜ大問題になるのか疑問だ。Googleホームページを即席で複製するようなことは防ぐべきだというセキュリティ上の懸念は理解できるが、それはCORSで簡単に解けるように見える[1] https://frontendmasters.com/blog/seeking-an-answer-why-cant-...
HTMLは文書内の特定位置にimportされて表示されるべきだが、HTML ImportsはJavaScriptなしではそれができなかった。詳しくは https://github.com/whatwg/html/issues/2791#issuecomment-3112... を参照
記憶では、importした後にJavaScriptでテンプレートをインスタンス化する必要があり、単純なタグ1つで済む方式ではなかった
Netscape 4 には inflow layer としてこの機能があった
https://web.archive.org/web/19970630074729fw_/http://develop...
https://web.archive.org/web/19970630094813fw_/http://develop...
SRC属性を変更する動作はかなりクラッシュしやすく、機能もすぐに削除されたベータ版でいじっていた記憶があるが、正式版では消えていた
この機能の名前は トランスクルージョン(transclusion) である
https://en.wikipedia.org/wiki/Transclusion
Project Xanadu の一部で、もともとハイパーテキストの重要な機能と見なされていた。特に MediaWiki はトランスクルージョンを広範に使っており、時々 Wiki がハイパーテキストの最も純粋な形のように感じられる
https://en.wikipedia.org/wiki/Federated_Wiki
しかし、うまく普及することはなかった
Xanadu では、ある文書の一部の抜粋だけを別の文書に transclude できた。HTML でこれを行うには、CSS に対する答えが必要になる。特定の状況では、ホスト文書、ゲスト文書、ホスト内に埋め込まれたゲストの間で、どの属性を一貫させるべきかを判断して解決できるが、一般的な場合は不明確である。単純なタグ方式なら、ゲスト文書はホストが用意した CSS 環境の中で動作するように設計されなければならない。もう一つの単純な答えは Shadow DOM で、概ねゲストが文書の他の部分に影響を与えずに自分のスタイルを適用できるようにする。この場合でも、ホストがゲストを補正するためのスタイルを一部入れることはできると思う
昔のちゃんとした frameset がやろうとしていたのは、これではなかったかと思う。iframe ではなく、HTML 4 時代の frameset のことだ
少なくともサイズの自動拡張はうまくできたし、ユーザーが望むサイズに調整することもできた。フレームに対する批判は多かったが [1]、Java API ドキュメント [2] のように有用な場所では成功裏に使われていた。結局消えたのは、デザイナーにとって柔軟性があまりに足りなかったからだと思う。情報ページには十分だったが、無骨なスクロールバーと制約の多い画面分割のせいで、デザイナーの要求を満たせなかった。今ではモバイルでそのままの frameset はうまく動かないだろうから、復活させるには遅すぎる
[1] <https://www.nngroup.com/articles/why-frames-suck-most-of-the...> - ここに書かれている内容の多くは今では当てはまらず、フレームについて指摘されたすべての問題が、今日の Web にはもっとごちゃごちゃした形で存在しているのが興味深い
[2] <https://www.eeng.dcu.ie/~ee553/ee402notes/html/figures/JavaD...>
ディープリンクが不可能だったため、ブックマークや Google、あるいはそれ以前の検索エンジンから来た人はナビゲーションのないページに落とされ、それを JavaScript で回避しようとしたが、良い体験にはならなかった
「include」機能はサーバー側、つまりWebブラウザーの外で処理されるものと見なされている
HTMLはクライアント側のもので、実質的にはプログラミング言語ではなくマークアップ構文です。記事で述べられているように、この問題はすでに解決済みです。Webデザインの学生がPHPを学ぶときに最初に触れるのがincludeであり、ほとんどのCMSではincludeはテンプレートのpartialとなって、ドキュメントの冒頭で説明されます。HTMLだけでincludeを可能にする必要は特にありません。HTMLは表現形式であり、CSSとJSなしでは面白いことはしません
実際、HTMLにはframesとiframesという、もっと悪いバージョンがすでにあります。サーバーサイドincludeのクライアント側の対応物は、人々がHTMLで行うことに自然に合致します
おそらくカスタム要素で実装できるでしょうし、GitHubに似たようなリポジトリがなければむしろ驚きます
画像や画面下部のコンテンツのように、すでに非同期で読み込まれるコンテンツもあります。「HTMLはプログラミング言語ではなくマークアップ構文」という言い方はflamebaitに近く、ブラウザーエンジンごとに解釈される宣言型言語です
スタイリングのことを言っているなら、表現を担当するのはCSSです
選択肢は2つです。第一に、HTMLのパース時点、つまりDOM生成前に処理する方法ですが、includeのためにサーバーへの同期リクエストが必要になり、望ましくありません。第二に、DOM生成後に特定の要素がDOMに現れ、断片を非同期で読み込んだ後、そのDOM要素を外部断片で置き換える方法ですが、既存のDOM構造検証メカニズムを無力化します。ただしSciterエンジンでは第一の戦略で実装しました。SciterのHTMLは通常ローカルアプリのリソースやファイルシステムから来るため、追加の断片リクエストのコストは無視できます
https://docs.sciter.com/docs/HTML/html-include
HTML includeには、他の人たちが述べているようにいくつもの問題があります
main.htmlがchild/include1.htmlを含み、child/include1.html内にsrc="include2.html"リンクがある場合、ユーザーがクリックしたときにどこへ行くべきでしょうか。include2.htmlへ行くなら、名前からしてinclude用ページなので残りの部分が欠けているはずですし、main.htmlへ行くなら、今度はinclude1.htmlではなくinclude2.htmlを使うようにどう指定するのでしょうか。逆に、article1.html、article2.html、article3.htmlがそれぞれheader.html、footer.html、navi.htmlを含むようにもできますが、そうすると全記事の構造をグローバルに変更するには全記事を修正しなければなりません。全記事にcomments.htmlを追加したいなら、結局またテンプレートでページを生成したくなり、その時点でブラウザーincludeは不要になります。ヘッダーがタイトルを知る必要がある、あるいはフッターが前後リンクを知る必要があるといった問題も生じ、include同士で情報をやり取りする方法が必要になり、結局ページ生成に戻ることになります。考えてみると、HTML includeはほとんどの用途で実質的に役に立たない可能性が高いですここには2つの異なるユースケースが混在しています。1つは断片の再利用で、もう1つは埋め込み可能な独立した島です。後者はすでにiframeが処理しているので、前者だけを解決すればよいです
include2.htmlが残りを失うというincludeの論理は、他のincludeにもまったく同じように当てはまりますユーザーが
src="include.css"リンクをクリックすれば、めちゃくちゃになるでしょう。静的データ、画像、CSS、静的HTMLコンテンツには問題ないかもしれませんWHATWGにこれに関連する未解決のissueがあります。ブログ記事のコメント欄でも言及されています
Client side include feature for HTML
https://github.com/whatwg/html/issues/2791
HTMLにはincludeがありましたが、人気がなくなりました
実際の「include」という用語はXMLの機能であり、記事が望んでいるのもその機能です。HTMLにはXML以前に生まれた別のアプローチがあり、それがframesでした。framesはXML includeよりはるかに多くのことを行ったため、HTMLはその機能を別途得ませんでした。framesは誤用、セキュリティ、アクセシビリティなど多くの問題により人気を失いました
たまに使う分には今でも好きですが、ユーザーやブラウザーに渡す前にそれを評価するコンパイル段階が必要です