【初心者向け】querySelectorの使い方 - 要素選択の基本
JavaScriptのquerySelector・querySelectorAllメソッドについて初心者向けに詳しく解説。基本的な使い方からCSSセレクタの活用、DOM操作の実践的な例まで具体的なコード例で学びます。
みなさん、JavaScriptでWebページの要素を操作していますか?
「getElementById以外にどんな方法があるの?」「CSSセレクタでJavaScriptから要素を選択できないの?」と思ったことはありませんか?
そんな疑問をお持ちの方に朗報です! querySelectorとquerySelectorAllという、とても便利なメソッドがあるんです。
querySelectorとquerySelectorAllは、CSSセレクタを使ってHTML要素を柔軟に選択できる便利なメソッドです。 従来のgetElementByIdやgetElementsByClassNameよりも直感的で、CSSの知識をそのまま活用できます。
この記事では、querySelector・querySelectorAllの使い方について詳しく解説します。 基本的な概念から実践的な活用方法まで、具体的なコード例を交えて初心者向けに分かりやすく説明していきます。
きっと「こんなに簡単に要素を選択できるんだ!」と感じられるはずですよ。
querySelectorって何?基本を理解しよう
querySelectorとは
querySelectorは、CSSセレクタを使ってHTML要素を選択するJavaScriptのメソッドです。 CSSでスタイルを適用する時と同じ記法で、JavaScriptからDOM要素にアクセスできます。
// 基本的な使い方const element = document.querySelector('セレクタ');
// IDで選択const header = document.querySelector('#header');
// クラスで選択const button = document.querySelector('.btn');
// タグ名で選択const firstParagraph = document.querySelector('p');
// 属性で選択const emailInput = document.querySelector('input[type="email"]');
// 複合セレクタconst navLink = document.querySelector('nav a.active');
// 疑似クラスconst firstChild = document.querySelector('li:first-child');
基本の書き方はとても簡単です。
document.querySelector()
の括弧内に、CSSセレクタを文字列で指定するだけです。
querySelectorとquerySelectorAllの違い
この2つのメソッドには、重要な違いがあります。
// querySelector:最初の1つの要素を取得const firstButton = document.querySelector('.button');console.log(firstButton); // HTMLElement または null
// querySelectorAll:該当するすべての要素を取得const allButtons = document.querySelectorAll('.button');console.log(allButtons); // NodeList(配列のようなオブジェクト)
具体的な例で見てみましょう。
// HTMLサンプル/*<div class="container"> <button class="button primary">ボタン1</button> <button class="button secondary">ボタン2</button> <button class="button primary">ボタン3</button></div>*/
// 1つ目のボタンのみ取得const singleButton = document.querySelector('.button');console.log(singleButton.textContent); // "ボタン1"
// 全てのボタンを取得const multipleButtons = document.querySelectorAll('.button');console.log(multipleButtons.length); // 3
querySelectorは最初の要素だけを返します。 querySelectorAllは該当する全ての要素をNodeListで返します。
NodeListの処理方法も確認しましょう。
// NodeListをforEachで処理multipleButtons.forEach((button, index) => { console.log(`ボタン${index + 1}: ${button.textContent}`);});
// NodeListを配列に変換const buttonArray = Array.from(multipleButtons);const buttonTexts = buttonArray.map(btn => btn.textContent);console.log(buttonTexts); // ["ボタン1", "ボタン2", "ボタン3"]
NodeListは配列によく似ていますが、配列の全てのメソッドが使えるわけではありません。
Array.from()
で配列に変換すると、mapやfilterなどのメソッドも使えます。
従来のメソッドとの比較
従来のメソッドとquerySelectorの違いを見てみましょう。
// HTMLサンプル/*<div id="main-content"> <h1 class="title">メインタイトル</h1> <div class="content"> <p class="text important">重要なテキスト</p> <p class="text">通常のテキスト</p> </div> <ul class="list"> <li data-id="1">項目1</li> <li data-id="2">項目2</li> </ul></div>*/
// 従来のメソッドconst mainById = document.getElementById('main-content');const titlesByClass = document.getElementsByClassName('title');const paragraphsByTag = document.getElementsByTagName('p');
// querySelector/querySelectorAllconst mainByQuery = document.querySelector('#main-content');const titleByQuery = document.querySelector('.title');const paragraphsByQuery = document.querySelectorAll('p');
querySelectorの便利さがより分かる例を見てみましょう。
// 複雑な選択が簡単にconst importantText = document.querySelector('.content .text.important');const firstListItem = document.querySelector('.list li:first-child');const itemById = document.querySelector('li[data-id="2"]');
// 従来のメソッドでは複雑になる選択// .content内の.importantクラスの<p>要素const contentDiv = document.querySelector('.content');const importantParagraphs = contentDiv.querySelectorAll('p.important');
console.log('従来:', titlesByClass[0]); // HTMLCollectionconsole.log('querySelector:', titleByQuery); // HTMLElementconsole.log('複雑な選択:', importantText.textContent);
従来のメソッドでは複雑だった選択が、querySelectorなら1行で書けます。 CSSセレクタの知識をそのまま活用できるのが大きなメリットです。
基本的なセレクタの使い方
IDセレクタでピンポイント選択
IDセレクタは、特定の要素を1つだけ選択するときに使います。
// HTMLサンプル/*<div id="app"> <header id="main-header">ヘッダー</header> <main id="content">メインコンテンツ</main> <footer id="site-footer">フッター</footer></div>*/
// IDで要素を選択(#を使用)const app = document.querySelector('#app');const header = document.querySelector('#main-header');const content = document.querySelector('#content');
要素が存在するかどうかのチェックも重要です。
// 要素の存在確認if (app) { console.log('アプリ要素が見つかりました'); app.style.backgroundColor = '#f0f0f0';} else { console.log('アプリ要素が見つかりません');}
より実用的な例も見てみましょう。
// 安全な要素操作function updateElementText(elementId, newText) { const element = document.querySelector(`#${elementId}`); if (element) { element.textContent = newText; return true; } else { console.warn(`要素 #${elementId} が見つかりません`); return false; }}
// 使用例updateElementText('main-header', '新しいヘッダーテキスト');updateElementText('content', '更新されたコンテンツ');updateElementText('nonexistent', 'エラーテスト'); // 警告が出力される
この関数を使うと、要素が存在しない場合でもエラーにならず安全に処理できます。
複数のIDを一度に処理することもできます。
// 複数のIDを一度に処理const elementIds = ['main-header', 'content', 'site-footer'];const elements = elementIds.map(id => ({ id: id, element: document.querySelector(`#${id}`)})).filter(item => item.element !== null);
elements.forEach(item => { console.log(`要素 ${item.id} を処理中`); item.element.setAttribute('data-processed', 'true');});
存在する要素だけを取得して、まとめて処理しています。
クラスセレクタで柔軟な選択
クラスセレクタは、同じスタイルや機能を持つ要素をまとめて選択するときに便利です。
// HTMLサンプル/*<div class="container"> <button class="btn primary">プライマリボタン</button> <button class="btn secondary">セカンダリボタン</button> <button class="btn danger">デンジャーボタン</button> <div class="card"> <button class="btn small">小さなボタン</button> </div></div>*/
// クラスで要素を選択(.を使用)const firstButton = document.querySelector('.btn');const allButtons = document.querySelectorAll('.btn');
console.log('最初のボタン:', firstButton.textContent);console.log('ボタンの総数:', allButtons.length);
複数のクラスを持つ要素も簡単に選択できます。
// 複数クラスを持つ要素const primaryButton = document.querySelector('.btn.primary');const smallButton = document.querySelector('.btn.small');
実際のイベント処理での活用例を見てみましょう。
// すべてのボタンにイベントリスナーを追加allButtons.forEach((button, index) => { button.addEventListener('click', function() { console.log(`ボタン${index + 1}がクリックされました: ${this.textContent}`); // クリックされたボタンにクラスを追加 this.classList.add('clicked'); // 他のボタンからclickedクラスを削除 allButtons.forEach(btn => { if (btn !== this) { btn.classList.remove('clicked'); } }); });});
この例では、クリックされたボタンのみにactiveクラスを付けています。
より具体的な使用例も確認しましょう。
// 特定のクラスの組み合わせconst dangerButtons = document.querySelectorAll('.btn.danger');const cardButtons = document.querySelectorAll('.card .btn');
console.log('デンジャーボタン数:', dangerButtons.length);console.log('カード内のボタン数:', cardButtons.length);
// クラスベースの要素操作function toggleButtonState(className) { const buttons = document.querySelectorAll(`.btn.${className}`); buttons.forEach(button => { if (button.disabled) { button.disabled = false; button.textContent = button.textContent.replace(' (無効)', ''); } else { button.disabled = true; button.textContent += ' (無効)'; } });}
// 使用例toggleButtonState('primary'); // プライマリボタンの有効/無効を切り替え
クラス名を指定して、特定の種類のボタンの状態を一括で変更できます。
タグセレクタで要素の種類を指定
タグセレクタは、特定のHTML要素を全て選択したいときに使います。
// HTMLサンプル/*<article> <h1>記事のタイトル</h1> <h2>サブタイトル1</h2> <p>最初の段落です。</p> <p>2番目の段落です。</p> <h2>サブタイトル2</h2> <p>3番目の段落です。</p> <ul> <li>リスト項目1</li> <li>リスト項目2</li> <li>リスト項目3</li> </ul></article>*/
// タグ名で選択const firstHeading = document.querySelector('h1');const allHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');const allParagraphs = document.querySelectorAll('p');const allListItems = document.querySelectorAll('li');
console.log('メインタイトル:', firstHeading.textContent);console.log('見出しの数:', allHeadings.length);console.log('段落の数:', allParagraphs.length);
カンマ区切りで複数のタグを同時に選択することもできます。
タグを使った実用的な処理例を見てみましょう。
// 特定のタグの内容を処理allParagraphs.forEach((paragraph, index) => { paragraph.setAttribute('data-paragraph', index + 1); paragraph.style.marginBottom = '1em';});
// 見出しレベルの分析const headingLevels = {};allHeadings.forEach(heading => { const level = heading.tagName.toLowerCase(); headingLevels[level] = (headingLevels[level] || 0) + 1;});
console.log('見出しレベル分析:', headingLevels);
見出しの種類ごとに個数を数えて、記事の構造を分析しています。
より高度な例として、目次の自動生成も見てみましょう。
// タグベースの目次生成function generateTOC() { const headings = document.querySelectorAll('h1, h2, h3'); const toc = document.createElement('div'); toc.className = 'table-of-contents'; const tocTitle = document.createElement('h3'); tocTitle.textContent = '目次'; toc.appendChild(tocTitle); const tocList = document.createElement('ul'); headings.forEach((heading, index) => { const id = `heading-${index + 1}`; heading.id = id; const listItem = document.createElement('li'); const link = document.createElement('a'); link.href = `#${id}`; link.textContent = heading.textContent; link.className = `toc-${heading.tagName.toLowerCase()}`; listItem.appendChild(link); tocList.appendChild(listItem); }); toc.appendChild(tocList); return toc;}
// 目次を記事の最初に挿入const article = document.querySelector('article');if (article) { const toc = generateTOC(); article.insertBefore(toc, article.firstChild);}
この例では、記事内の見出しを自動で検出して目次を作成しています。 querySelectorAllでh1〜h3を一度に取得して、それぞれにリンクを作成しています。
高度なセレクタの活用方法
属性セレクタで細かい条件指定
属性セレクタを使うと、HTML要素の属性に基づいて選択できます。
// HTMLサンプル/*<form> <input type="text" name="username" placeholder="ユーザー名"> <input type="email" name="email" placeholder="メールアドレス" required> <input type="password" name="password" placeholder="パスワード" required> <input type="submit" value="送信"> <input type="reset" value="リセット"> <button type="button" data-action="cancel">キャンセル</button></form>*/
// 属性で選択const emailInput = document.querySelector('input[type="email"]');const requiredInputs = document.querySelectorAll('input[required]');const submitButton = document.querySelector('input[type="submit"]');const cancelButton = document.querySelector('[data-action="cancel"]');
console.log('メール入力欄:', emailInput.name);console.log('必須入力欄数:', requiredInputs.length);
属性セレクタは、type属性やrequired属性など、HTML要素の詳細な条件で選択できます。
より複雑な属性選択も可能です。
// 部分一致での属性選択const textInputs = document.querySelectorAll('input[type^="text"]'); // textで始まるconst emailInputs = document.querySelectorAll('input[type$="email"]'); // emailで終わるconst dataInputs = document.querySelectorAll('input[name*="data"]'); // dataを含む
// 属性値による分類function categorizeInputs() { const inputs = document.querySelectorAll('input'); const categories = { text: [], password: [], email: [], submit: [], other: [] }; inputs.forEach(input => { const type = input.type; if (categories[type]) { categories[type].push(input); } else { categories.other.push(input); } }); return categories;}
const inputCategories = categorizeInputs();console.log('入力欄の分類:', inputCategories);
この例では、input要素をtype属性で分類しています。
疑似クラスセレクタで状態や位置を指定
疑似クラスセレクタを使うと、要素の状態や位置で選択できます。
// HTMLサンプル/*<ul class="menu"> <li><a href="#home">ホーム</a></li> <li><a href="#about">概要</a></li> <li><a href="#services">サービス</a></li> <li><a href="#contact">お問い合わせ</a></li></ul><div class="content"> <p>最初の段落</p> <p>2番目の段落</p> <p>3番目の段落</p> <p>最後の段落</p></div>*/
// 疑似クラスで選択const firstMenuItem = document.querySelector('.menu li:first-child');const lastMenuItem = document.querySelector('.menu li:last-child');const secondMenuItem = document.querySelector('.menu li:nth-child(2)');
const firstParagraph = document.querySelector('.content p:first-of-type');const lastParagraph = document.querySelector('.content p:last-of-type');
console.log('最初のメニュー:', firstMenuItem.textContent);console.log('最後のメニュー:', lastMenuItem.textContent);console.log('2番目のメニュー:', secondMenuItem.textContent);
:first-child
、:last-child
、:nth-child()
などで、要素の位置を指定できます。
より実用的な例も見てみましょう。
// 偶数・奇数の行に異なるスタイルを適用const evenItems = document.querySelectorAll('.menu li:nth-child(even)');const oddItems = document.querySelectorAll('.menu li:nth-child(odd)');
evenItems.forEach(item => { item.style.backgroundColor = '#f0f0f0';});
oddItems.forEach(item => { item.style.backgroundColor = '#ffffff';});
// 複数の疑似クラスを組み合わせconst notFirstParagraphs = document.querySelectorAll('.content p:not(:first-child)');notFirstParagraphs.forEach(p => { p.style.marginTop = '1em';});
console.log('最初以外の段落数:', notFirstParagraphs.length);
:not()
疑似クラスを使うと、特定の条件を除外して選択できます。
複合セレクタで複雑な条件を指定
複数のセレクタを組み合わせることで、より具体的な条件で要素を選択できます。
// HTMLサンプル/*<div class="container"> <header class="site-header"> <nav class="main-nav"> <ul> <li><a href="#" class="nav-link active">ホーム</a></li> <li><a href="#" class="nav-link">サービス</a></li> <li><a href="#" class="nav-link">お問い合わせ</a></li> </ul> </nav> </header> <main class="content"> <article class="post featured"> <h2 class="post-title">注目記事</h2> <p class="post-content">記事の内容...</p> </article> <article class="post"> <h2 class="post-title">通常記事</h2> <p class="post-content">記事の内容...</p> </article> </main></div>*/
// 子孫セレクタ(スペース区切り)const navLinks = document.querySelectorAll('.main-nav .nav-link');const postTitles = document.querySelectorAll('.content .post-title');
// 直接の子セレクタ(>)const directChildren = document.querySelectorAll('.container > header');const navItems = document.querySelectorAll('.main-nav > ul > li');
// 隣接セレクタ(+)const nextSibling = document.querySelector('h2 + p');
// 兄弟セレクタ(~)const allSiblings = document.querySelectorAll('h2 ~ p');
console.log('ナビリンク数:', navLinks.length);console.log('記事タイトル数:', postTitles.length);console.log('直接の子要素:', directChildren.length);
子孫セレクタ、直接の子セレクタ、隣接セレクタなど、要素の関係性で選択できます。
実用的な例として、アクティブなナビゲーションの処理を見てみましょう。
// アクティブなナビゲーション処理function setActiveNavigation(targetHref) { // 全てのナビリンクからactiveクラスを削除 const allNavLinks = document.querySelectorAll('.main-nav .nav-link'); allNavLinks.forEach(link => { link.classList.remove('active'); }); // 指定されたリンクにactiveクラスを追加 const targetLink = document.querySelector(`.main-nav .nav-link[href="${targetHref}"]`); if (targetLink) { targetLink.classList.add('active'); }}
// 使用例setActiveNavigation('#services');
// 注目記事の特別な処理const featuredPosts = document.querySelectorAll('.content .post.featured');featuredPosts.forEach(post => { const title = post.querySelector('.post-title'); if (title) { title.style.color = '#e74c3c'; title.style.fontWeight = 'bold'; }});
console.log('注目記事数:', featuredPosts.length);
複合セレクタを使うことで、具体的な条件を満たす要素だけを正確に選択できます。
実践的なDOM操作例
フォームバリデーションの実装
querySelectorを使った実用的なフォームバリデーションを作ってみましょう。
// HTMLサンプル/*<form id="user-form" class="validation-form"> <div class="form-group"> <label for="username">ユーザー名</label> <input type="text" id="username" name="username" required> <span class="error-message"></span> </div> <div class="form-group"> <label for="email">メールアドレス</label> <input type="email" id="email" name="email" required> <span class="error-message"></span> </div> <div class="form-group"> <label for="password">パスワード</label> <input type="password" id="password" name="password" required> <span class="error-message"></span> </div> <button type="submit">送信</button></form>*/
// フォームバリデーションクラスclass FormValidator { constructor(formSelector) { this.form = document.querySelector(formSelector); this.init(); } init() { if (!this.form) return; // 送信イベントの処理 this.form.addEventListener('submit', (e) => { e.preventDefault(); this.validateForm(); }); // リアルタイムバリデーション const inputs = this.form.querySelectorAll('input[required]'); inputs.forEach(input => { input.addEventListener('blur', () => { this.validateField(input); }); }); } validateForm() { const requiredFields = this.form.querySelectorAll('input[required]'); let isValid = true; requiredFields.forEach(field => { if (!this.validateField(field)) { isValid = false; } }); if (isValid) { this.showSuccess('フォームの送信が完了しました!'); this.form.reset(); } return isValid; } validateField(field) { const value = field.value.trim(); const fieldType = field.type; const errorElement = field.parentElement.querySelector('.error-message'); // エラーメッセージをクリア this.clearError(field, errorElement); // 必須チェック if (!value) { this.showError(field, errorElement, 'この項目は必須です'); return false; } // タイプ別のバリデーション switch (fieldType) { case 'email': if (!this.isValidEmail(value)) { this.showError(field, errorElement, '正しいメールアドレスを入力してください'); return false; } break; case 'password': if (value.length < 6) { this.showError(field, errorElement, 'パスワードは6文字以上で入力してください'); return false; } break; } return true; } isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } showError(field, errorElement, message) { field.classList.add('error'); errorElement.textContent = message; errorElement.style.color = '#e74c3c'; } clearError(field, errorElement) { field.classList.remove('error'); errorElement.textContent = ''; } showSuccess(message) { const existingMessage = this.form.querySelector('.success-message'); if (existingMessage) { existingMessage.remove(); } const successDiv = document.createElement('div'); successDiv.className = 'success-message'; successDiv.textContent = message; successDiv.style.color = '#27ae60'; successDiv.style.padding = '10px'; successDiv.style.marginTop = '10px'; this.form.appendChild(successDiv); setTimeout(() => { successDiv.remove(); }, 3000); }}
// フォームバリデーターの初期化const validator = new FormValidator('#user-form');
ちょっと長いコードですね。でも大丈夫です! 一つずつ見ていきましょう。
まず、初期化部分から。
constructor(formSelector) { this.form = document.querySelector(formSelector); this.init();}
document.querySelector()
でフォーム要素を取得して保存しています。
次に、イベントリスナーの設定を見てみましょう。
// 送信イベントの処理this.form.addEventListener('submit', (e) => { e.preventDefault(); this.validateForm();});
// リアルタイムバリデーションconst inputs = this.form.querySelectorAll('input[required]');inputs.forEach(input => { input.addEventListener('blur', () => { this.validateField(input); });});
querySelectorAll('input[required]')
で必須項目だけを取得して、それぞれにイベントリスナーを設定しています。
動的コンテンツの生成
querySelectorを使って動的にコンテンツを生成する例を見てみましょう。
// データからカード要素を生成class CardGenerator { constructor(containerSelector) { this.container = document.querySelector(containerSelector); } generateCards(data) { if (!this.container) return; // 既存のカードをクリア this.container.innerHTML = ''; data.forEach(item => { const card = this.createCard(item); this.container.appendChild(card); }); } createCard(data) { const card = document.createElement('div'); card.className = 'card'; card.setAttribute('data-id', data.id); card.innerHTML = ` <div class="card-header"> <h3 class="card-title">${data.title}</h3> </div> <div class="card-body"> <p class="card-description">${data.description}</p> <div class="card-actions"> <button class="btn btn-primary" data-action="view" data-id="${data.id}"> 詳細を見る </button> <button class="btn btn-secondary" data-action="edit" data-id="${data.id}"> 編集 </button> </div> </div> `; return card; } setupEventListeners() { // イベント委譲を使用 this.container.addEventListener('click', (e) => { const button = e.target.closest('button[data-action]'); if (!button) return; const action = button.getAttribute('data-action'); const id = button.getAttribute('data-id'); this.handleCardAction(action, id); }); } handleCardAction(action, id) { const card = this.container.querySelector(`[data-id="${id}"]`); switch (action) { case 'view': this.viewCard(card, id); break; case 'edit': this.editCard(card, id); break; } } viewCard(card, id) { card.classList.add('highlighted'); console.log(`カード ${id} の詳細を表示`); setTimeout(() => { card.classList.remove('highlighted'); }, 2000); } editCard(card, id) { const title = card.querySelector('.card-title'); const description = card.querySelector('.card-description'); const newTitle = prompt('新しいタイトルを入力:', title.textContent); const newDescription = prompt('新しい説明を入力:', description.textContent); if (newTitle) title.textContent = newTitle; if (newDescription) description.textContent = newDescription; console.log(`カード ${id} を編集しました`); }}
// 使用例const cardData = [ {id: 1, title: 'カード1', description: '最初のカードです'}, {id: 2, title: 'カード2', description: '2番目のカードです'}, {id: 3, title: 'カード3', description: '3番目のカードです'}];
const generator = new CardGenerator('#card-container');generator.generateCards(cardData);generator.setupEventListeners();
この例では、データ配列からHTMLカードを動的に生成しています。
querySelector()
とquerySelectorAll()
を使って、生成した要素に対して操作を行っています。
検索・フィルタリング機能の実装
最後に、querySelectorを活用した検索・フィルタリング機能を作ってみましょう。
// 検索・フィルタリングクラスclass ContentFilter { constructor(searchInputSelector, contentSelector) { this.searchInput = document.querySelector(searchInputSelector); this.contentContainer = document.querySelector(contentSelector); this.allItems = []; this.init(); } init() { if (!this.searchInput || !this.contentContainer) return; // 全アイテムを取得 this.updateItemList(); // 検索イベント this.searchInput.addEventListener('input', () => { this.performSearch(); }); // フィルターボタンがあれば設定 const filterButtons = document.querySelectorAll('[data-filter]'); filterButtons.forEach(button => { button.addEventListener('click', () => { this.applyFilter(button.getAttribute('data-filter')); this.updateActiveFilter(button); }); }); } updateItemList() { this.allItems = Array.from(this.contentContainer.querySelectorAll('.item')); } performSearch() { const searchTerm = this.searchInput.value.toLowerCase().trim(); this.allItems.forEach(item => { const text = item.textContent.toLowerCase(); const matches = text.includes(searchTerm); if (matches) { item.style.display = ''; this.highlightSearchTerm(item, searchTerm); } else { item.style.display = 'none'; this.removeHighlight(item); } }); this.updateResultCount(); } applyFilter(filterType) { this.allItems.forEach(item => { if (filterType === 'all') { item.style.display = ''; } else { const matchesFilter = item.classList.contains(filterType) || item.getAttribute('data-category') === filterType; item.style.display = matchesFilter ? '' : 'none'; } }); this.updateResultCount(); } highlightSearchTerm(item, searchTerm) { if (!searchTerm) { this.removeHighlight(item); return; } const textElements = item.querySelectorAll('.searchable-text'); textElements.forEach(element => { const originalText = element.getAttribute('data-original-text') || element.textContent; element.setAttribute('data-original-text', originalText); const regex = new RegExp(`(${searchTerm})`, 'gi'); const highlightedText = originalText.replace(regex, '<mark>$1</mark>'); element.innerHTML = highlightedText; }); } removeHighlight(item) { const textElements = item.querySelectorAll('.searchable-text'); textElements.forEach(element => { const originalText = element.getAttribute('data-original-text'); if (originalText) { element.textContent = originalText; } }); } updateActiveFilter(activeButton) { const allFilterButtons = document.querySelectorAll('[data-filter]'); allFilterButtons.forEach(button => { button.classList.remove('active'); }); activeButton.classList.add('active'); } updateResultCount() { const visibleItems = this.allItems.filter(item => item.style.display !== 'none' ); const countElement = document.querySelector('.result-count'); if (countElement) { countElement.textContent = `${visibleItems.length}件の結果`; } }}
// フィルター機能の初期化const filter = new ContentFilter('#search-input', '#content-list');
この検索機能では、querySelectorAll()
で検索対象の要素を取得し、検索条件に合わせて表示・非表示を切り替えています。
querySelectorを使うことで、複雑なDOM操作も直感的に実装できます。
パフォーマンスの考慮事項
効率的なセレクタの書き方
querySelectorを使うときは、パフォーマンスも考慮する必要があります。
// 効率的なセレクタの例
// 良い例:具体的で効率的const specificButton = document.querySelector('#submit-btn');const navItems = document.querySelectorAll('.main-nav .nav-item');
// 避けるべき例:非効率的const inefficientSelection = document.querySelectorAll('* .button'); // 全要素をチェックconst complexSelector = document.querySelectorAll('div > p + span ~ a'); // 複雑すぎる
// 改善案const efficientSelection = document.querySelectorAll('.button');const containerButtons = document.querySelector('.container').querySelectorAll('.button');
セレクタはできるだけ具体的で簡潔に書きましょう。
要素の再利用とキャッシュ
同じ要素を繰り返し取得するのは非効率的です。
// 悪い例:毎回要素を取得function updateContent() { document.querySelector('#title').textContent = 'タイトル'; document.querySelector('#title').style.color = 'blue'; document.querySelector('#title').addEventListener('click', handleClick);}
// 良い例:要素をキャッシュfunction updateContentEfficiently() { const title = document.querySelector('#title'); title.textContent = 'タイトル'; title.style.color = 'blue'; title.addEventListener('click', handleClick);}
// さらに良い例:グローバルキャッシュclass UIManager { constructor() { this.elements = { title: document.querySelector('#title'), content: document.querySelector('#content'), sidebar: document.querySelector('#sidebar') }; } updateTitle(text) { if (this.elements.title) { this.elements.title.textContent = text; } } updateContent(html) { if (this.elements.content) { this.elements.content.innerHTML = html; } }}
const ui = new UIManager();ui.updateTitle('新しいタイトル');
要素を一度取得したら変数に保存して再利用することが重要です。
DOMが変更される場合の注意点
動的にDOM要素が追加・削除される場合は注意が必要です。
// 動的要素への対応class DynamicContentHandler { constructor() { this.container = document.querySelector('#dynamic-container'); this.setupEventDelegation(); } // イベント委譲を使用(推奨) setupEventDelegation() { this.container.addEventListener('click', (e) => { // 動的に追加された要素でも動作する if (e.target.matches('.dynamic-button')) { this.handleButtonClick(e.target); } }); } // 直接イベント設定(非推奨) setupDirectEvents() { // 後から追加された要素には適用されない const buttons = this.container.querySelectorAll('.dynamic-button'); buttons.forEach(button => { button.addEventListener('click', this.handleButtonClick); }); } addNewContent() { const newContent = document.createElement('div'); newContent.innerHTML = ` <button class="dynamic-button">新しいボタン</button> `; this.container.appendChild(newContent); // 直接イベント設定の場合は、ここで再設定が必要 // this.setupDirectEvents(); // 非効率 } handleButtonClick(button) { console.log('ボタンがクリックされました:', button.textContent); }}
const handler = new DynamicContentHandler();
動的に追加される要素には、イベント委譲を使うのがベストプラクティスです。
まとめ
querySelectorとquerySelectorAllは、JavaScriptでのDOM操作を格段に簡単にしてくれる強力なメソッドです。
重要なポイントをおさらい
今回学習した内容を確認しましょう。
- querySelector: 最初の1つの要素を取得
- querySelectorAll: 該当するすべての要素をNodeListで取得
- CSSセレクタがそのまま使える: IDセレクタ(#)、クラスセレクタ(.)、属性セレクタなど
- 複合セレクタ: 複数の条件を組み合わせて詳細な指定が可能
- 疑似クラス:
:first-child
、:nth-child()
などで位置や状態を指定
従来のメソッドと比べて、より柔軟で直感的な要素選択ができます。
実践での活用方法
以下のような場面で特に威力を発揮します。
- フォームバリデーション: 必須項目の一括取得と処理
- 動的コンテンツ生成: データからHTMLを自動生成
- 検索・フィルタリング: 条件に合致する要素の表示切り替え
- イベント処理: 特定の条件を満たす要素へのイベント設定
これらの機能を組み合わせることで、よりインタラクティブなWebアプリケーションを作成できます。
学習を続けるために
querySelectorをマスターするために、以下のステップで練習してみませんか?
- 基本セレクタから始める: ID、クラス、タグセレクタの練習
- 実際にコードを書く: 簡単なDOM操作から始める
- 複合セレクタにチャレンジ: より複雑な条件での要素選択
- 実用的な機能を作る: フォームやリストの操作機能を実装
querySelector・querySelectorAllをマスターすれば、JavaScript でのDOM操作が格段に楽になります。
ぜひ今日から、この便利なメソッドを使って実際のプロジェクトに取り組んでみてください!