▼最初に
JavaScriptで頻繁に使用するDOM操作。操作したい要素を取得して、その要素に対して作成、更新、削除といった操作ができる便利な機能ですよね。
皆さんは要素を取得する時にどのメソッドを使っていますか?私はgetElementById
をよく使っています。と言ってもこれしかほぼ使った事がないのが本音です。
今回は初めてgetElementsByClassName
を使ってDOM操作をする時に苦戦した話をアウトプットとして記事にしたいと思います。
▼苦戦した話
まずはgetElementsByClassName
の仕組みに苦戦した話をしたいと思います。
以下のコードを例に説明します。
このプログラムは、ボタン1、2,3が画面上に表示されており、
どのボタンを押しても”ボタンがクリックされました。”と表示されます。
<body>
<button class="btn">ボタン1</button>
<button class="btn">ボタン2</button>
<button class="btn">ボタン3</button>
</body>
// ボタン要素のclassを取得
const btn = document.getElementsByClassName("btn");
//ボタン要素に対してクリックイベント処理
btn.addEventListener('click',function(){
//pタグを生成
const p = document.createElement('p');
//pタグにテキストを追加
p.textContent = 'ボタンがクリックされました。';
//body内にpタグを配置
document.body.appendChild(p);
});
ところが実際動かしてみると....ボタンを押しても反応がない!!
class名で要素取得して、取得した要素にイベント処理してるから問題ないはずなんだけど...。
とりあえずコンソール見てみることに。
TypeError: btn.addEventListener is not a function
これだけだとよく分からないけど、イベント処理が成立していない事だけは何となく分かった。
デバッグして手前の処理(要素取得処理)を見てみる。
...と変数btn
の中身見てみたらこんなの出てきました。
HTMLCollection!?なんだこれ!?
とりあえずMDNさんに聞いてみる...
HTMLCollection インターフェイスは、(文書内の順序における)要素の汎用的な集合(arguments のような配列風のオブジェクト)を表し、リストから選択するためのメソッドとプロパティを提供します。
引用:MDN
ふむふむ、配列ね...。
変数btn
の中身は”HTMLCollection”という事が分かりました。
addEventListener
は一つの要素しか処理できない仕組みで、”HTMLCollection”には複数の要素が含まれていた為にイベント処理が実行されませんでした。
ではどうしたらいいのか。さらに調べるとQiitaに参考となる記事がありました。そちらを手掛かりに修正していきたいと思います。
※記事の最後にリンク貼っておきます。
▼修正
修正したコードは以下の通りとなります。
const btn = document.getElementsByClassName("btn");
+ const array = Array.prototype.slice.call(btn);
+ array.forEach(function (target) {
+ target.addEventListener('click', function () {
- btn.addEventListener('click',function(){
const p = document.createElement('p');
p.textContent = 'ボタンがクリックされました。';
document.body.appendChild(p);
});
});
それでは順番に解説していきます。
▼1.”HTMLCollection”を配列に変換
一つずつ処理を実行するための準備として、HTMLCollectionから配列に変換します。
※配列に変換する理由は後ほど説明します。
const array = Array.prototype.slice.call(btn);
・配列の一部を取り出して新しい配列にする
Array.prototype.slice()
・call()メソッドで変数btn
を呼び出す
call(btn)
▼2.繰り返し処理で全ての要素に対してイベント処理を行う
修正前のコードは、配列(複数要素)に対してイベント処理をしていました。addEventListenerは一つの要素しか処理できない為、繰り返し処理で全ての要素にイベント処理をする修正をしていきます。
ここで使うforEach()メソッドは、配列に処理はできますが、HTMLCollectionに処理はできません。その為の準備としてHTMLCollectionを配列に変換しました。※for文は使えます。
array.forEach(function (target) {
target.addEventListener('click', function () {
const p = document.createElement('p');
p.textContent = 'ボタンがクリックされました。';
document.body.appendChild(p);
});
});
・forEach文で配列array
の要素を引数target
に格納
array.forEach(function (target)
・引数target
に対してイベント処理
target.addEventListener
※forEach文の処理
1回目:インデックス番号[0]の要素にイベント処理
2回目:インデックス番号[1]の要素にイベント処理
3回目:インデックス番号[2]の要素にイベント処理
▼3.修正完了
修正完了しました。
全てのボタンに対してイベント処理が実行され、”ボタンがクリックされました。”の文字が表示されるようになりました。
▼まとめ
・getElementsByClassName
で取得できる要素は”HTMLCollection”と呼ばれる集合体。
・addEventListener
は一つの要素しか処理できない為、forEachメソッドやfor文を使ってすべての要素に対してイベント処理をする。
・”HTMLCollection”にforEachメソッドは使えない為、新しい配列にする必要がある。
私は普段からgetElementByIdで要素を取得していました。今回初めてgetElementsByClassNameを使いましたが、イベント処理が実行できなくてかなり焦りました。しかし、仕組みを理解できたことでもう苦戦することはないと思います。
本記事を通して"getElementsByClassName"の仕組みを理解いただけたら幸いです。
また、エラー対処の流れもお分かりになったのではないでしょうか。
エラーを修正するためにいきなりググっても遠回りになるケースもあります。原因を細分化して一つ一つ丁寧に調べることで原因が分かってきます。
今回は”HTMLCollection”についての解説は割愛したため、モヤモヤしている人もいると思います。こちらに関しては別の記事で解説したいと思いますので、興味のある方は読んでみてください。
▼参考記事
【JavaScript】getElementsByClassName() メソッドでハマった件
addEventListenerを使用する際に注意するべきこと