Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第7章 壊れた HTML をどう扱うべきか

この章では、HTML が「エラーを出さない言語」なのではなく、「閲覧を止めないためにエラー回復を仕様化している言語」だと見直します。ゴールは、壊れた HTML がブラウザで何となく動く場面を見たときに、「エラーがない」のではなく「エラーはあるが、回復して読み続けている」と説明できるようになることです。

前章では、p 要素の暗黙終了という個別規則を見ました。この章ではそこから一段引いて、そうした規則を含む HTML 全体の回復方針を扱います。次章では、その結果として View Source と DevTools がなぜ違って見えるのかを扱います。

7.1 HTML は「エラーがない」のではなく「止まらない」方向を選んだ

HTML について、「多少壊れていても動く」「ブラウザはエラーを出さない」と言われることがあります。現象としては半分当たっていますが、説明としては雑です。

実際には、HTML にはパースエラーがあります。これは、ブラウザが HTML を読み解く途中で「仕様どおりではない入力だ」と判断する種類の問題です。タグの入れ子がおかしい、閉じタグが欠けている、置けない要素が置かれている。そうした入力は、仕様の観点では問題のある入力です。ただし HTML は、その問題が見つかった時点で閲覧を止めるのではなく、できるかぎり読める文書構造へ回復しながら処理を進めます。

つまり、HTML の特徴は「エラーが存在しない」ことではありません。エラーがあっても、利用者の閲覧を止めないことを優先した ところにあります。この違いを外すと、後の章で出てくる補完や DevTools の挙動が全部「適当に直している」ように見えてしまいます。

7.2 壊れた入力でもブラウザは読み進める

例として、段落の中に div を入れた前章のケースを思い出してください。

<p>前置き
<div>本文</div>

これは、文書構造として素直ではありません。それでもブラウザは処理を止めず、div の前で p を閉じる形へ回復して読み進めます。

同じことは tbody の補完でも起きています。

<table>
  <tr><td>A</td></tr>
</table>

ソースに tbody がなくても、ブラウザは表モデルに必要な階層を立てて DOM を作ります。ここで重要なのは、これらが別々の小技ではないことです。どちらも「入力は不完全でも、内部の文書構造はなるべく整えて処理を続ける」という同じ方針から出ています。

もう 1 つ、入れ子の崩れた例も見ておきます。

<ul>
  <li>項目 A
  <div>補足</div>
</ul>

この種の入力でも、ブラウザは「どこまでが li の内容か」「ul の子として何をぶら下げるか」を処理しながら、読み進められる形へ寄せていきます。ここで起きているのも、「不正だから停止する」ではなく、「いまのトークン列から、どの文書構造を組み立てるのがもっともましか」を選ぶ動きです。

見方を変えると、ここでブラウザがしているのは、著者の意図を推測して甘く採点することではありません。いま受け取っているトークン列を、既存のページをできるだけ壊さずに読める木構造へ落とし込むことです。第4章で見た補完も、第5章と第6章で見た挿入や暗黙終了も、この回復方針の具体的な現れとして並べて読むほうが実態に近いです。

HTML パーサーは、壊れた入力に出会うたびに「これはもう読めないので中断する」ではなく、「どの形に直せば、もっとも壊れにくく読み続けられるか」を選びます。ここでいうパーサーは、HTML の文字列を読んで文書構造へ変える読み取り器だと思えば十分です。Web が大量の既存ページを抱えたまま成り立つためには、この性質が必要でした。

7.3 XML のように停止しないのは Web の条件が違ったからである

ここで自然に出る疑問は、「不正な入力なら、その場でエラーにして止めたほうがきれいなのではないか」です。たしかに、厳格な形式ではその考え方が筋の通った場面があります。

しかし Web では、停止のコストが非常に高い。著者が 1 つタグを閉じ忘れただけで利用者がページ全体を読めなくなれば、公開文書空間としては脆すぎます。しかも現実の Web には、手書きのページ、古いツールが出力したページ、完全ではないテンプレートなどが大量に存在します。それらを全部「不正だから閲覧不可」としていたら、Web はここまで広がりませんでした。

だから HTML は、XML 的な厳格停止ではなく、回復しながら進む方向を選びました。これは「正しさを諦めた」というより、「公開された文書を読めるように保つ」という Web の責務を優先した結果です。第3章で見た「完成度よりも届いて読めることを優先する」という性格は、ここでも生きています。

7.4 エラー回復と「雑に書いてよい」はまったく別である

ここで最も危ない誤解を潰しておきます。ブラウザが回復してくれるからといって、壊れた HTML を気にしなくてよいわけではありません。

回復は、あくまで利用者の閲覧を守るための仕組みです。著者や開発者への免罪符ではありません。壊れた入力に依存すると、ブラウザが最終的にどう解釈したかを毎回追いかける必要があり、ソースの可読性も保守性も下がります。CSS や JavaScript の挙動が想定とずれたときに、原因が仕様どおりの回復にあるのか、単なる実装ミスにあるのかも見分けにくくなります。

第5章と第6章で見たように、tbody の挿入も p の暗黙終了も、最終的には DOM の形を変えます。つまり「壊れていても表示された」で済ませると、あとでスタイルやスクリプトの調査コストとして跳ね返ってきます。HTML が回復可能であることと、壊れた入力に寄りかかることは、はっきり分けて考える必要があります。

7.5 実務では validator と DevTools をどう使うか

では実務ではどう扱えばよいのでしょうか。基本は 2 つです。1 つは、HTML が回復することを前提にせず、まず入力自体を点検すること。もう 1 つは、すでに起きた現象については、ブラウザがどう回復したかを観察することです。

入力の点検には validator が向いています。validator は、HTML が仕様上おかしくないかを検査する道具です。たとえば Nu Html Checker のようなツールを使うと、仕様上問題のあるマークアップを機械的に拾えます。これは「ブラウザが表示できるか」ではなく、「仕様上どう問題があるか」を見る道具です。

たとえば、閉じタグの抜けや不適切な入れ子があったとき、validator は「この要素はここに置けない」「この終了タグは不要である」「この開始タグに対応する終了タグがない」といった種類の問題を指摘します。そこで初めて、「ブラウザでは見えているが、入力は壊れている」という状態を、感覚ではなく仕様の言葉で捉えられます。表示結果だけを見ていると正常に見えるページでも、入力としては修正すべき箇所があると切り分けられるわけです。

一方で、いま画面に何が起きているかを調べるには DevTools が向いています。なぜなら、ブラウザが実際に扱っているのは回復後の DOM だからです。つまり、validator は入力の問題を見つけるため、DevTools は回復後の現実を観察するために使います。両者の役割は重なっていません。

この切り分けができると、「壊れていても動いたから問題ない」という雑な判断を避けやすくなります。仕様上まずいのか、ブラウザがどう回復したのか、その結果 DOM がどうなったのかを分けて考えられるからです。

HTML はエラーを無視しているのではありません。壊れた入力に対しても、閲覧を止めないために回復しながら処理を続けるよう設計されています。この見方を持つと、tbody の補完や p の暗黙終了は、気まぐれな例外ではなく、同じ回復方針の一部として読めます。次章では、その回復後の現実をどこで見ているかという観点から、View Source と DevTools がなぜ一致しないのかを見ます。

参考資料