0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

新・JavaScript文法(10):DOM操作とイベント

Last updated at Posted at 2025-08-04

前回の記事 では、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アプリケーション開発に必要な技術を紹介しました。

技術的メリット

  1. 柔軟なDOM操作
    • querySelector/querySelectorAll による柔軟な要素選択
    • classList によるクラス操作
    • dataset によるデータ属性アクセス
  2. モダンなイベント処理
    • addEventListener による複数イベントリスナー対応
    • イベントオブジェクトによる詳細な情報取得
    • preventDefault によるデフォルト動作の制御
  3. パフォーマンスの向上
    • イベント委譲によるイベント管理
    • 動的要素に対する柔軟な対応
  4. ユーザビリティの向上
    • リアルタイムなフィードバック
    • キーボード操作対応
    • アクセシビリティへの配慮

活用場面の例

  • フォームの検証と送信
    リアルタイムバリデーションとユーザビリティの向上
  • 動的なUI構築
    タブ、モーダル、フィルタリング機能の実装
  • インタラクティブなコンテンツ
    ギャラリー、ToDoリスト、ダッシュボード
  • ユーザー体験の向上
    スムーズなアニメーション、レスポンシブな操作

注意すべきポイント

DOM操作は頻繁に行なうとパフォーマンスに影響するため、必要最小限に抑え、可能な限りバッチ処理を心がけることが重要です。

イベントリスナーの追加時は、メモリリークを防ぐために、不要になったリスナーは適切に削除することを忘れないでください。

また、現代のWebアプリケーション開発では、ReactやVue.jsなどのフレームワークを使用することが多いです。しかし、これらのフレームワークの理解を深めるためにも、基本的なDOM操作とイベント処理を確実に理解しておくことが大切です。


次回 は、非同期処理(Promise / async,await)について、実践的なAPI通信を含めて紹介します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?