TL

DOM(ドキュメントオブジェクトモデル)

ブラウザが HTML を読み込んでメモリ上に作る“ツリー構造のオブジェクト”。JavaScript はこれを通してページを取得・生成・操作する。

中級DOMWebJavaScriptブラウザ最終更新: 2026-06-04
TL;DR要点だけ先に
  • 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);                    // ツリーにぶら下げる → 画面に反映
innerHTML に外部の文字列を入れない

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 の変更軽い
DOM 操作はまとめてやる

ループの中で 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 を丁寧に最小限だけ触れば、素の DOM API でも十分速く書けます。レンダリングの全体像は ブラウザのレンダリングの仕組み も参照してください。

まとめ

  • DOM は HTML を木構造のオブジェクトにしたもの。HTML テキストそのものではなく、ブラウザがメモリ上に組み立てた生きたモデル
  • JavaScript は DOM API で 取得・生成・変更・イベントを行う。textContentinnerHTML の使い分け、イベントのバブリングがポイント。
  • 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、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

DOMWebJavaScriptブラウザDOMWebJavaScriptブラウザ
参考: 公式情報