DOM(Document Object Model)は、ブラウザが HTML 文書を JavaScript から読み取り、変更できるオブジェクトの木として表したものです。DOM を理解すると、querySelector やイベント処理だけでなく、React や Vue が「再レンダー後に何をしているのか」も具体的に捉えられます。
重要なのは、DOM は HTML の文字列そのものでも、画面に描かれたピクセルそのものでもないことです。HTML を解析した結果としてブラウザ内に存在し、UI を表す状態の一つです。JavaScript は DOM を変更でき、ブラウザはその変更を必要に応じて描画へ反映します。
HTML、DOM、画面は別のもの
次の HTML を考えます。
<main>
<h2>注文一覧</h2>
<button disabled>購入する</button>
</main>
ブラウザはこれを解析し、Document を根とするオブジェクトの木を作ります。main や button は Element、注文一覧 はテキストを表す Text ノードです。disabled のような属性も、要素に属する情報として扱われます。
一方、画面に何色で何ピクセルに描くかは CSS、フォント、画面サイズなども踏まえて決まります。同じ DOM でも CSS を変えれば画面は変わり、逆に CSS の変更だけなら DOM の木は変わりません。この区別は、表示の遅さを調査するときや、DOM を直接操作するコードを設計するときの出発点になります。
| 層 | 主な役割 | 例 |
|---|---|---|
| HTML | 文書の初期構造を記述する | <button disabled> |
| DOM | ブラウザ内で操作できる文書モデル | button.disabled = false |
| CSSOM | CSS を解析したスタイル規則のモデル | display: grid |
| レンダリング結果 | DOM と CSS から計算される見た目 | ボタンの位置、文字の色 |
Node と Element を区別する
DOM API では多くのものを Node と呼びます。Document、Element、Text、コメントはいずれも Node です。Element は HTML 要素を表す Node の一種で、classList、children、matches() のような要素向け API を持ちます。
この違いは、子を走査するときに現れます。childNodes は改行や空白を含むテキストノードも返しますが、children は要素だけを返します。
const list = document.querySelector('ul');
console.log(list.childNodes); // Text も含む
console.log(list.children); // li などの Element だけ
インデントされた HTML では、開始タグの直後の改行と空白も Text ノードになります。「最初の子が li のはず」と firstChild に依存すると壊れやすい理由です。要素が欲しいなら firstElementChild や children を選びます。
属性とプロパティは常に同じではない
HTML に書かれた属性は初期値を表します。DOM 上のプロパティは、現在の状態を表す場合があります。入力欄は典型例です。
<input id="email" value="initial@example.com">
const input = document.querySelector('#email');
input.getAttribute('value'); // "initial@example.com"
input.value; // ユーザー入力を反映する現在値
input.value = 'next@example.com';
ユーザーが入力した後も getAttribute('value') が初期の文字列を返すことがあります。フォームの現在値を読み書きするなら、属性ではなく value、checked、disabled などの対応するプロパティを使う、と覚えると安全です。属性を文字列として扱う setAttribute が適する場合と、意味を持つプロパティを使うべき場合を分けましょう。
DOM は生きたアプリケーション状態にしない
DOM は変更可能なので、アプリケーションの状態をそこに保存したくなることがあります。たとえば、表示中の件数を画面から読み戻して計算する実装です。しかし、DOM は表示のための派生結果として扱う方が保守しやすくなります。
データ・状態 → UI を決定する処理 → DOM
正本となる配列、選択状態、通信状態を JavaScript やフレームワークの state に置き、DOM はその結果として更新します。DOM にしか値がないと、初期表示、別の処理、テスト、サーバー描画との同期を追いにくくなります。例外は、フォーカス位置、選択範囲、要素サイズのようにブラウザが保持する情報を読む場面です。その場合も、DOM が外部システムとの境界であることを意識します。
DevTools で観察する
Elements パネルは現在の DOM を表示します。これは最初に取得した HTML の表示ではありません。JavaScript による変更や、ブラウザによる HTML の補正後の状態です。たとえば不正な入れ子を含む HTML は、解析時に想定と異なる木へ修正されることがあります。
Console で要素を選び、$0.children や $0.outerHTML を確認すると、コードがどのノードを扱っているかを確かめられます。DOM 操作の不具合では、まず「選んだ要素は正しいか」「イベント後に DOM はどう変わったか」を観察すると、推測より早く原因へ近づけます。
まとめ
DOM は HTML をブラウザが扱うためのオブジェクトの木であり、HTML の文字列や最終的な画面とは別の層です。Node と Element、属性と現在値の違いを押さえると、DOM API の振る舞いを正確に読めます。次は要素の選択・生成・更新を学び、その変更が描画へ及ぼす影響を理解しましょう。