はじめに

Webページでボタンをクリックしたときにメニューが表示されたり、入力フォームの内容がリアルタイムで検証されたりする動的な機能は、JavaScriptがHTMLの要素を操作することで実現されています。この操作を可能にするのがDOM(Document Object Model)です。

DOMを理解することは、JavaScriptでWebページを動的に制御するための第一歩です。本記事では、DOMの基本概念から、querySelectorgetElementByIdなどを使ったHTML要素の取得方法まで、初心者向けに具体的なコード例を交えて解説します。

DOMとは何か

DOMの定義

DOM(Document Object Model)は、HTMLやXMLドキュメントをプログラムから操作するためのインターフェースです。MDN Web Docsでは以下のように定義されています。

DOMはドキュメントの構造(HTMLを表すWebページなど)をメモリ上で表現し、スクリプトやプログラミング言語とWebページを接続します。

簡単に言えば、DOMはHTMLドキュメントをJavaScriptで扱えるオブジェクトの集合体として表現したものです。これにより、JavaScriptからHTML要素の取得、追加、削除、属性の変更などが可能になります。

DOMツリーの構造

ブラウザがHTMLを読み込むと、その内容を「DOMツリー」と呼ばれるツリー構造に変換します。以下のHTMLを例に見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>サンプルページ</title>
  </head>
  <body>
    <h1>見出し</h1>
    <p>段落テキスト</p>
  </body>
</html>

このHTMLは、以下のようなDOMツリーとして表現されます。

graph TD
    A[document] --> B[html]
    B --> C[head]
    B --> D[body]
    C --> E[title]
    E --> F["#text: サンプルページ"]
    D --> G[h1]
    D --> H[p]
    G --> I["#text: 見出し"]
    H --> J["#text: 段落テキスト"]

    style A fill:#4a90d9,color:#fff
    style B fill:#e34c26,color:#fff
    style C fill:#f7df1e,color:#000
    style D fill:#f7df1e,color:#000
    style E fill:#61dafb,color:#000
    style G fill:#61dafb,color:#000
    style H fill:#61dafb,color:#000

DOMツリーの構成要素には、以下のような種類があります。

ノードの種類 説明
Document ドキュメント全体を表すルートノード document
Element HTML要素を表すノード <div>, <p>, <h1>
Text 要素内のテキストを表すノード "見出し", "段落テキスト"
Attribute 要素の属性を表すノード id="main", class="container"

documentオブジェクト

JavaScriptでDOMを操作する際の起点となるのがdocumentオブジェクトです。documentはWebページ全体を表すオブジェクトであり、要素の取得や作成などのメソッドを提供します。

1
2
3
4
// documentオブジェクトの基本的な使用例
console.log(document.title);       // ページのタイトルを取得
console.log(document.URL);         // ページのURLを取得
console.log(document.body);        // body要素を取得

HTML要素を取得するメソッド

JavaScriptでDOM操作を行うには、まず操作対象の要素を取得する必要があります。以下では、代表的な要素取得メソッドを紹介します。

getElementById - IDで要素を取得

getElementByIdは、指定したID属性を持つ要素を1つ取得します。IDはページ内で一意であるべきため、常に単一の要素(またはnull)が返されます。

1
2
3
4
<div id="main-content">
  <h1 id="page-title">ようこそ</h1>
  <p id="description">このページの説明文です。</p>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// IDで要素を取得
const title = document.getElementById("page-title");
console.log(title.textContent); // "ようこそ"

const description = document.getElementById("description");
console.log(description.textContent); // "このページの説明文です。"

// 存在しないIDを指定するとnullが返る
const notFound = document.getElementById("non-existent");
console.log(notFound); // null

getElementByIdの特徴は以下の通りです。

  • 戻り値は単一のElement、またはnull
  • 最も高速な要素取得メソッドの1つ
  • IDが重複している場合は最初に見つかった要素のみ返される

querySelector - CSSセレクタで要素を取得

querySelectorは、CSSセレクタを使って最初にマッチする要素を1つ取得します。CSSセレクタの知識をそのまま活かせるため、柔軟な要素の指定が可能です。

1
2
3
4
5
6
7
<div class="container">
  <ul class="menu">
    <li class="menu-item active">ホーム</li>
    <li class="menu-item">製品</li>
    <li class="menu-item">お問い合わせ</li>
  </ul>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// クラスセレクタで取得
const container = document.querySelector(".container");
console.log(container); // <div class="container">...</div>

// 複合セレクタで取得
const activeItem = document.querySelector(".menu-item.active");
console.log(activeItem.textContent); // "ホーム"

// 子孫セレクタで取得
const firstMenuItem = document.querySelector(".menu .menu-item");
console.log(firstMenuItem.textContent); // "ホーム"

// 属性セレクタで取得
const inputEmail = document.querySelector('input[type="email"]');

// IDセレクタで取得(getElementByIdと同様の結果)
const title = document.querySelector("#page-title");

querySelectorAll - 複数の要素を取得

querySelectorAllは、CSSセレクタにマッチするすべての要素をNodeList(配列に似たオブジェクト)として取得します。

1
2
3
4
5
<ul class="product-list">
  <li class="product">商品A</li>
  <li class="product">商品B</li>
  <li class="product">商品C</li>
</ul>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// すべての.product要素を取得
const products = document.querySelectorAll(".product");
console.log(products.length); // 3

// forEachで各要素を処理
products.forEach((product, index) => {
  console.log(`${index + 1}: ${product.textContent}`);
});
// 出力:
// 1: 商品A
// 2: 商品B
// 3: 商品C

// 配列メソッドを使用する場合はArray.fromで変換
const productArray = Array.from(products);
const productNames = productArray.map(p => p.textContent);
console.log(productNames); // ["商品A", "商品B", "商品C"]

NodeListと配列の違いを理解しておくことが重要です。

特徴 NodeList Array
forEach 使用可能 使用可能
map, filter, reduce 使用不可 使用可能
配列への変換 Array.from()で可能 不要
ライブ更新 querySelectorAllでは静的 静的

getElementsByClassName - クラス名で取得

getElementsByClassNameは、指定したクラス名を持つすべての要素をHTMLCollectionとして取得します。

1
2
3
<div class="card highlight">カード1</div>
<div class="card">カード2</div>
<div class="card highlight">カード3</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// クラス名で要素を取得
const cards = document.getElementsByClassName("card");
console.log(cards.length); // 3

// 複数のクラスを持つ要素を取得(スペース区切り)
const highlightedCards = document.getElementsByClassName("card highlight");
console.log(highlightedCards.length); // 2

// インデックスでアクセス
console.log(cards[0].textContent); // "カード1"
console.log(cards[1].textContent); // "カード2"

HTMLCollectionは「ライブコレクション」です。DOMの変更がリアルタイムに反映されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const items = document.getElementsByClassName("item");
console.log(items.length); // 3

// 新しい要素を追加
const newItem = document.createElement("div");
newItem.className = "item";
document.body.appendChild(newItem);

// HTMLCollectionは自動的に更新される
console.log(items.length); // 4

getElementsByTagName - タグ名で取得

getElementsByTagNameは、指定したタグ名を持つすべての要素をHTMLCollectionとして取得します。

1
2
3
4
5
<article>
  <p>最初の段落</p>
  <p>2番目の段落</p>
  <p>3番目の段落</p>
</article>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// すべてのp要素を取得
const paragraphs = document.getElementsByTagName("p");
console.log(paragraphs.length); // 3

// すべての要素を取得(*を使用)
const allElements = document.getElementsByTagName("*");
console.log(allElements.length); // ページ内のすべての要素数

// for...ofでループ処理
for (const p of paragraphs) {
  console.log(p.textContent);
}

要素取得メソッドの比較と使い分け

各メソッドの特徴を比較表にまとめます。

メソッド 戻り値 セレクタ ライブ更新 対応ブラウザ
getElementById Element / null ID - 全ブラウザ
querySelector Element / null CSS - IE8+
querySelectorAll NodeList(静的) CSS なし IE8+
getElementsByClassName HTMLCollection クラス あり IE9+
getElementsByTagName HTMLCollection タグ あり 全ブラウザ

使い分けの指針

flowchart TD
    A[要素を取得したい] --> B{IDで特定できる?}
    B -->|はい| C[getElementById]
    B -->|いいえ| D{複雑なセレクタが必要?}
    D -->|はい| E{複数の要素が必要?}
    E -->|はい| F[querySelectorAll]
    E -->|いいえ| G[querySelector]
    D -->|いいえ| H{クラス名で取得?}
    H -->|はい| I{ライブ更新が必要?}
    I -->|はい| J[getElementsByClassName]
    I -->|いいえ| K[querySelectorAll]
    H -->|いいえ| L[getElementsByTagName]

    style C fill:#4caf50,color:#fff
    style F fill:#2196f3,color:#fff
    style G fill:#2196f3,color:#fff
    style J fill:#ff9800,color:#fff
    style K fill:#2196f3,color:#fff
    style L fill:#ff9800,color:#fff

実際の開発では、以下の基準で選択することをお勧めします。

1
2
3
4
5
6
7
8
9
// 1. IDが分かっている場合 → getElementById(最速)
const header = document.getElementById("header");

// 2. 柔軟なセレクタが必要な場合 → querySelector / querySelectorAll
const activeButton = document.querySelector("button.active");
const allLinks = document.querySelectorAll("nav a");

// 3. パフォーマンスが重要でシンプルな取得の場合 → getElements系
const allDivs = document.getElementsByTagName("div");

取得した要素の基本操作

要素を取得した後は、その内容やスタイルを操作できます。ここでは基本的な操作方法を紹介します。

テキストの取得と変更

1
2
3
4
5
6
7
8
const element = document.getElementById("message");

// テキストの取得
console.log(element.textContent); // 要素内のすべてのテキスト
console.log(element.innerText);   // 表示されているテキスト(CSSを考慮)

// テキストの変更
element.textContent = "新しいメッセージ";

textContentinnerTextの違いは以下の通りです。

1
2
3
4
<div id="sample">
  表示テキスト
  <span style="display: none;">非表示テキスト</span>
</div>
1
2
3
const sample = document.getElementById("sample");
console.log(sample.textContent); // "表示テキスト 非表示テキスト"
console.log(sample.innerText);   // "表示テキスト"

HTMLの取得と変更

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const container = document.getElementById("container");

// HTMLの取得
console.log(container.innerHTML);

// HTMLの変更(注意:XSS攻撃のリスクあり)
container.innerHTML = "<strong>強調テキスト</strong>";

// 外側のHTMLも含めて取得
console.log(container.outerHTML);

innerHTMLを使用する際は、ユーザー入力を直接設定しないように注意してください。セキュリティ上の脆弱性(XSS)を引き起こす可能性があります。

属性の操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const link = document.querySelector("a");

// 属性の取得
console.log(link.getAttribute("href"));
console.log(link.href); // プロパティとしてもアクセス可能

// 属性の設定
link.setAttribute("target", "_blank");
link.href = "https://example.com";

// 属性の存在確認
console.log(link.hasAttribute("target")); // true

// 属性の削除
link.removeAttribute("target");

クラスの操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const element = document.getElementById("box");

// クラスの追加
element.classList.add("active");

// クラスの削除
element.classList.remove("hidden");

// クラスの切り替え(あれば削除、なければ追加)
element.classList.toggle("selected");

// クラスの存在確認
console.log(element.classList.contains("active")); // true

// 複数のクラスを一度に操作
element.classList.add("class1", "class2", "class3");
element.classList.remove("class1", "class2");

実践的なコード例

ここでは、DOM操作を使った実践的な例を紹介します。

タブ切り替えUIの実装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<div class="tabs">
  <button class="tab-button active" data-tab="tab1">タブ1</button>
  <button class="tab-button" data-tab="tab2">タブ2</button>
  <button class="tab-button" data-tab="tab3">タブ3</button>
</div>
<div class="tab-content">
  <div id="tab1" class="tab-panel active">タブ1の内容</div>
  <div id="tab2" class="tab-panel">タブ2の内容</div>
  <div id="tab3" class="tab-panel">タブ3の内容</div>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// すべてのタブボタンを取得
const tabButtons = document.querySelectorAll(".tab-button");
const tabPanels = document.querySelectorAll(".tab-panel");

tabButtons.forEach(button => {
  button.addEventListener("click", () => {
    // すべてのタブからactiveを削除
    tabButtons.forEach(btn => btn.classList.remove("active"));
    tabPanels.forEach(panel => panel.classList.remove("active"));

    // クリックされたタブをアクティブに
    button.classList.add("active");

    // 対応するパネルを表示
    const tabId = button.getAttribute("data-tab");
    const targetPanel = document.getElementById(tabId);
    targetPanel.classList.add("active");
  });
});

フォームバリデーション

1
2
3
4
5
6
7
<form id="signup-form">
  <input type="text" id="username" placeholder="ユーザー名">
  <span id="username-error" class="error"></span>
  <input type="email" id="email" placeholder="メールアドレス">
  <span id="email-error" class="error"></span>
  <button type="submit">登録</button>
</form>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const form = document.getElementById("signup-form");
const usernameInput = document.getElementById("username");
const emailInput = document.getElementById("email");

form.addEventListener("submit", (event) => {
  let isValid = true;

  // ユーザー名の検証
  const usernameError = document.getElementById("username-error");
  if (usernameInput.value.trim().length < 3) {
    usernameError.textContent = "ユーザー名は3文字以上で入力してください";
    isValid = false;
  } else {
    usernameError.textContent = "";
  }

  // メールアドレスの検証
  const emailError = document.getElementById("email-error");
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!emailPattern.test(emailInput.value)) {
    emailError.textContent = "有効なメールアドレスを入力してください";
    isValid = false;
  } else {
    emailError.textContent = "";
  }

  // バリデーション失敗時は送信を中止
  if (!isValid) {
    event.preventDefault();
  }
});

DOMContentLoadedイベント

JavaScriptでDOM操作を行う際、HTMLの読み込みが完了してから実行する必要があります。そのために使用するのがDOMContentLoadedイベントです。

1
2
3
4
5
6
// HTMLの解析が完了したら実行
document.addEventListener("DOMContentLoaded", () => {
  // ここでDOM操作を行う
  const element = document.getElementById("main");
  console.log(element);
});

または、<script>タグを<body>の閉じタグ直前に配置する方法もあります。

1
2
3
4
<body>
  <!-- ページの内容 -->
  <script src="script.js"></script>
</body>

さらに、defer属性を使用する方法も現代的なアプローチとして推奨されます。

1
2
3
<head>
  <script src="script.js" defer></script>
</head>

defer属性を付けると、HTMLの解析と並行してスクリプトがダウンロードされ、解析完了後に実行されます。

まとめ

本記事では、JavaScriptにおけるDOMの基本概念と要素取得メソッドについて解説しました。

DOMの基本として押さえておくべきポイントは以下の通りです。

  • DOMはHTMLドキュメントをツリー構造として表現したもの
  • documentオブジェクトがDOM操作の起点となる
  • 要素、テキスト、属性などがノードとして表現される

要素取得メソッドの選択基準は以下を参考にしてください。

  • IDで取得する場合はgetElementByIdが最速
  • 柔軟なセレクタが必要ならquerySelector/querySelectorAll
  • ライブ更新が必要ならgetElementsByClassName/getElementsByTagName

DOM操作はJavaScriptによるWeb開発の基礎です。まずはquerySelectorgetElementByIdを使いこなせるようになることを目標に、実際のコードで練習してみてください。

参考リンク