DOM(ドキュメントオブジェクトモデル)
ブラウザが HTML を読み込んでメモリ上に作る“ツリー構造のオブジェクト”。JavaScript はこれを通してページを取得・生成・操作する。
- 1.DOM は HTML を木構造のオブジェクトとして表現したもの。HTML テキストそのものではなく、ブラウザがメモリ上に組み立てた“生きた”モデル。
- 2.JavaScript は DOM API(getElementById / querySelector / createElement / addEventListener など)でノードを取得・生成・操作・イベント監視する。
- 3.DOM をいじると見た目の再計算(リフロー)と再描画(リペイント)が走る。仮想 DOM は、この実 DOM 操作を減らすための“差分計算用のコピー”。
HTML と DOM は別物
ここが最初のつまずきどころです。.html ファイルに書いたタグは、ただの テキスト。ブラウザがそれを読み込み、解析(パース)して、メモリ上にオブジェクトの木を組み立てたものが DOM です。
- HTML: ディスク上・ネットワーク上の静的な文字列(ソース)
- DOM: ブラウザが組み立てた、JavaScript から触れる生きたツリー
だから JavaScript で要素を足したり消したりしても、元の .html ファイルは書き換わりません。変わるのはブラウザが持っている DOM であり、画面はその DOM を映したものです。開発者ツールの「Elements」タブで見えているのは HTML ソースではなく、現在の DOM の状態です。
<!-- これは HTML(ただのテキスト) -->
<ul id="list">
<li>りんご</li>
<li>みかん</li>
</ul>
このソースは、ブラウザの中でおおよそ次のツリーになります。
document
└─ html
└─ body
└─ ul#list
├─ li ─ "りんご"(テキストノード)
└─ li ─ "みかん"(テキストノード)
ノードと要素
DOM の木を構成する一つ一つが ノード(Node) です。すべてのノードが「タグ」とは限りません。
| 種類 | 正体 | 例 |
|---|---|---|
| Document | ツリー全体の入口 | window.document |
| 要素ノード(Element) | HTML タグに対応 | <div> / <li> / <a> |
| テキストノード(Text) | タグの中の文字列 | "りんご" |
| 属性 | 要素が持つ設定値 | id="list" / href="..." |
| コメントノード | <!-- ... --> | ソース中のコメント |
要素(Element)はノードの一種にすぎません。タグの間の改行やスペースも テキストノード として木に入ります。そのため element.childNodes には空白テキストが混ざりますが、element.children は要素だけを返します。「子要素を回したいのに空テキストが来る」事故はここが原因です。
JavaScript からの操作
DOM API の使い方は、大きく 取得 → 生成 → 変更 → イベント の流れで覚えると整理できます。
取得(探す)
// id で 1 つ取る(最速・最も明示的)
const list = document.getElementById('list');
// CSS セレクタで取る(柔軟。最初の 1 つ / 全部)
const first = document.querySelector('#list li');
const items = document.querySelectorAll('#list li'); // NodeList(複数)
生成・変更(作る・書き換える)
const li = document.createElement('li'); // 新しい要素を作る
li.textContent = 'ばなな'; // 中身のテキストを設定
li.classList.add('fruit'); // クラスを付与
list.appendChild(li); // ツリーにぶら下げる → 画面に反映
element.innerHTML = userInput のように、ユーザー由来の文字列をそのまま HTML として流し込むのは危険です。<script> や onerror 付きタグが混ざると実行され、XSS(クロスサイトスクリプティング) になります。表示するだけなら textContent を使う、どうしても HTML を入れるならサニタイズする、が原則です(→ Web 認証とセキュリティ)。
イベント(反応する)
// クリックされたら反応する
const btn = document.querySelector('#add');
btn.addEventListener('click', (event) => {
const li = document.createElement('li');
li.textContent = '追加された項目';
list.appendChild(li);
});
イベントは、クリックされた要素から 親へ向かって伝わっていく(バブリング) のが基本です。これを使うと、子要素一つ一つにリスナーを付けなくても、親で一括して受け取る「イベント委譲」が書けます。
// li が何個増えても、親の ul で一括処理できる
list.addEventListener('click', (event) => {
if (event.target.matches('li')) {
event.target.classList.toggle('done');
}
});
リフローとリペイント
DOM や CSS を変更すると、ブラウザは画面を作り直します。ここに性能の落とし穴があります。
| 用語 | 何が起きる | きっかけの例 | 重さ |
|---|---|---|---|
| リフロー(リレイアウト) | 要素の位置・大きさを再計算 | 要素の追加/削除、幅・高さ変更、offsetTop の読み取り | 重い |
| リペイント | 色など見た目だけを描き直す | color / background / visibility の変更 | 中 |
| 合成(コンポジット) | GPU でレイヤーを重ねるだけ | transform / opacity の変更 | 軽い |
ループの中で 1 件ずつ appendChild するとリフローが何度も走りがちです。DocumentFragment に組み立ててから一度だけ本体へ差し込む、あるいはアニメは位置(top/left)ではなく transform / opacity を使う、といった工夫でリフローを減らせます。
const frag = document.createDocumentFragment();
for (const name of ['ぶどう', 'もも', 'なし']) {
const li = document.createElement('li');
li.textContent = name;
frag.appendChild(li); // ここではまだ画面に触れない
}
list.appendChild(frag); // 反映は 1 回だけ
offsetWidth などの位置・サイズの読み取りは、それまでの変更を確定させるため強制リフローを引き起こします。書き込み→読み取り→書き込み→読み取り… を交互に繰り返すと、毎回リフローが走って一気に重くなります(レイアウトスラッシング)。読み取りはまとめて先に、書き込みはまとめて後にが定石です。
仮想 DOM との違い
React などで聞く 仮想 DOM(Virtual DOM) は、新しい技術ではなく実 DOM を効率よく更新するための仕組みです。
- まず UI の状態を JavaScript のオブジェクト(軽いコピー) として持つ
- 変更前後のコピーを比べて、差分(変わった所だけ) を計算する(差分検出 / diff)
- その差分だけを実 DOM に反映する
| 観点 | 実 DOM(DOM API 直接) | 仮想 DOM |
|---|---|---|
| 正体 | ブラウザが持つ本物のツリー | メモリ上の軽量なコピー |
| 更新の単位 | 命令した操作がそのまま実行 | 差分を計算してから最小限を反映 |
| 手書きの手間 | どこを変えるか自分で管理 | 状態を書けばライブラリが面倒を見る |
| 速さの勘所 | 雑にいじるとリフロー多発 | 実 DOM への接触回数を抑えやすい |
最終的に画面を描くのは必ず実 DOMです。仮想 DOM は「無駄な実 DOM 操作を減らす」ための層であって、それ自体が魔法のように速いわけではありません。実 DOM を丁寧に最小限だけ触れば、素の DOM API でも十分速く書けます。レンダリングの全体像は ブラウザのレンダリングの仕組み も参照してください。
まとめ
- DOM は HTML を木構造のオブジェクトにしたもの。HTML テキストそのものではなく、ブラウザがメモリ上に組み立てた生きたモデル。
- JavaScript は DOM API で 取得・生成・変更・イベントを行う。
textContentとinnerHTMLの使い分け、イベントのバブリングがポイント。 - DOM をいじると リフロー/リペイントが走る。まとめて操作し、
transform/opacityを活かすと軽い。 - 仮想 DOM は実 DOM 更新を減らすための差分計算の仕組みで、最後に描くのは常に実 DOM。
土台となる文書の正体は HTML、それを動かす言語は JavaScript、見た目は CSS が担います。
Web/フロントエンド Article
DOM(ドキュメントオブジェクトモデル)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
DOM
比較で見る軸
難易度: intermediate / カテゴリ: Web/フロントエンド / タグ数: 4
導入後に効く点
JavaScript は DOM API(getElementById / querySelector / createElement / addEventListener など)でノードを取得・生成・操作・イベント監視する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- intermediate
- カテゴリ
- Web/フロントエンド
- タグ数
- 4
判断チェックリスト
- 自社の用途が「DOM / Web」に近いか確認する。
- 強みである「DOM は HTML を木構造のオブジェクトとして表現したもの。HTML テキストそのものではなく、ブラウザがメモリ上に組み立てた“生きた”モデル。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。