本記事では、addEventListenerが効かない事例とその解決策について解説します。
特に「addEventListenerでは、getElementsByClassNameが使えない」点に注意が必要です。
「税込金額計算ツール」を例に、エラーと解決策を見ていきましょう。
addEventListener メソッドを持っていないと addEventListener が効かなくなる
どのようなときにaddEventListenerでgetElementsByClassNameが使えなくなるのか、「税込金額計算ツール」の実装過程から見ていきます。
※OS環境はMacOS、エディタはVisual Studio Codeを使用
- HTML・CSSで、入力ボックス・計算結果の外観を作成します。
今回は、”item_price”クラス内に金額を入力し、”total_price”クラスで計算値を出力します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>税込価格を表示する</title> <style> input{ width: 200px; font-size: 16px; padding: 8px; margin-bottom: 8px; } </style> </head> <body> <input type="number" class="item_price" placeholder="金額を入力してください"> <div class="total_price"></div> </body> </html>
- 税込金額を自動計算・表示させるために、以下のJavaScriptを入力します。
<script> // "item_price" クラスの要素(入力した値)を、変数:inputPrice と定義// const inputPrice = document.getElementsByClassName("item_price"); // "total_price" クラスの要素(税込で表示する値)を、変数:totalPrice と定義// const totalPrice = document.getElementsByClassName("total_price"); // "item_price" クラスに値が入力されたときの処理を、addEventListenerで指定// inputPrice.addEventListener('input', function() { //入力された値が空白の場合は、空白で返す(NaNと表示されるのを防止)// if (inputPrice.value === '') { totalPriceDisplay.textContent = ''; return; } //税率を設定する(今回は10%)// const taxRate = 0.1; //変数:totalPriceに、「税込価格:”入力値の税込金額”円」(小数点第2位まで)を代入する=>"total_price"クラスに税込金額を表示// totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); </script>
- ボックスに数値を入力しても、なぜか税込金額は表示されません。
-
検証画面でエラー内容を確認したところ、Uncaught TypeError: inputPrice.addEventListener is not a functionが表示されています。
「inputPrice は関数ではない」=「addEventListener メソッドを持っていない」ことから、メソッドを呼び出すことができないようです。
エラーが発生した理由:getElementsByClassName で取得した要素は、addEventListener が使えない
document.getElementsByClassName() で取得した inputPrice の値は、関数ではなくHTMLコレクションです。
HTMLコレクションは「HTMLの要素の集合を表す複数オブジェクト」であることから、addEventListener メソッドが存在しません。
一方で、addEventListener は単一の要素に対してイベントリスナーを追加します。
そのため、getElemntsByClassName で取得した要素に対して addEventListener を直接適用できないのです。
コラム:HTMLコレクションの使用例:男女数の自動カウントツール
HTMLコレクションについて、男女数の自動カウントツールを例に解説します。
- 以下の通り、HTM・CSSコードを入力します。 liタグより、男性にはmanクラスを、女性にはwomenクラスをそれぞれ設定しています。
<ul> <!-- 男性にはmanクラスを、女性にはwomenクラスをそれぞれ設定 --> <li class="man">山田太郎</li> <li class="woman">田中花子</li> <li class="man">鈴木次郎</li> <li class="woman">佐藤優子</li> <li class="woman">渡辺由美</li> </ul> <div id="result"></div>
- 以下の通り、JavaScriptを実装します。
- manクラスに一致するすべての要素を、変数:menと定義
- womanクラスに一致するすべての要素を、変数:womenと定義
- “result”IDのHTML要素に、カウントした数を表示する
<script> //manクラスの要素を取得し、変数:menと定義 const men = document.getElementsByClassName('man'); //womanクラスを持つ要素を取得し、変数:womenと定義 const women = document.getElementsByClassName('woman'); //id="result"のHTML内に、「男性は "menの要素数" 人、女性は "womenの要素数" 人です。」を代入// document.getElementById('result').innerHTML = `男性は ${(men.length)} 人、女性は ${(women.length)} 人です。`; </script>
-
すると、名簿の一覧に対する男女の人数を表示できます。
HTMLコレクションは「HTMLの要素の集合を表す複数オブジェクト」なので、getElementsByClassName()を用いることで、指定されたクラス名に一致するすべての要素を取得できるのです。
解決策1:document.getElementsByClassName() の代わりにquerySelector を使用する
以下の通り、item_price クラスと total_price クラスの値を取得する際に、querySelector を使用すると、正しく表示されます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>税込価格を表示</title> <style> input { width: 200px; font-size: 16px; padding: 8px; margin-bottom: 8px; } </style> </head> <body> <input type="number" class="item_price" placeholder="金額を入力してください"> <div class="total_price"></div> <script> const inputPrice = document.querySelector(".item_price"); const totalPrice = document.querySelector(".total_price"); inputPrice.addEventListener('input', function () { if (inputPrice.value === '') { totalPrice.textContent = ''; return; } const taxRate = 0.1; totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); </script> </body> </html>
querySelector() とは、指定されたCSSセレクタに一致する最初のHTML要素を取得するメソッドです。
単一の要素が取得されることから、直接 addEventListener を適用できます。
解決策2:クラスの代わりにIDを取得させる
以下の通り、item_price と total_price をクラスではなく id に設定し、getElementById() で id の要素を取得すると、正しく表示されます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>税込価格を表示</title> <style> input { width: 200px; font-size: 16px; padding: 8px; margin-bottom: 8px; } </style> </head> <body> <input type="number" id="item_price" placeholder="金額を入力してください"> <div id="total_price"></div> <script> const inputPrice = document.getElementById("item_price"); const totalPrice = document.getElementById("total_price"); inputPrice.addEventListener('input', function () { if (inputPrice.value === '') { totalPrice.textContent = ''; return; } const taxRate = 0.1; totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); </script> </body> </html>
HTML文書内で要素に付けられるIDは単一であり、重複してはいけません。
もし同じIDが複数の要素で使われている場合、メソッドは最初に見つかった要素を返します。
このように単一の要素が取得されることから、直接 addEventListener を適用できるのです。
addEventListener 使用時に注意したい他のエラー
addEventListener を使用した際に発生するエラーは、他にもあります。
先ほどと同じ「税込金額計算ツール」を例にして、見ていきましょう。
複数の箇所に同じクラスを設定した場合、2つ目以降のクラスでaddEventListenerが動作しない
以下の通り、HTMLで価格を入力する欄を追加したとき、下段のボックスに数値を入力しても税込金額が表示されません。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>税込価格を表示</title> <style> input { width: 200px; font-size: 16px; padding: 8px; margin-bottom: 8px; } </style> </head> <body> <div class="price"> <p>価格1</p> <input type="number" class="item_price" placeholder="金額を入力してください"> <div class="total_price"></div> </div> <div class="price"> <p>価格2</p> <input type="number" class="item_price" placeholder="金額を入力してください"> <div class="total_price"></div> </div> <script> const inputPrice = document.querySelector(".item_price"); const totalPrice = document.querySelector(".total_price"); inputPrice.addEventListener('input', function () { if (inputPrice.value === '') { totalPrice.textContent = ''; return; } const taxRate = 0.1; totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); </script> </body> </html>
querySelector() は「最初のHTML要素を取得するメソッド」です。そのためこのエラーは、下段に設定された “item_price” クラスと “total_price” クラスでは無効なのが原因です。
そこで、以下の通りJavaScriptを入力します。
<script> const prices = document.querySelectorAll(".price"); prices.forEach(price => { const inputPrice = price.querySelector(".item_price"); const totalPrice = price.querySelector(".total_price"); inputPrice.addEventListener('input', function () { if (inputPrice.value === '') { totalPrice.textContent = ''; return; } const taxRate = 0.1; totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); }); </script>
まずは、querySelectorAll メソッドで「指定されたCSSセレクターに一致するすべての要素を取得」します。
次に、pricesクラスに含まれる各要素に対して、以下のループ処理を設定します。
- priceクラス内の”item_price”の要素を取得して、”inputPrice”と定義する
- priceクラス内の”total_price”の要素を取得して、”totalPrice”と定義する
- “item_price” クラスに値が入力されたときの処理を、addEventListenerで指定
これでエラーが解消され、価格1と価格2の両方に対して正しい税込価格が表示できるようになります。
イベントリスナーが重複した場合、後から書いた処理に上書きされる
以下の通り、ダブルクリックした際に入力金額が消去される動作を2つ追加します。
- 入力した価格と税込価格の欄を消去する
- 入力した価格を消去したあとに、税込価格の欄に「金額がクリアされました!」と表示する
<script> const prices = document.querySelectorAll(".price"); prices.forEach(price => { const inputPrice = price.querySelector(".item_price"); const totalPrice = price.querySelector(".total_price"); inputPrice.addEventListener('input', function () { if (inputPrice.value === '') { totalPrice.textContent = ''; return; } const taxRate = 0.1; totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); //金額が消去された後は、何も表示されない inputPrice.addEventListener('dblclick', function () { inputPrice.value = ''; totalPrice.textContent = ''; }); //金額が消去された後に、「金額がクリアされました!」と表示 inputPrice.addEventListener('dblclick', function () { inputPrice.value = ''; totalPrice.textContent = '金額がクリアされました!'; }); }); </script>
すると、①の設定が②の設定に上書きされてしまいます。
addEventListener は、イベントリスナーが追加された順番に実行されます。
そのため、先に追加されたリスナーが実行された後に、次に追加されたリスナーが実行されてしまい、その結果上書きされてしまうのです。
これを解決するには、コードを理解しやすくするために、1つのイベントリスナー内で必要な処理をまとめて実行するようにしましょう。
<script> const prices = document.querySelectorAll(".price"); prices.forEach(price => { const inputPrice = price.querySelector(".item_price"); const totalPrice = price.querySelector(".total_price"); inputPrice.addEventListener('input', function () { if (inputPrice.value === '') { totalPrice.textContent = ''; return; } const taxRate = 0.1; totalPrice.textContent = `税込価格: ${(inputPrice.value * (1 + taxRate)).toFixed(2)} 円`; }); inputPrice.addEventListener('dblclick', function () { inputPrice.value = ''; totalPrice.textContent = ''; }); }); </script>
まとめ
getElementsByClassName で取得した要素は、HTMLコレクションになるため、addEventListener を呼び出せません。そのため、以下のメソッドでエラーを解消させましょう。
- querySelector() を使用する
- クラスではなくidに設定し、getElementById() を使用する
他にも「複数の箇所に同じクラスを設定する場合は、querySelectorAll メソッドを用いてforeach文でループさせる」「イベントリスナーを重複させない」ことも覚えておくと良いです。
addEventListener への理解を深めて、実装バリエーションを増やしましょう。
関連記事
テキストボックスの値を別のテキストボックスへ反映させる方法
2024.1.10
2024.5.10
最新記事
-
【初めてでも安心】プロジェクト計画の基本と成功へのステップガイド
2024.9.5
-
【初心者必見】システム設計の基本から学ぶ!成功するプロジェクトの秘訣
2024.9.5
-
【Googleフォームをカスタマイズ】完了画面(サンクスページ)の編集方法
2024.6.26
-
addEventListenerを使用する時の注意点とエラーの解決方法
2024.5.9