HTMLにおける自己終了タグの使用、歴史的背景、使用に対する賛否両論
Post on:2023年7月20日
HTMLやCSSのデモページでコードを見ると、HTMLに自己終了タグ(/>
)が使用されているのを見かけたことはありませんか?
HTMLにおける自己終了タグの使用、自己終了タグの歴史的背景、自己終了タグの使用に対する賛否両論について紹介します。
The case against self-closing tags in HTML
by Jake Archibald (@jaffathecake)
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
/>
について話をしましょう。
1 2 3 |
<input type="text" /> <br /> <img src="…" /> |
私のブログでこの構文を目にすることがあるのはPrettierの機能のためで、私はPrettierがとても気に入っています。ただし、/>
が良いことだとは思いません。
まず最初に、自己終了タグが誕生した歴史的背景を見てましょう。
/>の歴史的背景
XHTMLの登場
1990年後半から2000年代前半にかけて、W3CはXMLに力を入れ、HTML構文に代わるべきものだと考えました。
これには十分な理由がありました。当時はHTMLのパーサーがなかったため、4つのブラウザエンジンが同じHTMLドキュメントを4通りの方法で解釈することがよくありました。一方、XMLには完全に定義されたパーサーがありました。
しかし、これを一度に行うのは大きな変更であったため、2000年にXHTML 1.0が勧告され、既存のHTMLパーサーとXMLパーサーの両方と互換性のある方法でHTMLを記述することが提案されました。
つまり、こうなりました。
1 2 3 4 5 6 7 |
<HTML LANG="en"> <!-- これの代りに: --> <!-- こう書きます: --> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
知らない人はびっくりするかもしれません。
1 2 3 4 5 |
<option value=foo selected>…</option> <!-- これの代りに: --> <!-- こう書きます: --> <option value="foo" selected="selected">…</option> |
XMLでは属性に値が必要であり、二重引用符で囲む必要があるためです。
さらに、こう書きます。
1 2 3 4 5 |
<img src="…"> <!-- これの代りに: --> <!-- こう書きます: --> <img src="…" /> |
XMLではタグを明示的に閉じる必要があり、XMLには自己終了タグの省略表現/>
がありました。
XMLでは一般的に/
の前にスペースを入れずに<this/>
のような形式になりますが、Netscape Navigator 4では/
が属性の直後に続く<input type="text"/>
に対応できなかったため、仕様では/
の前にスペースを入れることを推奨しました。
だが、ブラウザは無視した
これらのルールはXMLパーサーのためのもので、ドキュメントはHTMLとして提供されていたため(サイトをapplication/xhtml+xml
として提供したいた人であれば私に言う必要はありません)、これらの構文上の「おまけ」は無視されました。
<option selected="selected">
の場合は、値は無視されるため、<option selected="">
も<option selected="false">
(false
は無視される)と同様に機能しますが、「一貫性」のために属性名を繰り返すことが良いアイデアだと判断されました。
属性を引用符で囲むのを忘れた場合でもブラウザは文句を言わず、ページをレンダリング作業を続行しました。
タグを/>
で終えると、ブラウザはそれをパーサーのエラーとみなして無視しました。問題視となるのは、そこからです。
1 2 3 4 5 6 7 8 9 |
<br /> brは閉じています。このテキストはbr内にはありません。 しかし: <br> brは閉じています。このテキストはbr内にはありません。 ここで混乱が生じます: <div /> divは開いています。このテキストはdiv内にあります。 |
XMLでは<div />
は自己終了タグですが、HTMLではそうではありません。HTMLではbr
を閉じるのは/>
ではなく、「br
」です。これは子を持つことができない要素の特別なリストにあるため、自動的に閉じられます。「div
」はそのリストにないため、<div />
は閉じられません。
XHTMLの終焉
XHTMLの移行フェーズは、XHTML 1.1で終了しました。この時点で仕様はドキュメントがXMLとして提供され、パースされることを要求しました。XMLのパースルールは、無効な構文が発生した場合を除き、明確に定義されていました。ブラウザができる最善のことは、エラーページを表示することだけです。そうでない場合はブラウザが何かを作り上げ、それぞれが異なる振る舞いをすることに戻ってしまいます。それに対して、ブラウザはノーサンキューと言いました。
まぁ、正確にはノーとは言いませんでした。ブラウザは一応サポートし、今日もサポートしています。application/xhtml+xml
として提供される有効なXHTMLドキュメントと無効なドキュメントを紹介しておきます。しかし、ブラウザはこれを未来とは考えていませんでした。
ここで問題です。
地元の病院の診療時間を調べるためにWebサイトにアクセスした場合、どのブラウザが最適ですか? 診療時間を表示するブラウザ? それともXMLのエラーメッセージを表示するブラウザ?
ブラウザの優れた点の一つはエラーに強いことで、ブラウザはそれを手放すことに興味はありませんでした。
XHTMLは最終的に放棄されました。なぜなら、ブラウザがより満足できる新しいものが登場したからです。
HTML5の登場
HTML5が登場したのは2008年で、その主要なものの一つがパース仕様でした。また、XMLの仕様とは異なり、奇妙で無効なマークアップに遭遇したときにブラウザが何をすべきかも決まっていました。
XHTMLで導入されたXMLの要件はすべて廃止され、当時存在したHTMLパーサーの緩さに傾倒しました。たとえば、/>
は無視するだけです。
SVG-in-HTMLの登場
2010年初頭、HTMLに<svg>
を含める機能が仕様化され、ブラウザに登場し始めました。
1 2 3 4 5 |
<div> <svg viewBox="0 0 100 100"> <circle cx="50" cy="50" r="50" /> </svg> </div> |
SVGはXML形式ですが、HTMLドキュメントに埋め込まれている場合、HTMLパーサーによって解析されます。ただし、コピーペーストされたSVGコンテンツとの互換性を高めるために、HTMLパーサーが<svg>
タグ内にある場合は外部コンテンツモードに切り替わります。つまり、/>
は実際に意味があります。
1 2 3 4 5 6 7 8 |
<div/> divは開いています。 このテキストはdiv内にあります。 しかし: <svg> <g><text>This is inside the group</text></g> <g/><text>This is outside the group</text> </svg> |
MathMLのような他の外部コンテンツも同じように振る舞います。
そして、これが現在の状況です。/>
はHTMLドキュメントではほとんど意味がなく、外部コンテンツでは例外となります。
/>に対する賛否両論
わたし達はXHTMLの要件のほとんどを破棄しましたが、この自己終了タグは10年以上前に放棄された仕様の名残であるにかかわらず、根強く残っています。/>
の前にスペースまで含めていますが、これはかなり前のブラウザ(Netscape)との互換性のために追加されたものです。
私は、これは混乱させる過去の遺物だと思います。また、Prettierのようなツールがこれを推し進めるべきだとも思いません。
この場で私のツイートにいただいた意見に答えてみます。
自己終了タグにより読みやすくなり、新人にも役立ちます。どのタグが自己で閉じるかを覚える必要はありません。
外部コンテンツ以外では、自己終了する要素は常に自己で終了します。それ以外は閉じません。
1 2 3 4 5 6 7 |
<input />このテキストはinputの外側にあります。 <input>このテキストはinputの外側にあります。</input> <div>このテキストはdivの内側にあります。</div> <div />このテキストはdivの内側にあります。 |
上記の/>
は、単に何もしません。<input />
だと許容され、<div />
だと許容されないことを知る唯一の方法は、どの要素が自己で閉じるかを学び、覚えておくことです。最悪なことですが、それが現実です。
しかし、/>
は役に立つために機能する必要がありますか? コードのコメントは機能しません。/>
のように表示であり、誤解を招くかもしれませんが、コードのコメントを削除するための論拠にはなりません。さらに悪いことに、外部コンテンツに関するルールのせいで、常にコメントのように振る舞うとは限りません。
そして、新人にとってもよくないと思います。<img src = "...">
を初めて見たと想像してみてください。他の要素とは異なり、閉じタグはありません。デバッガーやバリデーターは、閉じタグがないことについて文句を言いません。この要素については、学習する必要があることを示唆しています。閉じる必要はなく、自己終了であり、この動作に特化しています。
そして今、あなたが<img src = "... /">
を初めて見たと想像してみてください。この新しい構文を検索し、タグが自己終了であることを学びます。この時点であなたは、なぜ<iframe />
も自己終了なのか? または<img src = "..."></img>
は有効なのか? と考えるかもしれません。MDNが初心者向けのドキュメントで自己終了タグを使用しているのは非常に悲しいことです。
JSXと整合性があります。
JSXとHTMLは、異なるフォーマットです。互いに一貫性はありません。一貫性があるように装うのは誤解を招きます。
下記のHTMLは「Hello world」とレンダリングします。
1 2 3 4 |
<div> <span>Hello</span> <span>world</span> </div> |
下記のJSXは「Helloworld」とレンダリングします。フォーマットは異なって機能します。
1 2 3 4 5 6 |
const Component = () => ( <div> <span>Hello</span> <span>world</span> </div> ); |
下記のHTMLを使用すると、テキストはdiv
の内側にあります。
1 2 3 4 |
<main> <div /> Hello </main> |
下記のJSXを使用すると、テキストはdiv
の外側にあります。別のシステムです!
1 2 3 4 5 6 |
const Component = () => ( <main> <div /> Hello </main> ); |
下記のHTMLは、classname
属性を持つdiv
を生成します。
1 |
<div classname="foo"></div> |
下記のJSXは、class
属性を持つdiv
を生成します。これはJSXというよりReactの問題ですが、JSXを使用する方法として一般的なものです。
1 |
const Component = () => <div classname="foo"></div>; |
一貫性について議論は必要ないと思います。視覚的な類似性はありますが、JSXとHTMLは異なるフォーマットであり、異なる動作をするものです。
XMLパーサーでHTMLをパースできることを意味します。
ある種の純粋主義者と呼ばれるかもしれませんが、HTMLドキュメントをパースしたければHTMLパーサーを使用します。YAMLパーサーでパースできるようにJSONを書こうとはしないので、HTMLとXMLで同じことをする理由が分かりません。
ほぼすべての言語に対して、多くの素晴らしいHTMLパーシングライブラリが存在します。そして、パーサーが指定されているので、結果は一貫しています。
マークアップのパースが速くなります。
これは何度か耳にしたことがあります。提供する情報が多ければ多いほど、プロセッサーはより適切に最適化できるという仮定からきていると思います。しかし、この2つを比較してみましょう。
<br>
のパース
<
: おお、タグが新しく追加されたぞ!br
: これで、どの要素を作成するか分かるぞ。>
: タグの終わりか。br
は自己終了要素であるため、開いている要素のスタックには入りません。
<br/>
のパース
<
: おお、タグが新しく追加されたぞ!br
: これで、どの要素を作成するか分かるぞ。/
: これは無視。>
: タグの終わりか。br
は自己終了要素であるため、開いている要素のスタックには入りません。
つまり、技術的には<br/>
は余分なジャンクを含んでいるため、パースが遅くなります。致命的に遅くなることはありませんが、速くなることはありません。
見た目がいい。
確かに、そういう意見もあるかもしれません。しかし、それは主観的なものです。私は初めてコードベースで作業したときは、/
を見苦しいと思いましたが、すぐに慣れました。そして見逃すことにも慣れました。
見た目が目的であれば、<input type="text" 🛑>
はどうでしょう。
冗談はおいといて、真面目な話、構文がいかに誤解を招きやすいかを考えると、美学は後回しにすべきです。
Prettierには一貫性がない
Prettierの「我が道を行く」アプローチは尊重していますが、ここでは一貫性がないと思います。
<br>
を<br />
に変更しますが、<div />
では何も起こりません。実際に<div/>
を指定すると、<div />
に変更します。
Prettierではパーサーにとって無意味な場合には/>
を削除するか、/>
が誤解を招く場合には修正する必要があると思います。下記のように。
1 2 |
<div /> Hello |
下記のように変更する必要があります。
1 |
<div>Hello</div> |
閉じられていないタグを処理する方法と同様です。
それとも、HTMLは閉じタグをどこでも使用できるのでしょうか?
問題の肝となる部分は、同じHTMLドキュメント内で/>
が無視されることもあれば、無視されないこともあることです。
/>
が常に意味を持つようにパースのルールを切り替えるオプションはあるでしょうか? たとえば、<div/>
は実際には自己終了します。これについては指摘しましたが、既存のライブラリ、特にセキュリティに敏感なライブラリとの互換性がないため、実行できないのではないかと思われます。
元記事は、GitHubでも公開されています。
GitHub
sponsors