JavaScript

The DOM (Document Object Model) is a tree-like data structure created by the browser from your HTML. JavaScript uses this tree to find elements, update text and attributes, handle events, and reflect changes on the screen.

DOM(Document Object Model)

DOMDocument Object Model)は、ブラウザがHTMLを読み取って作る「画面の裏側のツリー(木構造)」です。JavaScriptはこのツリーを操作することで、画面の内容を変えたり、クリックなどのイベントを受け取ったりできます。

「HTMLは文字なのに、なぜJavaScriptで要素を探したり書き換えたりできるの?」という疑問は、DOM を“データ構造”として捉えるとスッと解決します。

このページでできるようになること

まずは直感:DOM って何?

直感的には、HTMLを「部品の木」へ変換したものです。HTMLは元々“文字列”ですが、ブラウザはそれを読み取って「<div> の中に <p> があって…」という“親子関係のある部品”として組み立て直します。その結果できるのが DOM です。

HTMLは「設計図」、DOMは「組み立て後の部品表」
HTMLを読んだブラウザは、要素・属性・テキストなどをオブジェクト(部品)として作り、木構造で持ちます。 だからJavaScriptは、「文字列の中から無理やり探す」のではなく、部品(要素)を直接つかんで操作できます。
DOMは「画面」そのものではなく「画面の元データ」
DOMは“見た目のピクセル”ではなく、「<button> がここにある」「この要素のテキストはこれ」といった構造情報です。 見た目は主にCSSが担当し、ブラウザはDOMとCSSなどをもとに画面を描画します。
まずのゴール:DOMを「触れる構造」として想像できる
DOMの勉強の最初は、仕様やクラス階層を暗記するより、 「ページがツリーでできていて、document からたどれる」ことが分かれば十分です。

覚え方:DOM は「HTMLを“木”にしたもの」。JavaScriptは「木の枝(要素)をつかんで変える」。

正確な定義(仕様に沿った説明・DOMの中での位置づけ)

DOM は、Webプラットフォームで文書(HTML/XMLなど)をツリー構造のノードとして表し、スクリプトからアクセス・変更できるようにするためのモデルです。実務では「document から始まるノードツリー」と捉えるのが分かりやすいです。

document は「今見ているページの入口」
JavaScriptの document は、今のページ(文書)を表すオブジェクトです。 ここから document.bodyquerySelector などで、DOM上の要素へアクセスします。
Node は「ツリーの1点(要素・テキストなど)」
DOMのツリーは「ノード」の集合です。要素(Element)だけでなく、テキスト(Text)などもノードです。 実務で最初に意識すると効くのは「要素だけがツリーじゃない」という点です(混乱ポイントで扱います)。
Element は「タグに対応するノード」
<div><button> のような“要素”に対応します。 クラスや属性(class / id / data-*)を持ち、見た目やイベントと強く関わります。

仕様の細部(どのインターフェースがどれを継承するか等)は、最初は完全理解しなくてOKです。まずは document → 要素を探す → 変更 → 画面に反映 の流れを押さえるのが優先です。

基本的な考え方(初心者がまず押さえるポイント)

ポイント1:DOMは「読み取り」より「操作」のために意識する
DOMは「構造を表すデータ」なので、やりたいことは大体この3つに分類できます。
  • 探す:querySelector / getElementById などで要素を取得する
  • 変える:テキスト・属性・クラス・スタイル・子要素を変更する
  • 反応する:addEventListener でクリック等に反応する
ポイント2:「どれを基準に探すか」が分かれば迷子にならない
いつも入口は document です。 ただし、取得した要素(たとえば const box = document.querySelector(...))から、 その内部だけを探す(box.querySelector(...))こともできます。 これは「DOMをツリーとして扱っている」からできることです。
ポイント3:DOM操作は「変更→反映」を安く見積もらない
DOMを書き換えると、ブラウザは画面の更新を考える必要があります。 だから実務では「必要な時だけ」「まとめて」変更する方が安定します(後で実務メモで扱います)。

ここで止まりがち:querySelector で取れない時は「セレクタのミス」だけでなく「DOMがまだ作られていない(読み込み前)」ことがあります。対策は defer を使う、または DOMContentLoaded 後に実行する、のどちらかです。

よくある勘違い・混同ポイント

DOM と「HTML文字列」は同じ?
似ていますが別物です。HTMLは文字列(テキスト)で、DOMはブラウザ内のオブジェクト構造です。 element.outerHTML で“HTMLっぽい文字列”が取れることもありますが、実体はDOMです。
「要素」だけがDOMだと思っていた
DOMにはテキストノードもあります。たとえば <p>Hello</p>Hello は「テキストノード」です。 この違いを知らないと、childNodesfirstChild で「変な空白がある」ように見えて混乱しがちです。
innerHTML は便利だから基本これで良い?
便利ですが、実務では使いどころが限られます。 理由は2つあります:
  • 安全性:外部入力を入れるとHTMLとして解釈され、意図しないスクリプト実行につながることがある
  • 破壊:中身を入れ替えると、内部の要素やイベントが作り直される(状態が消える)
表示テキストの更新なら、まず textContent を優先するのが安全です。
DOMCSS の役割が混ざる
DOMは構造(部品・親子関係・属性)で、CSSは見た目(色・余白・配置)です。 もちろんDOM操作でクラスを付け替えて見た目を変えることはできますが、見た目のルール自体はCSSに寄せる方が保守しやすいです。

実務でよくある使用シーン(フロントエンド視点)

フォーム入力のバリデーション(その場でエラー表示)
入力値に応じてDOMにクラスを付けたり、エラーメッセージ要素を表示/非表示にします。 ここでのコツは「文字列を組み立てる」より、既にある要素を見せる/隠す方が安全でシンプルになりやすい点です。
モーダルやアコーディオンなど、UIの開閉
表示状態をDOMの属性やクラス(例:hiddenaria-expandedclass)で管理します。 DOMは「状態の置き場所」になりやすいので、どこに何の状態があるかを意識すると設計が安定します。
リストの追加・削除(ToDo・タグ・検索結果など)
document.createElement で要素を作り、append で追加します。 小さなUIでも「どの要素を、どこに、どの順で追加するか」が分かっていると、バグが減ります。
イベント処理(クリック、入力、スクロールなど)
addEventListener でイベントを登録します。 DOMにイベントが届く流れ(バブリングなど)を知ると、親要素でまとめて処理(イベント委譲)できるようになります。 これは大量の要素があるUIでとても効きます(補足で触れます)。

実務メモ:「DOMを触る」=「要素の見た目を直接変える」ではありません。実務では “状態をDOMに持たせて、CSSで見た目を切り替える” が定番です(例:クラスを付け替える)。

実務で起きがちなミスと回避策

ミス:null に対して操作して落ちる(要素が取れていない)
原因:セレクタの間違い、読み込み順、条件分岐でDOMが存在しない等。
回避策:まず取得直後に存在チェックを入れ、必要なら早期returnします。

JavaScript

const button = document.querySelector("#save");
if (!button) {
    // ここで止まりがち:要素が無い状態で操作して例外になる
    return;
}
button.addEventListener("click", () => {
    // ...
});
ミス:innerHTML でイベントが消える/状態が飛ぶ
原因:innerHTML は中身を作り直すため、既存の要素参照やイベントが失われます。
回避策:更新がテキストなら textContent、要素の追加なら createElementappend を基本にします。
ミス:DOM操作をループで細かく繰り返して重くなる
原因:1つずつDOMに追加・変更を繰り返すと、ブラウザが画面更新を何度も考えることがあります。
回避策:まとめて追加する(DocumentFragment 等)か、まずデータを配列で整えてから、最後にまとめてDOMへ反映します。

ここはパフォーマンスの話なので、最初は「大量に触ると重くなることがある」くらいでOKです。

ミス:クリック対象を勘違いする(イベントの届き方)
原因:イベントは必ずしも“付けた要素だけ”で終わらず、親へ伝わることがあります。
回避策:event.target(実際に押された要素)と、event.currentTarget(リスナーを付けた要素)を区別します。

注意:外部入力(フォーム、URL、APIレスポンス等)を innerHTML に入れるのは危険になり得ます。基本は textContent を使い、どうしてもHTMLを扱う場合は「入力の出どころ」と「安全な変換(サニタイズ)」を設計に含めます。

HTML / CSS / JavaScript との関係

DOMは「3者の接点」です。HTMLが構造の元になり、CSSが見た目のルールを与え、JavaScriptが状態と操作を追加します。

HTML:DOMの“材料”
HTMLはDOMの初期状態を決めます。要素の種類(<button> など)や属性(idclassdata-*)は、DOMを探す・意味を伝える・スタイルを当てるための手がかりになります。
CSS:DOMに“見た目のルール”を当てる
CSSはDOM要素にルールを適用します。JavaScriptは色や位置を直接いじるより、クラスの付け替えでCSSに任せる方が設計が安定しやすいです。
JavaScript:DOMを“読み書き”して動かす
JavaScriptはDOMを探し、値や属性を更新し、イベントに反応します。 つまりJavaScriptが触っているのは「DOM(データ構造)」で、結果として画面が変わります。

コツ:「見た目のルールはCSS」「状態の切替はクラス」「必要な時だけDOMを触る」――この分担ができると、UIは壊れにくくなります。

フロントエンドエンジニア視点での設計の考え方

DOMを知る目的は「書ける」ではなく「判断できる」です。ここでは、DOMを前提に“どこまでをDOM操作でやるか”を考える軸を作ります。

DOMを直接触る場面 / 触らない場面

触る(向いている)
  • UIの状態切替(開閉・選択・エラー表示など)
  • ユーザー操作に反応して、表示内容を更新する
  • 小さなウィジェットをページに埋め込む(フォーム補助など)
触らない(別の設計が向く)
  • 同じUIを大量に生成して複雑な状態管理が必要(フレームワークの導入を検討)
  • 外部入力をそのままHTMLとして出す必要がある(安全設計が必須)
  • ページ全体の構造を頻繁に作り直す(差分更新の設計が必要)

「触らない」は“禁止”ではなく、「設計の負担が増えやすい」サインです。

構造を意識する意味

理由1:バグの原因が「構造のズレ」で起きる
DOMはツリーです。どの親の中にいるか、どの順番か、どの属性が付いているかで、セレクタやイベントの挙動が変わります。 だから「今の構造を図で言える」だけで、デバッグが速くなります。
理由2:仕様とアクセシビリティが“構造”に乗る
ボタンなのに <div> を使うと、キーボード操作や支援技術への意味伝達が弱くなります。 DOMを理解するほど、「正しい要素を選ぶ」価値が上がります。
理由3:将来の変更に強い
DOMを“データ構造”として扱うと、表示(CSS)や状態(クラス)を分離しやすくなり、変更に強くなります。 「ここを変えたらどこが影響するか」が読みやすいコードになります。

結論:DOMを前提に考えるとは、「画面を“部品の木”として捉え、どの部品にどんな状態と役割があるかを言語化してから実装する」ことです。

コピペで動く最小デモ(DOMを“つかんで変える”)

DOMは「ツリー」ですが、最初は“要素をつかんで変える”が分かれば十分です。次のデモは、querySelector で要素を取得し、textContentclassList を更新します。

デモ:メッセージを書き換える DOM操作

こんにちは。ここは DOM で書き換わります。

いま触っている要素:#domDemoMessage

<div id="domDemo">
  • <p id="domDemoMessage">…</p>
  • <input id="domDemoInput">
  • <button id="domDemoApply">
  • <button id="domDemoReset">

この“簡略ツリー”は説明用です。実際のDOMにはもっと多くのノードがあります。

HTML

<p id="domDemoMessage">こんにちは。ここは DOM で書き換わります。</p>
<input type="text" id="domDemoInput">
<button type="button" id="domDemoApply">適用</button>

JavaScript

const message = document.querySelector("#domDemoMessage");
const input = document.querySelector("#domDemoInput");
const apply = document.querySelector("#domDemoApply");

apply.addEventListener("click", () => {
    message.textContent = input.value.trim() || "(空です)";
});

ここで止まりがち:見た目を変えたい時に style を直接いじりすぎると、後からCSSの設計が崩れやすいです。まずは classList.add/remove でクラスを切り替える設計を検討してください。

よくある質問(FAQ)

DOM はどこに存在していますか?
DOMはブラウザの中にあります。HTMLを読み取った結果としてメモリ上に作られ、document からアクセスできます。
documentwindow はどう違いますか?
document は「文書(ページ)」、window は「ブラウザのウィンドウ(環境)」の入口です。 DOMを触る入口は主に document で、画面サイズや履歴など“環境”は window 側に寄っています。
querySelectorgetElementById はどちらを使えばいいですか?
どちらでも目的は達成できます。querySelector はCSSセレクタで書けるので統一しやすく、 getElementByIdid がある場合に意図が明確で軽量なことが多いです。 迷ったら querySelector で揃えてOKです(ただし # の付け忘れに注意)。
textContentinnerHTML の使い分けは?
基本は textContent を使うのが安全です。 innerHTML は「HTMLとして解釈して挿入」するため、外部入力と混ぜると危険になり得ます。 HTMLを組み立てる必要がある場合は、要素を作って組み立てる(createElement)方が安全に寄せやすいです。
DOM操作で、必ずすぐ画面が更新されますか?
多くの場合は“結果として”画面が変わりますが、ブラウザは最適化のために更新のタイミングを調整します。 体感としては「DOMを変えると表示が変わる」でOKですが、連続で大量に変えると重くなることがあるので注意します。
フレームワーク(React/Vueなど)を使うと、DOMを知らなくても良い?
知らなくても書ける場面は増えますが、DOMを知っているとデバッグと設計の判断が強くなります。 たとえば「なぜこのイベントが発火するのか」「なぜここで再描画が起きるのか」は、DOMの基本が土台になります。

まとめ:迷ったときの判断軸(チェックリスト)