ドキュメントツリーとは何か
ドキュメントツリーは、HTML文書を「ノード(要素・テキスト・コメント)」の入れ子で表した木構造です。最上位の document を根にして、html、head、body と階層が続き、各要素が子ノードを持ちます。ここが重要です:ブラウザはこのツリー(設計図)をもとに画面を描画します。ツリーを操作すると、画面が同期して更新されます。
基本構造の全体像(html・head・body とノードの役割)
ルートからの階層
documentが根。直下にhtml要素があり、その中にheadとbodyが並びます。headはメタ情報(title、meta、link、scriptなど)、bodyは画面に表示されるコンテンツ(h1、p、divなど)を担います。
例:このHTMLのツリー
<!doctype html>
<html>
<head>
<title>ツリーの例</title>
</head>
<body>
<h1 id="title">こんにちは</h1>
<p class="msg">DOM は木構造です</p>
</body>
</html>
HTMLこの構造は「document → html → head/title、body/h1、p」という親子関係のツリーになります。h1 内の文字「こんにちは」はテキストノードとして h1 の子になります。
親子・兄弟・祖先の関係(ツリーの辿り方)
親子と兄弟を辿る
const h1 = document.querySelector("#title");
console.log(h1.parentNode.tagName); // "BODY"(親)
console.log(h1.nextElementSibling.tagName); // "P"(次の兄弟:要素だけ)
console.log(h1.firstChild.nodeType); // 3(テキストノード)
JavaScriptここが重要です:要素だけを扱うときは children や nextElementSibling、テキストも含めて扱うときは childNodes や nextSibling を使います。空白もテキストノードとして存在するため、用途に応じてAPIを選ぶのがコツです。
children と childNodes の違い(要素のみか、すべてか)
要素だけのコレクション
const body = document.body;
console.log(body.children); // HTMLCollection(要素のみ)
JavaScriptすべてのノード(テキスト・コメントを含む)
console.log(body.childNodes); // NodeList(テキスト・コメントも含む)
JavaScriptここが重要です:意図せず空白テキストに当たるとロジックが崩れます。「DOMをレイアウト単位で扱う」なら要素中心のAPI、「文字も分析したい」ならノード中心のAPIを使い分けましょう。
ツリーの構築と更新(作成・挿入・削除で形が変わる)
新規ノードの生成と挿入
const li = document.createElement("li");
li.textContent = "項目";
const ul = document.querySelector("ul");
ul.append(li); // 末尾に追加(ツリーに組み込まれる)
JavaScriptノードの削除
li.remove(); // 親から切り離され、ツリーから消える
JavaScript大量追加はフラグメントで効率化
const frag = document.createDocumentFragment();
for (let i = 0; i < 500; i++) {
const li = document.createElement("li");
li.textContent = `Item ${i}`;
frag.append(li);
}
ul.append(frag); // まとめて挿入で再描画コストを削減
JavaScriptここが重要です:生成→内容設定→挿入の順にすると、中途半端な状態が画面に出ず、再描画も少なく済みます。大量更新はフラグメントで“バッチ処理”にするのが定石です。
ツリーの走査と選択(最短経路で目的のノードへ)
CSSセレクタで直接選ぶ
const items = document.querySelectorAll("ul > li.active");
JavaScript種類で絞る走査(TreeWalker)
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
let node;
while ((node = walker.nextNode())) {
// テキストノードだけを処理
}
JavaScriptここが重要です:普段は querySelector(All) が最も実用的。大量に、かつ種類で厳密に絞って辿る必要があるときは TreeWalker/NodeIterator を使います。
レンダリングと性能の勘所(読み書きのバッチ化)
DOMの読み取り(サイズ、位置)と書き込み(挿入、属性変更)が交互に混ざると、ブラウザが何度もレイアウト計算・再描画を行い、パフォーマンスが落ちます。読み取りをまとめる、書き込みをまとめる、スタイル変更はクラス切り替えで一括適用する、アニメーションは requestAnimationFrame を使う——これらでツリー更新の負荷を抑えられます。ここが重要です:DOM操作は強力だが重い。更新の“塊”を作る意識が、滑らかなUIを支えます。
よくある落とし穴(空白テキスト・DOM構築タイミング)
- 空白文字がテキストノードとして存在するため、
childNodesやnextSiblingが意図しないノードを返すことがあります。要素だけならchildren/nextElementSiblingを使う。 - DOM構築前に要素を参照すると
null。defer属性を付けたスクリプトや、DOMContentLoadedイベントで「ツリーが完成してから」操作する。 innerHTMLの乱用は、XSSやイベント喪失、再描画の負荷につながります。必要なときだけ、信頼できるデータに限定し、基本は要素生成・挿入で組み立てる。
ここが重要です:ツリーは「いつ」「何を」「どのAPIで」触るかで安定性が決まります。タイミングと対象の種類を意識し、意図通りに辿れる道具を選びましょう。
まとめ
ドキュメントツリーは、document を根とするノードの木構造で、html/head/body を軸に全要素・テキストが階層化されています。要素中心のAPI(children、classList、querySelector)とノード中心のAPI(childNodes、nodeType、TreeWalker)を使い分け、生成→設定→挿入で構造を安全に更新します。空白テキストやDOM構築タイミングの落とし穴を避け、読み書きのバッチ化で性能を守る——この感覚が身につくと、ツリーの全体像を手の内で扱えるようになります。
