はじめに
DOM(Document Object Model)とは、HTML文書をJavaScriptから操作するための仕組みです。これを使うことで、要素の属性や内容、スタイルを動的に変更したり、新しい要素を追加・削除したり、ユーザーの操作に反応するインタラクティブなWebページを作成できます。
この記事で学べること
- DOM要素の選択と操作方法
- テキストや属性、スタイルの変更
- 要素の追加と削除
- イベント処理の基礎と応用
- 実践的なDOM操作とイベントの組み合わせ
基本的な流れは、querySelectorやgetElementByIdで要素を選択し、そのプロパティやメソッドで操作するというシンプルなものです。メソッドとプロパティは非常に多岐にわたるため、必要に応じて調べながら使っていきましょう。
DOM操作の基礎
要素の選択方法
DOM操作の第一歩は、操作したい要素を選択することです。JavaScriptには複数の要素選択方法があります。
querySelector / querySelectorAll
CSSセレクタを使って要素を選択する最も柔軟な方法です。querySelectorは最初にマッチした要素を1つ、querySelectorAllはマッチしたすべての要素を返します。
// 最初のボタン要素を取得
const button = document.querySelector("button");
// class="item"を持つすべての要素を取得
const items = document.querySelectorAll(".item");
// より複雑なセレクタも使用可能
const firstListItem = document.querySelector("ul > li:first-child");
getElementById / getElementsByClassName
特定のIDやクラス名で要素を選択する従来の方法です。getElementByIdは高速ですが、querySelectorの方が柔軟性が高いため、現在ではquerySelectorの使用が推奨されています。
// ID指定で要素を取得
const element = document.getElementById("myElement");
// クラス名で要素を取得(HTMLCollectionを返す)
const elements = document.getElementsByClassName("item");
テキスト内容の操作
innerTextは、要素の表示されているテキスト内容を取得・設定するプロパティです。HTMLタグを無視して純粋なテキストのみを扱い、CSSで非表示の要素は含まれません。
const element = document.getElementById("myElement");
element.innerText = "新しいテキスト";
console.log(element.innerText);
textContentは、非表示要素も含めてすべてのテキストを取得・設定します。innerTextとの違いは、表示・非表示に関わらずすべてのテキストノードを対象とする点ですね。
element.textContent = "すべてのテキストを更新";
innerHTMLは、HTML要素を含めてコンテンツを更新する際に使用します。HTMLタグをそのまま解釈するため動的にHTML構造を変更できますが、XSS攻撃(悪意あるスクリプトの実行)のリスクがあります。ユーザー入力を直接設定する場合は注意が必要です。
element.innerHTML = "<strong>太字のテキスト</strong>";
属性の操作
HTML要素の属性(src、href、alt、data-*など)は、getAttribute、setAttribute、removeAttributeメソッドで取得・変更できます。
const element = document.getElementById("myElement");
// 属性を設定
element.setAttribute("data-value", "100");
// 属性を取得
const value = element.getAttribute("data-value");
// 属性を削除
element.removeAttribute("data-value");
これにより、ユーザーの操作に応じて画像のURLを変更したり、リンク先を動的に設定できます。
// 画像のソースを変更
const img = document.querySelector("img");
img.setAttribute("src", "new-image.jpg");
// リンク先を変更
const link = document.querySelector("a");
link.setAttribute("href", "https://example.com");
スタイルの変更
JavaScriptからスタイルを変更する際、styleプロパティを使う方法は推奨されません。このプロパティは直接インラインスタイルとして設定されたもののみを参照し、CSSファイルで定義されたスタイルは取得できないためです。
現在のスタイルを正確に取得したい場合はgetComputedStyleメソッドを使用しますが、スタイル変更の最も推奨される方法はclassListを使ってCSSクラスを追加・削除する方法です。
const element = document.getElementById("myElement");
// クラスを追加
element.classList.add("new-class");
// クラスを削除
element.classList.remove("old-class");
// クラスの切り替え(存在すれば削除、なければ追加)
element.classList.toggle("toggle-class");
// クラスが存在するか確認
if (element.classList.contains("active")) {
console.log("activeクラスが存在します");
}
classListを使うと、配列のように複数のクラスを管理でき、CSSとJavaScriptの責務を明確に分離できるため保守性が向上します。
// 計算済みスタイルを取得(参照のみ)
const computedStyle = getComputedStyle(element);
console.log(computedStyle.color); // 実際に適用されている色を取得
要素の親子・兄弟関係
親要素から子要素へ、または兄弟要素へと辿ることで、特定の要素に効率的にアクセスできます。parentElement、children、nextElementSibling、previousElementSiblingなどのプロパティを使用しましょう。
const element = document.getElementById("myElement");
// 親要素を取得
const parent = element.parentElement;
// 子要素を取得(HTMLCollection)
const children = parent.children;
// 次の兄弟要素を取得
const nextSibling = element.nextElementSibling;
// 前の兄弟要素を取得
const previousSibling = element.previousElementSibling;
querySelectorで毎回検索するよりも、親子関係を利用した方がパフォーマンスが向上する場合があります。
要素の追加と削除
appendは、親要素の末尾に新しい子要素を追加します。appendChildとほぼ同じですが、appendは複数の要素やテキストを同時に追加できる点が便利です。
prependは、親要素の最初に新しい子要素を追加します。既存の子要素がある場合、その前に挿入されます。
removeは要素自身を削除し、removeChildは親要素から特定の子要素を削除する際に使用します。
const parent = document.getElementById("parent");
// 新しい要素を作成
const newElement = document.createElement("div");
newElement.textContent = "新しい要素";
// 末尾に追加
parent.append(newElement);
// 先頭に追加
parent.prepend(newElement);
// 要素を削除
newElement.remove();
// 親から子要素を削除
parent.removeChild(newElement);
DOMイベント:インタラクティブなページを作る
イベントとは
DOMイベントは、ユーザーの操作(クリック、キー入力、ドラッグなど)に反応してWebページに動きを与える仕組みです。DOM操作と組み合わせることで、ボタンをクリックしたら要素を追加する、入力内容に応じてスタイルを変更するなど、動的なWebアプリケーションを実装できます。
イベントの設定方法:3つのアプローチ
JavaScriptでイベントを設定する方法は主に3つあります。それぞれの特徴を見ていきましょう。
HTMLインライン方式(非推奨)
HTML要素の属性として直接JavaScriptを記述する方法です。
<button onclick="alert('クリックされました')">クリックしてね</button>
<button onclick="alert('クリックされました')">クリックしてね</button>
<button onclick="alert('クリックされました')">クリックしてね</button>
この方式は同じ処理を何度も書く必要があり、コードの保守性が低いため推奨されません。HTMLとJavaScriptの責任を分離する観点からも避けるべき方法です。
onclickプロパティ
JavaScriptからHTML要素のプロパティとしてイベントを設定する方法です。
<button id="myButton">クリックしてね</button>
<script>
const button = document.getElementById("myButton");
button.onclick = function () {
alert("クリックされました");
};
</script>
インライン方式よりは改善されていますが、1つの要素に対して1つのイベントハンドラしか設定できないという制限があります。
addEventListenerメソッド(推奨)
現代的なイベント処理の標準的な方法です。
<button id="myButton">クリックしてね</button>
<script>
const button = document.getElementById("myButton");
button.addEventListener("click", function () {
alert("クリックされました");
});
</script>
addEventListenerのメリット
- 同じ要素に複数のイベントリスナーを追加できる
- イベントの削除が容易(removeEventListenerを使用)
- イベントフェーズ(キャプチャ/バブリング)を制御できる
イベントリスナーの基礎知識
thisキーワードの扱い
イベントリスナー内でのthisは、イベントが発生した要素を参照します。
<button id="myButton">クリックしてね</button>
<script>
const button = document.getElementById("myButton");
button.addEventListener("click", function () {
alert(this.id); // 'myButton'と表示される
});
</script>
ただし、アロー関数を使用した場合、thisは外側のスコープを参照するため注意が必要です。
// 通常の関数:thisはbutton要素を指す
button.addEventListener("click", function () {
console.log(this); // <button id="myButton">
});
// アロー関数:thisは外側のスコープを指す
button.addEventListener("click", () => {
console.log(this); // Windowオブジェクトなど
});
イベントオブジェクト
イベントが発生すると、イベントの詳細情報を含むオブジェクトが自動的に生成されます。このオブジェクトには、クリック座標、押されたキー、イベントの発生元など様々な情報が含まれています。
<button id="myButton">クリックしてね</button>
<script>
const button = document.getElementById("myButton");
button.addEventListener("click", function (event) {
console.log(event.clientX); // クリックされたX座標
console.log(event.clientY); // クリックされたY座標
console.log(event.target); // イベントが発生した要素
});
</script>
さまざまなイベントの種類
クリックイベント
最も基本的なイベントです。マウスクリックやタップ操作で発火します。
element.addEventListener("click", function (event) {
// クリック時の処理
});
キーボードイベント
キーボードの操作を検知するイベントです。テキスト入力やショートカットキーの実装に使用します。
<input type="text" id="myInput" placeholder="ここにタイプしてね" />
<script>
const input = document.getElementById("myInput");
input.addEventListener("keydown", function (event) {
console.log(event.key); // 押されたキーの名前(例:"a", "Enter")
console.log(event.keyCode); // 押されたキーのコード(非推奨)
console.log(event.ctrlKey); // Ctrlキーが押されているか
});
</script>
主なキーボードイベント
-
keydown: キーが押された瞬間 -
keyup: キーが離された瞬間 -
keypress: キーが押されて文字が入力された時(非推奨)
フォームイベント
フォームの送信時に発火するイベントです。デフォルトではページ遷移が発生しますが、preventDefaultメソッドでこれを防ぐことができます。
<form id="myForm">
<input type="text" name="username" placeholder="ユーザー名" />
<button type="submit">送信</button>
</form>
<script>
const form = document.getElementById("myForm");
form.addEventListener("submit", function (event) {
event.preventDefault(); // フォームのデフォルト動作を防ぐ
alert("フォームが送信されました");
});
</script>
inputイベント
ユーザーが入力フィールドの内容を変更したときに発生するイベントです。リアルタイムで入力内容を取得できます。
<input type="text" id="myInput" placeholder="ここにタイプしてね" />
<script>
const input = document.getElementById("myInput");
input.addEventListener("input", function (event) {
console.log(event.target.value); // 現在の入力内容をリアルタイムで表示
});
</script>
検索フィールドの自動補完やリアルタイムバリデーションなどに活用できます。
発展的なトピック
イベントバブリング
イベントバブリングとは、子要素で発生したイベントが親要素へと伝播していく現象です。「バブル(泡)が水中から浮上していく」というイメージで覚えておくと分かりやすいですね。
<div id="parent" style="padding: 20px; border: 1px solid black;">
親要素
<button id="child">子要素</button>
</div>
<script>
const parent = document.getElementById("parent");
const child = document.getElementById("child");
parent.addEventListener("click", function () {
alert("親要素がクリックされました");
});
child.addEventListener("click", function (event) {
alert("子要素がクリックされました");
event.stopPropagation(); // バブリングを停止
});
</script>
event.stopPropagation()を使用することで、イベントの伝播を停止できます。
イベントデリゲーション
ページ読み込み後に動的に生成される要素に対してもイベントを適用したい場合、イベントデリゲーションが有効です。親要素にイベントリスナーを設定し、実際のターゲット要素を判定して処理を行います。
<ul id="itemList">
<li>アイテム 1</li>
<li>アイテム 2</li>
</ul>
<button id="addItem">アイテムを追加</button>
<script>
const itemList = document.getElementById("itemList");
const addItemButton = document.getElementById("addItem");
// 親要素(ul)にイベントリスナーを設定
itemList.addEventListener("click", function (event) {
if (event.target.tagName === "LI") {
alert(event.target.textContent + " がクリックされました");
}
});
// 動的に要素を追加
addItemButton.addEventListener("click", function () {
const newItem = document.createElement("li");
newItem.textContent = "新しいアイテム";
itemList.appendChild(newItem);
});
</script>
イベントデリゲーションのメリット
- 動的に追加された要素にも自動的にイベントが適用される
- 多数の要素に個別にイベントリスナーを設定する必要がなく、パフォーマンスが向上する
- コードの保守性が高まる
まとめ
この記事では、DOM操作とイベント処理の基礎から実践的な組み合わせまでを解説しました。
DOM操作の重要なポイント
- 要素の選択にはquerySelectorを活用
- スタイル変更にはclassListを使用
- テキスト操作ではinnerHTML使用時のセキュリティに注意
- 親子関係を活用した効率的な要素アクセス
イベント処理の重要なポイント
- イベント設定にはaddEventListenerを使用
- イベントオブジェクトから様々な情報を取得
- イベントバブリングの仕組みを理解
- 動的要素にはイベントデリゲーションを活用
DOM操作のAPIは非常に多機能なため、すべてを暗記する必要はありません。基本的な操作パターンを理解し、必要に応じてMDNなどのドキュメントを参照しながら実装していくことが効率的な学習方法です。パフォーマンスとセキュリティを意識しながら、適切なメソッドを選択できるようになりましょう。