前回の記事 では、JavaScriptの組み込みオブジェクトとユーティリティ機能について、文字列、配列、数学、日付、国際化対応の実践的な活用方法を紹介しました。今回は、DOM操作とイベント処理について、モダンなWebアプリケーション開発に必要な技術を中心に紹介します。
Document Object Model(DOM)の基本
DOMは、HTMLやXMLドキュメントの構造をオブジェクトとして表現する仕組みです。JavaScriptからDOMを操作することで、Webページの内容や構造を動的に変更できます。
要素の取得方法(従来)
// IDで要素を取得
const title = document.getElementById('title');
// クラス名で要素を取得(複数の要素)
const buttons = document.getElementsByClassName('button');
// タグ名で要素を取得(複数の要素)
const paragraphs = document.getElementsByTagName('p');
querySelector/querySelectorAllの使用
// querySelector/querySelectorAll を使用
const header = document.querySelector('header');
const navItems = document.querySelectorAll('nav ul li');
// CSSセレクタで要素を取得(最初の1つ)
const firstButton = document.querySelector('.button');
// CSSセレクタで要素を取得(すべて)
const allButtons = document.querySelectorAll('.button');
// 複雑なセレクタも使用可能
const activeMenuItem = document.querySelector('nav ul li.active');
const dataAttributes = document.querySelectorAll('[data-category="electronics"]');
// 要素の存在チェック
const element = document.querySelector('#non-existent');
if (element) {
console.log('要素が存在します');
} else {
console.log('要素が存在しません');
}
要素の内容と属性の操作
// テキスト内容の取得と設定
const message = document.querySelector('#message');
console.log(message.textContent); // テキストのみ取得
console.log(message.innerHTML); // HTMLを含む内容取得
message.textContent = 'こんにちは、世界!';
message.innerHTML = '<strong>重要</strong>なメッセージ';
// 属性の操作
const link = document.querySelector('#external-link');
console.log(link.getAttribute('href'));
link.setAttribute('href', 'https://example.com');
link.setAttribute('target', '_blank');
// datasetの属性の操作
const product = document.querySelector('#product');
console.log(product.dataset.price); // data-price の値
product.dataset.category = 'electronics'; // data-category を設定
// クラスの操作
const button = document.querySelector('#toggle-button');
button.classList.add('active'); // クラスを追加
button.classList.remove('inactive'); // クラスを削除
button.classList.toggle('highlighted'); // クラスをトグル
console.log(button.classList.contains('active')); // クラスの存在チェック(この場合は true)
イベント処理の基本
addEventListenerの使用
// 基本的なイベントリスナー
const button = document.querySelector('#click-button');
button.addEventListener('click', function() {
console.log('ボタンがクリックされました');
});
// アロー関数を使用
button.addEventListener('click', () => {
console.log('ボタンがクリックされました');
});
// 名前付き関数を使用
function handleClick() {
console.log('ボタンがクリックされました');
}
button.addEventListener('click', handleClick);
// イベントリスナーの削除
button.removeEventListener('click', handleClick);
イベントオブジェクトの活用
// イベントオブジェクトから情報を取得
const input = document.querySelector('#text-input');
input.addEventListener('input', function(event) {
console.log('入力値:', event.target.value);
console.log('イベントタイプ:', event.type);
});
// キーボードイベント
document.addEventListener('keydown', function(event) {
console.log('押されたキー:', event.key); // 論理的な値(例: 'a', 'Enter')
console.log('キーコード:', event.code); // 物理的なキーコード(例: 'KeyA', 'Enter')
if (event.key === 'Enter') {
console.log('Enterキーが押されました');
}
if (event.ctrlKey && event.key === 's') {
event.preventDefault(); // ブラウザのデフォルト動作を防ぐ
console.log('Ctrl+Sが押されました');
}
});
// マウスイベント
const box = document.querySelector('#interactive-box');
box.addEventListener('mouseenter', function(event) {
console.log('マウスが要素に入りました');
event.target.style.backgroundColor = '#f0f0f0';
});
box.addEventListener('mouseleave', function(event) {
console.log('マウスが要素から離れました');
event.target.style.backgroundColor = '';
});
フォーム操作とバリデーション
// フォーム送信の処理
const form = document.querySelector('#contact-form');
form.addEventListener('submit', function(event) {
event.preventDefault(); // フォームの送信を防ぐ
const formData = new FormData(form); // XMLHttpRequestやfetchで送信するためのFormDataオブジェクトを作成
const name = formData.get('name');
const email = formData.get('email');
if (validateForm(name, email)) {
console.log('フォームが正常に送信されました');
// 実際の送信処理(省略)
}
});
// バリデーション関数
function validateForm(name, email) {
const errors = [];
if (!name || name.trim().length < 2) {
errors.push('名前は2文字以上入力してください');
}
if (!email || !email.includes('@')) {
errors.push('有効なメールアドレスを入力してください');
}
if (errors.length > 0) {
displayErrors(errors);
return false;
}
return true;
}
function displayErrors(errors) {
const errorContainer = document.querySelector('#error-messages');
errorContainer.innerHTML = '';
errors.forEach(error => {
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = error;
errorContainer.appendChild(errorElement);
});
}
// リアルタイムバリデーション
const nameInput = document.querySelector('#name');
nameInput.addEventListener('input', function(event) {
const value = event.target.value;
const feedback = document.querySelector('#name-feedback');
if (value.length < 2) {
feedback.textContent = '2文字以上入力してください';
feedback.className = 'error';
} else {
feedback.textContent = '入力内容に問題ありません';
feedback.className = 'success';
}
});
イベント委譲(Event Delegation)
委譲(Delegation)とは、親要素にイベントリスナーを設定し、子要素のイベントを一括で管理する手法です。これにより、動的に追加される要素にも対応できます。
// 効率的なイベント委譲
const todoList = document.querySelector('#todo-list');
// 親要素にイベントリスナーを設定
todoList.addEventListener('click', function(event) {
// 削除ボタンがクリックされた場合
if (event.target.classList.contains('delete-button')) {
const todoItem = event.target.closest('.todo-item');
todoItem.remove();
}
// 完了チェックボックスがクリックされた場合
if (event.target.classList.contains('complete-checkbox')) {
const todoItem = event.target.closest('.todo-item');
todoItem.classList.toggle('completed');
}
});
// 新しいTodoアイテムを動的に追加
function addTodoItem(text) {
const todoItem = document.createElement('div');
todoItem.className = 'todo-item';
todoItem.innerHTML = `
<input type="checkbox" class="complete-checkbox">
<span class="todo-text">${text}</span>
<button class="delete-button">削除</button>
`;
todoList.appendChild(todoItem);
}
// 使用例
addTodoItem('買い物に行く');
addTodoItem('報告書を作成する');
要素の動的生成と操作
// 要素の作成と追加
function createProductCard(product) {
// 要素の作成
const card = document.createElement('div');
card.className = 'product-card';
const image = document.createElement('img');
image.src = product.image;
image.alt = product.name;
const title = document.createElement('h3');
title.textContent = product.name;
const price = document.createElement('p');
price.className = 'price';
price.textContent = `¥${product.price.toLocaleString()}`;
const button = document.createElement('button');
button.textContent = 'カートに追加';
button.addEventListener('click', () => {
addToCart(product);
});
// 要素の組み立て
card.appendChild(image);
card.appendChild(title);
card.appendChild(price);
card.appendChild(button);
return card;
}
// 複数の商品を表示
function displayProducts(products) {
const container = document.querySelector('#product-container');
container.innerHTML = ''; // 既存の内容をクリア
products.forEach(product => {
const card = createProductCard(product);
container.appendChild(card);
});
}
// 使用例
const products = [
{ id: 1, name: 'ノートPC', price: 85000, image: 'laptop.jpg' },
{ id: 2, name: 'マウス', price: 3000, image: 'mouse.jpg' },
{ id: 3, name: 'キーボード', price: 8000, image: 'keyboard.jpg' }
];
displayProducts(products);
実践的なコード例
インタラクティブなタブUI
// タブUIの実装
class TabController {
constructor(tabContainer) {
this.container = tabContainer;
this.tabs = this.container.querySelectorAll('.tab');
this.contents = this.container.querySelectorAll('.tab-content');
this.init();
}
init() {
// タブクリックイベントの設定
this.tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
this.showTab(index);
});
});
// 最初のタブを表示
this.showTab(0);
}
showTab(index) {
// すべてのタブとコンテンツを非アクティブにする
this.tabs.forEach(tab => tab.classList.remove('active'));
this.contents.forEach(content => content.classList.remove('active'));
// 指定されたタブとコンテンツをアクティブにする
this.tabs[index].classList.add('active');
this.contents[index].classList.add('active');
}
}
// 使用例
const tabContainer = document.querySelector('#main-tabs');
new TabController(tabContainer);
復習
1. 以下のHTMLに対して、「Hello, World!」というテキストを表示するJavaScriptコードを書く
<div id="output"></div>
2. ボタンクリック時に、入力フィールドの値を取得して表示する処理を書く
<input type="text" id="userInput" placeholder="何か入力してください">
<button id="showButton">表示</button>
<div id="result"></div>
3. 以下のHTMLで、リストアイテムをクリックしたときに、そのテキストをコンソールに出力する処理を書く
<ul id="itemList">
<li>アイテム1</li>
<li>アイテム2</li>
<li>アイテム3</li>
</ul>
※ヒント:tagNameを使用して、クリックされた要素を特定できます。
参考:Element: tagName プロパティ(MDN)
解答例
問題1
const output = document.getElementById('output');
output.textContent = 'Hello, World!';
問題2
const userInput = document.getElementById('userInput');
const showButton = document.getElementById('showButton');
const result = document.getElementById('result');
showButton.addEventListener('click', function() {
const value = userInput.value;
result.textContent = value;
});
問題3
const itemList = document.getElementById('itemList');
itemList.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log(event.target.textContent);
}
});
まとめ
DOM操作とイベント処理について、モダンなWebアプリケーション開発に必要な技術を紹介しました。
技術的メリット
-
柔軟なDOM操作
-
querySelector/querySelectorAllによる柔軟な要素選択 -
classListによるクラス操作 -
datasetによるデータ属性アクセス
-
-
モダンなイベント処理
-
addEventListenerによる複数イベントリスナー対応 - イベントオブジェクトによる詳細な情報取得
-
preventDefaultによるデフォルト動作の制御
-
-
パフォーマンスの向上
- イベント委譲によるイベント管理
- 動的要素に対する柔軟な対応
-
ユーザビリティの向上
- リアルタイムなフィードバック
- キーボード操作対応
- アクセシビリティへの配慮
活用場面の例
-
フォームの検証と送信
リアルタイムバリデーションとユーザビリティの向上 -
動的なUI構築
タブ、モーダル、フィルタリング機能の実装 -
インタラクティブなコンテンツ
ギャラリー、ToDoリスト、ダッシュボード -
ユーザー体験の向上
スムーズなアニメーション、レスポンシブな操作
注意すべきポイント
DOM操作は頻繁に行なうとパフォーマンスに影響するため、必要最小限に抑え、可能な限りバッチ処理を心がけることが重要です。
イベントリスナーの追加時は、メモリリークを防ぐために、不要になったリスナーは適切に削除することを忘れないでください。
また、現代のWebアプリケーション開発では、ReactやVue.jsなどのフレームワークを使用することが多いです。しかし、これらのフレームワークの理解を深めるためにも、基本的なDOM操作とイベント処理を確実に理解しておくことが大切です。
次回 は、非同期処理(Promise / async,await)について、実践的なAPI通信を含めて紹介します。