DOM 入門
DOM は JavaScript と Web ページをつなぐ唯一の橋です。DOM がなければ、JS は純粋な計算言語でしかありません。DOM があれば、JS はページを「見て」、修正し、命を吹き込むことができます。
📖 まとめ
DOM とは何か
DOM は Document Object Model の略です。ブラウザが HTML を読み込むと、プレーンテキストとして保存するのではなく、オブジェクトツリーに変換します。各 HTML タグはオブジェクト(ノードと呼ばれる)になり、タグ間のネスト関係はツリーの親子関係になります。
DOM は逆さまの木のように考えてください。最も上には根(document)があり、枝分かれして幹(html、head、body)に、さらに葉(div、p、span...)へと続きます。
DOM ツリー構造
シンプルな HTML ページの DOM ツリーはおおよそこのようになります。
document
└── html
├── head
│ ├── meta
│ └── title
└── body
├── h1
├── p
└── div
└── span
documentは DOM ツリーのルートノードで、JS が DOM にアクセスするためのエントリポイントですhtmlはdocumentの唯一の子要素(ドキュメント要素)ですheadとbodyはhtmlの2つの子要素です- そこから層ごとにネストが続き、完全なツリーを形成します
ノードタイプ
DOM には3つのコアノードタイプがあります。
| ノードタイプ | 説明 | nodeType 値 |
|---|---|---|
| 要素ノード | HTML タグ(例:p、div) |
1 |
| テキストノード | タグ内のテキストコンテンツ | 3 |
| 属性ノード | タグの属性(例:class、id) |
2(一般的な使用は非推奨) |
HTML
<p class="intro">こんにちは世界</p>
この1行の中で:<p> は要素ノード、class="intro" は属性ノード、こんにちは世界 はテキストノードです。日常的な開発では、要素ノードが最も多く扱われます。
document オブジェクト
document は DOM へのエントリポイントです。ブラウザがこのグローバルオブジェクトを自動的に提供します。これにより以下が可能です。
- ページ要素へのアクセス:
document.getElementById()、document.querySelector() - 新しい要素の作成:
document.createElement() - ページコンテンツの読み書き:
document.title、document.body - ページイベントのリスニング:
document.addEventListener()
DOM の役割
JS 自体は Web ページを「見る」ことはできません。計算することしかできません。DOM が「目」と「手」を提供します。
- 読み取り:ページからテキスト、属性、スタイルを取得
- 変更:テキストコンテンツ、属性値、CSS スタイルを変更
- 追加:新しい要素を作成してページに挿入
- 削除:ページから要素を削除
- リスニング:ユーザーのクリック、入力、その他のインタラクションに応答
DOM がなければ、JS と HTML は完全に別々のものになります。DOM がそれらを接続します。
例:DOM 構造の確認
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>DOM 構造</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.tree { margin: 16px 0; padding: 16px; background: #f5f5f5; border-radius: 8px; font-family: monospace; font-size: 14px; line-height: 1.8; }
.node { color: #4a90d9; font-weight: bold; }
.text-node { color: #5cb85c; font-style: italic; }
.attr-node { color: #f0ad4e; }
.indent { margin-left: 24px; }
.info { background: #e8f4fd; padding: 12px; border-radius: 6px; margin: 16px 0; }
</style>
</head>
<body>
<h1 id="mainTitle">DOM 構造デモ</h1>
<p class="intro">これは段落です</p>
<div id="container">
<span>インライン要素</span>
</div>
<div id="output"></div>
<script>
function buildTree(node, depth) {
if (depth > 4) return "";
var indent = "";
for (var i = 0; i < depth; i++) indent += " ";
var result = "";
if (node.nodeType === 1) {
result += indent + '<span class="node"><' + node.nodeName.toLowerCase();
if (node.attributes && node.attributes.length > 0) {
for (var a = 0; a < node.attributes.length && a < 3; a++) {
result += ' <span class="attr-node">' +
node.attributes[a].name + '="' + node.attributes[a].value + '"</span>';
}
}
result += '></span>\n';
} else if (node.nodeType === 3) {
var text = node.textContent.trim();
if (text) {
result += indent + '<span class="text-node">"' + text + '"</span>\n';
}
return result;
}
var children = node.childNodes;
for (var c = 0; c < children.length; c++) {
result += buildTree(children[c], depth + 1);
}
if (node.nodeType === 1) {
result += indent + '<span class="node"></' +
node.nodeName.toLowerCase() + '></span>\n';
}
return result;
}
var treeStr = buildTree(document.documentElement, 0);
document.getElementById("output").innerHTML = `
<h2>このページの DOM ツリー</h2>
<div class="tree"><pre>${treeStr}</pre></div>
<div class="info">
<strong>色の凡例:</strong>
<span class="node">青 = 要素ノード</span> |
<span class="text-node">緑 = テキストノード</span> |
<span class="attr-node">オレンジ = 属性</span>
</div>
`;
</script>
</body>
</html>
例:document オブジェクトの探索
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>document オブジェクト</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.card { border: 2px solid #4a90d9; border-radius: 8px; padding: 16px; margin: 12px 0; background: #f0f7ff; }
.card h3 { margin: 0 0 8px; color: #4a90d9; }
.prop { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #e0e0e0; }
.prop:last-child { border-bottom: none; }
.key { font-weight: bold; color: #333; }
.val { color: #4a90d9; font-family: monospace; word-break: break-all; }
button { padding: 10px 20px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; margin: 4px; }
.btn-blue { background: #4a90d9; color: #fff; }
.btn-blue:hover { background: #357abd; }
.btn-green { background: #5cb85c; color: #fff; }
.btn-green:hover { background: #449d44; }
.btn-orange { background: #f0ad4e; color: #fff; }
.btn-orange:hover { background: #ec971f; }
.demo-area { min-height: 60px; border: 2px dashed #ccc; border-radius: 8px; padding: 16px; margin: 12px 0; text-align: center; }
</style>
</head>
<body>
<h2>document オブジェクトの探索</h2>
<div class="card">
<h3>一般的な document プロパティ</h3>
<div id="props"></div>
</div>
<div class="card">
<h3>実際に試してみましょう</h3>
<button class="btn-blue" id="btnTitle">document.title を変更</button>
<button class="btn-green" id="btnBody">body の背景を変更</button>
<button class="btn-orange" id="btnCreate">新しい要素を作成</button>
<button class="btn-blue" id="btnReset" style="background:#888;">リセット</button>
</div>
<div class="demo-area" id="demoArea">
<p>これはデモエリアです</p>
</div>
<script>
function showProps() {
var props = [
["document.title", document.title],
["document.URL", document.URL],
["document.domain", document.domain],
["document.contentType", document.contentType],
["document.documentElement", document.documentElement.nodeName],
["document.body", document.body.nodeName],
["document.head", document.head.nodeName],
["document.body.childElementCount", document.body.childElementCount],
["document.all.length", document.all.length + " 個の要素"]
];
document.getElementById("props").innerHTML = props.map(function(p) {
return '<div class="prop"><span class="key">' + p[0] +
'</span><span class="val">' + p[1] + '</span></div>';
}).join("");
}
showProps();
var titleCount = 0;
document.getElementById("btnTitle").addEventListener("click", function() {
titleCount++;
document.title = titleCount + " 回変更しました!";
showProps();
});
var bgColors = ["#fff8e1", "#e8f5e9", "#fce4ec", "#e3f2fd", "#f3e5f5"];
var bgIndex = 0;
document.getElementById("btnBody").addEventListener("click", function() {
document.body.style.background = bgColors[bgIndex % bgColors.length];
bgIndex++;
});
var createCount = 0;
document.getElementById("btnCreate").addEventListener("click", function() {
createCount++;
var p = document.createElement("p");
p.textContent = "新しく作成された段落 #" + createCount;
p.style.color = "#4a90d9";
p.style.fontWeight = "bold";
document.getElementById("demoArea").appendChild(p);
showProps();
});
document.getElementById("btnReset").addEventListener("click", function() {
document.title = "document オブジェクト";
document.body.style.background = "";
document.getElementById("demoArea").innerHTML = "<p>これはデモエリアです</p>";
titleCount = 0;
bgIndex = 0;
createCount = 0;
showProps();
});
</script>
</body>
</html>
例:ノードタイプの識別
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ノードタイプ</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
th { background: #4a90d9; color: #fff; }
.elem { background: #e3f2fd; }
.text { background: #e8f5e9; }
.note { background: #fff8e1; padding: 12px; border-radius: 6px; border-left: 4px solid #f0ad4e; margin: 16px 0; max-width: 700px; }
</style>
</head>
<body>
<h2>ノードタイプの説明</h2>
<p id="demo">これは段落です <span>複数のノードを</span>含んでいます</p>
<div id="output"></div>
<script>
var demo = document.getElementById("demo");
var results = [];
function analyzeNode(node, depth) {
var indent = "";
for (var i = 0; i < depth; i++) indent += "│ ";
var typeMap = { 1: "要素ノード", 3: "テキストノード" };
var typeName = typeMap[node.nodeType] || "その他(" + node.nodeType + ")";
var detail = "";
if (node.nodeType === 1) {
detail = "<" + node.nodeName.toLowerCase() + ">";
} else if (node.nodeType === 3) {
var text = node.textContent.trim();
if (text) detail = '"' + text + '"';
else return;
}
var cls = node.nodeType === 1 ? "elem" : "text";
results.push(
'<tr class="' + cls + '">' +
"<td>" + indent + "</td>" +
"<td><code>" + detail + "</code></td>" +
"<td>" + typeName + "</td>" +
"<td>" + node.nodeType + "</td>" +
"</tr>"
);
var children = node.childNodes;
for (var c = 0; c < children.length; c++) {
analyzeNode(children[c], depth + 1);
}
}
analyzeNode(demo, 0);
document.getElementById("output").innerHTML = `
<p><code><p id="demo">これは段落です <span>複数のノードを</span>含んでいます</p></code> の分析</p>
<table>
<tr><th>レベル</th><th>コンテンツ</th><th>ノードタイプ</th><th>nodeType</th></tr>
${results.join("")}
</table>
<div class="note">
💡 注意:タグ間の改行や空白もテキストノードになります!実際には、<code>children</code>(要素ノードのみ)の方が
<code>childNodes</code>(テキストを含むすべてのノード)より有用です。
</div>
`;
</script>
</body>
</html>
❓ よくある質問
Q DOM は JavaScript の一部ですか?
A いいえ。DOM は W3C が定義した標準です。JavaScript はそれと対話するための API を提供しているだけです。Node.js には DOM がありません。なぜならブラウザ環境ではないからです。DOM と JS を混同するのは初心者によくある誤解です。
Q childNodes に空のテキストノードがあるのはなぜですか?
A HTML の改行やインデントは DOM ではテキストノードとして解析されます。例えば、2つのタグ間の空白はテキストノード(改行文字を含む)になります。要素ノードだけが必要な場合は、
childNodes の代わりに children を使いましょう。Q DOM の変更はページのパフォーマンスに影響しますか?
A はい。DOM の変更はすべてブラウザのリフローとペイントをトリガーできます。頻繁な DOM 操作はパフォーマンスのキラーです。ベストプラクティス:まずすべての変更をメモリ内で構築し、その後一括で DOM に書き込みましょう。
📝 演習
- ブラウザの DevTools(F12)を開き、コンソールに
console.dir(document)と入力して、document オブジェクトのプロパティとメソッドを探索しましょう。認識できるか興味深いものを5つリストアップしてください。 - 現在のページの
body要素を取得し、そのchildElementCount(子要素の数)を出力してから、childrenを使ってすべての子要素をループし、それぞれのタグ名を出力するコードを書きましょう。 - 「要素ノード」と「テキストノード」の違いを説明してください。
<div>こんにちは <b>世界</b></div>が与えられた場合、そのノードツリー構造を描き、各ノードのタイプをラベル付けしてください。



