ブラウザがあらゆるHTMLストリームを、たとえ不正なものであっても、いかにして決定論的なDOMツリーへと変換するかを理解します。
このレッスンを Kodokon で開くXMLとは異なり、HTMLには致命的なエラーというものが存在しません。WHATWG仕様は、どんな入力ストリーム、たとえ不正なものであっても、それがどのようにDOMツリーになるべきかをバイト単位で定義しています。パーサーは2つの段階で動作します。まずトークナイザーがストリームをトークン(タグ、テキスト、コメント)に切り分け、次にツリービルダーが、挿入モード(in body、in tableなど)と呼ばれる状態を持つ状態機械に従ってそれらを挿入します。あらゆる「パースエラー」には標準化された回復方法があり、仕様に準拠した2つのブラウザはまったく同じDOMを生成します。
<table>
<tr><td>Cell</td></tr>
</table>パーサーは暗黙のタグを挿入します。行の周りに<tbody>が現れ、<li>、<p>、<option>は特定の開始タグの前で自動的に閉じられます。さらに過激なことに、<table>の内部で許可されていない場所に見つかったコンテンツはそこから引き抜かれ、その直前に再挿入されます。これはフォスターペアレンティングと呼ばれる仕組みです。あなたのCSSセレクターやスクリプトは、常にソースではなく、修正されたDOMに対して動作します。
const html =
"<table><div>out</div><tr><td>ok</td></tr></table>";
const doc = new DOMParser()
.parseFromString(html, "text/html");
console.log(doc.body.innerHTML);
// <div>out</div><table>...</table>誤ってネストされた整形タグは、仕様の中でも最も厄介な部分の一つである採用エージェンシーアルゴリズムを引き起こします。パーサーはアクティブな整形要素をクローンして再配置し、正しくネストされたツリーを生成します。以下の交差したマークアップがその結果を示しています。
<p>
<b>One <i>two</b> three</i>
</p><b>One <i>two</i></b><i> three</i> - iは2つの要素に分割されます。パーサーはストリーミングパーサーです。バイトが到着するにつれてツリーを構築していきます。同期的な<script>はそれを一時停止させます。スクリプトはdocument.writeを呼び出してストリームにトークンを注入できるからです。しかしプリロードスキャナーは残りのHTMLを走査し続け、画像、スタイルシート、スクリプトをプリロードします。パースが完了した後に呼び出されたdocument.writeはdocument.openを引き起こし、ページ全体を消去します。これはプラットフォームの最も古い罠の一つです。
<table><div>Out</div><tr><td>A</td></tr></table>の中の<div>はどうなりますか?<td>セルで包まれる<p><div>X</div></p>はどんなDOMを生成しますか?<p></p><div>X</div><p></p>: divが最初のpを閉じ、宙に浮いた</p>が2つ目の空のpを作る<p><div>X</div></p>、そのまま保持される<div><p>X</p></div>、パーサーがネストを反転させる