JavaScriptを学習中です。課題で「+/−ボタンで数字が増減するカウンター」を作ったのですが、DOM操作でつまずいた点が3つあったので整理しておきます。同じところで止まっている人の参考になればうれしいです。
作ったのは+・−・Resetの3ボタンがあるだけの画面です。
<div class="counter">
<div class="counter-number" id="js-counter">0</div>
<button type="button" class="button js-button">-</button>
<button type="button" class="button js-button">+</button>
<button type="button" class="button" id="js-reset-button">Reset</button>
</div>
デモ: https://step6javascripthomework.netlify.app/
つまずき①:getElementById と getElementsByClassName は「返ってくるもの」が違う
数字の表示は id、+/−ボタンは class で取りました。この2つ、返ってくるものが違います。
// id → 要素が「1個」返る
const counter = document.getElementById("js-counter");
counter.textContent = 5; // そのまま操作できる
// class → 「複数」まとめて返る(HTMLコレクション)
const buttons = document.getElementsByClassName("js-button");
buttons.addEventListener("click", clickHandler); // ❌ エラー。buttonsは1個じゃない
getElementsByClassName が返すのは要素そのものではなく、要素の**集まり(HTMLコレクション)**です。1個ではないので、そのままでは addEventListener できません。for で回して1個ずつ登録します。
const buttons = document.getElementsByClassName("js-button");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", clickHandler);
}
ここが一番ハマりました。「idもclassも同じ感覚で使えるだろう」と思っていたら、classの方はループが要る、という違いに気づくまで時間がかかりました。
つまずき②:textContent は「文字列」なので、そのまま +1 すると "01" になる
表示中の数字は counter.textContent で取れますが、これは文字列です。数値のつもりで + 1 すると、足し算ではなく文字の連結になります。
// textContent は "0"(文字列)
console.log(counter.textContent + 1); // "01" ← 足し算じゃない!
数として計算したいので、parseInt()(または Number())で数値に直してから足します。
let current = parseInt(counter.textContent, 10); // "0" → 0(数値)
counter.textContent = current + 1; // 0 + 1 = 1
「なぜか 0 の次が 01 になる」で数分固まりました。textContent は見た目が数字でも中身は文字列、というのが盲点でした。
つまずき③:+と−で同じ関数を使い回す(e.currentTarget で判定)
+と−は「+1」か「-1」かの違いだけなので、関数は1つにして、どっちのボタンが押されたかで分岐しました。押された要素はイベントの e.currentTarget で取れます。
const clickHandler = (e) => {
const button = e.currentTarget; // 押されたボタン
let current = parseInt(counter.textContent, 10);
if (button.textContent === "+") {
counter.textContent = current + 1;
} else {
counter.textContent = current - 1;
}
};
e.currentTarget は「イベントを仕掛けた要素」そのもの。ボタンの表示("+" / "-")を見て処理を分けています。
完成したカウンター
整理すると、カウンター本体はこうなりました。
(() => {
const counter = document.getElementById("js-counter");
const clickHandler = (e) => {
const button = e.currentTarget;
let current = parseInt(counter.textContent, 10);
if (button.textContent === "+") {
counter.textContent = current + 1;
} else {
counter.textContent = current - 1;
}
};
const buttons = document.getElementsByClassName("js-button");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", clickHandler);
}
})();
Resetボタンは別ファイルにして、押したら 0 に戻すだけにしました。
document.getElementById("js-reset-button").addEventListener("click", () => {
document.getElementById("js-counter").textContent = 0;
});
まとめ
カウンターを作ってつまずいた3つ:
-
getElementByIdは要素1個、getElementsByClassNameは複数(HTMLコレクション)。後者はaddEventListenerを直接つけられず、ループで1個ずつ登録する -
textContentは文字列。計算する前にparseInt/Numberで数値に直す(じゃないと"01") - 同じ処理を複数ボタンで使い回すときは、
e.currentTargetで「どれが押されたか」を判定する
まだ学習中の身なので、誤りや、もっと良い書き方があればコメントで教えていただけると嬉しいです。最後まで読んでいただきありがとうございました。