querySelectorAllで複数要素にaddEventListenerを付ける方法【forEach】

公開日
最終更新日
【Javascript】querySelectorAllで要素を取得し、addEventListenerでイベント処理をする

getElementByIdと同じ感覚で、querySelectorAllで複数の要素を取得してaddEventListenerを付けようとすると、なぜかイベントが動かない——JavaScriptを書き始めた頃によくつまずくポイントです。

結論から言うと、querySelectorAllが返すのは要素の集まり(NodeList)で、集まり自体にはaddEventListenerを付けられないのが原因です。解決策は、forEachでループし、1つずつの要素にイベントを付けることです。この記事では、単一要素の基本から、複数要素への正しい付け方までを解説します。

単一要素の場合(getElementById)

まずは基本から確認します。よく使うのは、getElementByIdで1つの要素を取得し、addEventListenerでイベントを付ける方法です。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
</head>

<body>

    <input type="button" id="button" value="ボタン">

</body>

</html>
// #buttonボタンをクリックしたときに、テキストを表示する
const button = document.getElementById("button");
button.addEventListener("click", function() {
    console.log("ボタンがクリックされました");
});

getElementByIdは1つの要素そのものを返すため、そこへ直接addEventListenerを付けられます。ここまでは問題なく動きます。

複数要素だとそのままでは動かない

ところが、同じ感覚でquerySelectorAllを使い、取得した複数要素にまとめてaddEventListenerを付けようとしても、うまくいきません。

理由は、querySelectorAllが返すのがNodeListという「要素の集まり」だからです。addEventListenerは個々の要素が持つメソッドであって、集まり全体に対して呼べるものではありません。そのため、NodeListに直接付けようとしても機能しないのです。では、複数の要素にイベントを付けるにはどうすればよいのでしょうか。

forEachでループして1つずつ付ける

答えは、forEachです。querySelectorAllで取得したNodeListをforEachでループし、その中で1つずつの要素にaddEventListenerを付けます。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
</head>

<body>

    <div class="tab">タブ</div>
    <div class="tab">タブ</div>
    <div class="tab">タブ</div>
    <div class="tab">タブ</div>
    <div class="tab">タブ</div>

</body>

</html>
// .tabをクリックしたときに、テキストを表示する
const tabs = document.querySelectorAll("div.tab");
tabs.forEach(function(tab) {
    tab.addEventListener("click", function() {
        console.log("タブがクリックされました");
    });
});

forEachのコールバックが受け取るtabが、1つずつの要素です。このtabに対してaddEventListenerを付けているので、5つの.tabすべてにクリックイベントが登録されます。これで、どのタブをクリックしても処理が実行されるようになります。

押さえておきたいポイント

NodeListは配列によく似ていますが、厳密には配列ではありません。ただしforEachは使えるので、ループ処理はそのまま書けます。もしmapfilterといった配列メソッドを使いたい場合は、Array.from()[...nodeList]でいったん配列に変換してから扱います。

また、querySelectorAllで要素が1つも見つからなかった場合でも、返るのは空のNodeListです。forEachは空なら単にループが回らないだけなので、この点ではエラーになりにくく安心です。タブやボタンなど「同じクラスの要素が複数ある」UIを扱うときの基本パターンとして、覚えておくと重宝します。

ちなみにjQueryを使う場合は、$('.tab').on('click', ...)と書くだけで、内部で該当するすべての要素にイベントが登録されます。素のJavaScriptでforEachを回すのは、これと同じことを手動で行っているイメージです。両者を対比して捉えておくと、jQueryと素のJavaScriptを行き来するときに混乱しません。

さらに、要素が非常に多い場合や、あとから動的に追加される要素にも対応したい場合は、親要素にイベントを1つだけ付けて子要素を判定する「イベント委譲(デリゲーション)」という手法もあります。まずはforEachを基本に押さえ、必要に応じて使い分けるとよいでしょう。

まとめ

  • getElementByIdは単一要素を返すので、直接addEventListenerを付けられる
  • querySelectorAllが返すNodeListは要素の集まりなので、直接は付けられない
  • forEachでループし、1つずつの要素にaddEventListenerを付ける
  • 配列メソッドが必要ならArray.from()で配列に変換する

「単一要素」と「複数要素」で書き方が変わる、という点さえ押さえれば迷いません。複数の要素に同じ処理を付けたいときは、querySelectorAllforEachをワンセットで思い出してください。

IPPEI FUKUTA(株式会社バッズブルー 代表)

IPPEI FUKUTA(株式会社バッズブルー 代表)

WordPress専門のWeb制作者として15年・150サイト以上を担当。現場でつまずいた問題と解決策を、同じ制作者・フリーランスの方に向けて発信しています。無料のWeb便利ツール集「バドツール」を開発・運営中。