404 Not Found

404 Not Found


nginx

DOM 入門

DOM は JavaScript と Web ページをつなぐ唯一の橋です。DOM がなければ、JS は純粋な計算言語でしかありません。DOM があれば、JS はページを「見て」、修正し、命を吹き込むことができます。

📖 まとめ

DOM とは何か

DOM は Document Object Model の略です。ブラウザが HTML を読み込むと、プレーンテキストとして保存するのではなく、オブジェクトツリーに変換します。各 HTML タグはオブジェクト(ノードと呼ばれる)になり、タグ間のネスト関係はツリーの親子関係になります。

DOM は逆さまの木のように考えてください。最も上には根(document)があり、枝分かれして幹(htmlheadbody)に、さらに葉(divpspan...)へと続きます。

DOM ツリー構造

シンプルな HTML ページの DOM ツリーはおおよそこのようになります。

document
  └── html
       ├── head
       │    ├── meta
       │    └── title
       └── body
            ├── h1
            ├── p
            └── div
                 └── span

ノードタイプ

DOM には3つのコアノードタイプがあります。

ノードタイプ 説明 nodeType 値
要素ノード HTML タグ(例:pdiv 1
テキストノード タグ内のテキストコンテンツ 3
属性ノード タグの属性(例:classid 2(一般的な使用は非推奨)
HTML
<p class="intro">こんにちは世界</p>

この1行の中で:<p> は要素ノード、class="intro" は属性ノード、こんにちは世界 はテキストノードです。日常的な開発では、要素ノードが最も多く扱われます。

document オブジェクト

document は DOM へのエントリポイントです。ブラウザがこのグローバルオブジェクトを自動的に提供します。これにより以下が可能です。

DOM の役割

JS 自体は Web ページを「見る」ことはできません。計算することしかできません。DOM が「目」と「手」を提供します。

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">&lt;' + 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 += '&gt;</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">&lt;/' +
          node.nodeName.toLowerCase() + '&gt;</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 = "&lt;" + node.nodeName.toLowerCase() + "&gt;";
      } 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>&lt;p id="demo"&gt;これは段落です &lt;span&gt;複数のノードを&lt;/span&gt;含んでいます&lt;/p&gt;</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 に書き込みましょう。

📝 演習

  1. ブラウザの DevTools(F12)を開き、コンソールに console.dir(document) と入力して、document オブジェクトのプロパティとメソッドを探索しましょう。認識できるか興味深いものを5つリストアップしてください。
  2. 現在のページの body 要素を取得し、その childElementCount(子要素の数)を出力してから、children を使ってすべての子要素をループし、それぞれのタグ名を出力するコードを書きましょう。
  3. 「要素ノード」と「テキストノード」の違いを説明してください。<div>こんにちは <b>世界</b></div> が与えられた場合、そのノードツリー構造を描き、各ノードのタイプをラベル付けしてください。
100%