5日目のゴールと今日のテーマ
5日目のテーマは「“強化版カウンター”として、小さなこだわり機能を入れながら設計を崩さずに育てること」です。
ここまでで、単一カウンター → 複数カウンター、イベント分離、0未満防止まできました。
今日はここに少しだけ“欲”を足します。
複数の増減ボタン(+1 / −1 に加えて、+5 / −5 など)
0未満防止をロジックとしてきれいに維持する
イベント分離を「ボタン種類」と「カウンター種類」の両方で意識する
ゴールは、
ボタンが増えてもコードがグチャグチャにならない。
「どのカウンターに」「いくつ足すか・引くか」がスッキリ書ける。
0未満防止を壊さずに機能を増やせる。
この3つを満たした状態のカウンターアプリです。
まずは昨日の「複数カウンター版」を思い出す
1カウンターをオブジェクトで表現していた
4日目では、カウンターをこう扱っていました。
const counterA = {
count: 0,
el: document.getElementById("counter-a"),
};
const counterB = {
count: 0,
el: document.getElementById("counter-b"),
};
JavaScriptそして、「1個のカウンターに対する処理」は関数にまとめていました。
function renderCounter(counter) {
counter.el.textContent = counter.count;
}
function changeCounter(counter, delta) {
const next = counter.count + delta;
if (next < 0) {
return;
}
counter.count = next;
renderCounter(counter);
}
function resetCounter(counter) {
counter.count = 0;
renderCounter(counter);
}
JavaScriptここでの大事なところは、「どのカウンターか」を引数で渡していることです。
これによって、A にも B にも同じロジックを使い回せていました。
5日目の一歩目:増減ボタンを増やしても壊れない形
HTMLを「ボタンの役割」が分かるようにしておく
今日は、各カウンターにこんなボタンを用意したとイメージします。
+1
−1
+5
−5
リセット
HTMLは、data-属性を使うと整理しやすいです。
<div class="counter" data-counter-id="a">
<div id="counter-a" class="counter-value">0</div>
<button data-counter-id="a" data-delta="1">+1</button>
<button data-counter-id="a" data-delta="-1">−1</button>
<button data-counter-id="a" data-delta="5">+5</button>
<button data-counter-id="a" data-delta="-5">−5</button>
<button data-counter-id="a" data-reset="true">リセット</button>
</div>
<div class="counter" data-counter-id="b">
<div id="counter-b" class="counter-value">0</div>
<button data-counter-id="b" data-delta="1">+1</button>
<button data-counter-id="b" data-delta="-1">−1</button>
<button data-counter-id="b" data-delta="5">+5</button>
<button data-counter-id="b" data-delta="-5">−5</button>
<button data-counter-id="b" data-reset="true">リセット</button>
</div>
ここでの重要ポイントは二つです。
data-counter-id="a" のように、「どのカウンター用のボタンか」が分かる。data-delta="1" や data-reset="true" で、「何をするボタンか」が分かる。
JavaScript側は、この「ヒント」を読めばよくなります。
カウンターを「IDから取り出せるように」整理する
counterA / counterB をまとめて管理する
4日目では counterA, counterB を別々に持っていましたが、
今日は「IDで引けるように」まとめます。
const counters = {
a: {
count: 0,
el: document.getElementById("counter-a"),
},
b: {
count: 0,
el: document.getElementById("counter-b"),
},
};
JavaScriptこうしておくと、
const counter = counters["a"]; // counterA
const counterBRef = counters["b"]; // counterB
JavaScriptのように、「文字列のIDから対応するカウンター」を取れます。
ここでの深掘りポイントは、「カウンターが増えても設計が崩れない」ということです。
c, d, e… と増やしたいときにも、counters に追加するだけで済みます。
共通ロジックはそのままにしておく
changeCounter / resetCounter は昨日のままでOK
昨日作った共通ロジックは、そのまま使えます。
function renderCounter(counter) {
counter.el.textContent = counter.count;
}
function changeCounter(counter, delta) {
const next = counter.count + delta;
if (next < 0) {
return;
}
counter.count = next;
renderCounter(counter);
}
function resetCounter(counter) {
counter.count = 0;
renderCounter(counter);
}
JavaScriptここで大事なのは、
「ボタンが増えても、このロジック部分は一切触らない」
ということです。
仕様(0未満防止)がある程度固まったら、ロジックは守る。
操作の種類やボタンの数が増えても、
ロジックを分解して増やさない。
これが、バグを増やさないコツです。
ボタンイベントを「一括で」登録する
deltaボタン(+1 / −1 / +5 / −5)の登録
前準備として、「delta を持っているボタン」を全部取ります。
const deltaButtons = document.querySelectorAll("button[data-delta]");
JavaScriptそして、forEach でまとめてイベントを付けます。
deltaButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.counterId; // "a" や "b"
const delta = Number(btn.dataset.delta); // 1, -1, 5, -5 など
const counter = counters[id];
if (!counter) {
return;
}
changeCounter(counter, delta);
});
});
JavaScriptここでの重要ポイントは3つです。
「どのカウンターか」は data-counter-id で判断。
「いくつ変えるか」は data-delta で判断。
ロジックは changeCounter に完全に任せる。
これで、+1 / −1 / +5 / −5 のボタンが何個あっても、
この1か所のロジックだけで対応できます。
リセットボタンも一括で扱う
同じ発想で、リセットボタンもまとめます。
const resetButtons = document.querySelectorAll("button[data-reset]");
resetButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.counterId;
const counter = counters[id];
if (!counter) {
return;
}
resetCounter(counter);
});
});
JavaScriptここでも、
どのカウンターか → data-counter-id
何をするか → data-reset(存在している=リセット)
という情報を読んで動いています。
「0未満防止」がちゃんと全ボタンに効いているか確認する
+5 / −5 でも 0未満防止が機能する理由
changeCounter を思い出します。
function changeCounter(counter, delta) {
const next = counter.count + delta;
if (next < 0) {
return;
}
counter.count = next;
renderCounter(counter);
}
JavaScriptdelta は、+1 でも +5 でも −1 でも −5 でも構いません。
重要なのは、「次の値 next が 0 未満かどうか」だけを見ていることです。
例えば、今の値が 3 のとき。
−1 ボタン → delta = −1 → next = 2 → 更新OK。
−5 ボタン → delta = −5 → next = −2 → 0未満なので return して何もしない。
つまり、「どのボタン経由でも、最終判断は changeCounter がしている」ということです。
これが「0未満防止」を1か所で守る、という設計の強さです。
5日目までの全体像を一枚にしてみる
コードのイメージをまとめて見る
ここまで学んだことを、簡略化した全体像で整理します。
const counters = {
a: {
count: 0,
el: document.getElementById("counter-a"),
},
b: {
count: 0,
el: document.getElementById("counter-b"),
},
};
function renderCounter(counter) {
counter.el.textContent = counter.count;
}
function changeCounter(counter, delta) {
const next = counter.count + delta;
if (next < 0) {
return;
}
counter.count = next;
renderCounter(counter);
}
function resetCounter(counter) {
counter.count = 0;
renderCounter(counter);
}
function setup() {
// 初期表示
renderCounter(counters.a);
renderCounter(counters.b);
// 増減ボタン
const deltaButtons = document.querySelectorAll("button[data-delta]");
deltaButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.counterId;
const delta = Number(btn.dataset.delta);
const counter = counters[id];
if (!counter) return;
changeCounter(counter, delta);
});
});
// リセットボタン
const resetButtons = document.querySelectorAll("button[data-reset]");
resetButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.counterId;
const counter = counters[id];
if (!counter) return;
resetCounter(counter);
});
});
}
setup();
JavaScriptここまで来ると、もう立派な「強化版・複数カウンターアプリ」です。
5日目のまとめと、6日目へのつなぎ
今日やったことを言葉で整理します。
複数ボタン(+1 / −1 / +5 / −5)を、data-delta として一括で扱える形にした。
各ボタンが「どのカウンター」に対応するかを data-counter-id で表現し、イベントの中で判断した。
counters オブジェクトで、「ID→カウンター状態」のマッピングを作り、拡張しやすくした。
0未満防止のロジックを changeCounter に集約し、どのボタン経由でもルールが守られるようにした。
イベント分離を「ボタンごと」から一歩進めて、「ボタンの種類ごと(delta系 / reset系)」に整理した。
6日目以降は、ここから例えば、
カウンターごとに「最大値」「名前」「色」などのプロパティを足す。
カウンターをボタンで“動的に追加”する方向にチャレンジする。
このカウンターアプリを、別のアプリ(例えばポイント管理や在庫管理)の土台として使ってみる。
といった発展が考えられます。
最後にひとつだけ質問を投げます。
今日の中で、「あ、この設計ちょっと気持ちいい」と感じたのはどこでしたか?
data-counter-id / data-delta で一気にボタンを扱えたところか、
changeCounter が全ボタンを守ってくれている安心感か。
その「気持ちいい」と感じたところが、あなたの設計センスの芯です。
そこを意識して、6日目のステップに進んでいきましょう。


