はじめに#
Webページでボタンをクリックしたときにメニューが表示されたり、入力フォームの内容がリアルタイムで検証されたりする動的な機能は、JavaScriptがHTMLの要素を操作することで実現されています。この操作を可能にするのがDOM(Document Object Model)です。
DOMを理解することは、JavaScriptでWebページを動的に制御するための第一歩です。本記事では、DOMの基本概念から、querySelectorやgetElementByIdなどを使った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:#000DOMツリーの構成要素には、以下のような種類があります。
| ノードの種類 |
説明 |
例 |
| 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 = "新しいメッセージ";
|
textContentとinnerTextの違いは以下の通りです。
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開発の基礎です。まずはquerySelectorとgetElementByIdを使いこなせるようになることを目標に、実際のコードで練習してみてください。
参考リンク#