6日目のゴールと今日のテーマ
6日目のテーマは「“仕様のこだわり”を足しても、設計が崩れないカウンターを育てること」です。
ここまでで、複数カウンター・複数ボタン・イベント分離・0未満防止は一通りできました。
今日は、同じテーマ(複数ボタン / イベント分離 / 数値管理 / 0未満防止)を、もう一段深くします。
カウンターごとに「設定(上限・下限・名前)」を持たせる。
その設定を changeCounter に組み込んで、ロジックをさらに“賢く”する。
「押せないときはボタンも見た目でわかる」UI側の工夫も考える。
コードを長くするのではなく、“設計の密度”を少し上げていきます。
まずは5日目までを「設計図」として整理し直す
counters オブジェクトで「カウンター一覧」を管理していた
5日目の時点で、カウンターはこう管理していました。
const counters = {
a: {
count: 0,
el: document.getElementById("counter-a"),
},
b: {
count: 0,
el: document.getElementById("counter-b"),
},
};
JavaScriptここでのポイントは二つです。
ひとつひとつのカウンターが「オブジェクト」としてまとまっている。
ID(”a”, “b”)から対応するカウンターを取り出せる。
この形があるからこそ、data-counter-id と組み合わせて、
「どのボタンがどのカウンターを動かすか」をシンプルに書けていました。
ボタンは「何をするか」を data-属性で持っていた
増減ボタンは、こういうイメージでした。
<button data-counter-id="a" data-delta="1">+1</button>
<button data-counter-id="a" 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>
JavaScript側では、
どのカウンターか → data-counter-id
いくつ変えるか → data-delta
を読み取って、共通ロジック changeCounter(counter, delta) に渡していました。
ここまで来ると、「イベント分離」が
どのボタンから来ても、
やること自体は共通処理に寄せる。
という形で、かなりきれいにできていました。
6日目のテーマ:カウンターごとに「ルール」を持たせる
例:Aは0〜10、Bは0〜100にしたい
ここで、ちょっとした仕様の違いを入れてみます。
カウンターAは、0〜10 の範囲にしたい。
カウンターBは、0〜100 の範囲にしたい。
つまり、「0未満防止」に加えて「上限防止」も入れたい。
しかも、その上限がカウンターごとに違う。
これを、どう設計に落とすかを考えます。
counters に「設定」を足す
さっきの counters を少しだけ拡張します。
const counters = {
a: {
count: 0,
min: 0,
max: 10,
el: document.getElementById("counter-a"),
},
b: {
count: 0,
min: 0,
max: 100,
el: document.getElementById("counter-b"),
},
};
JavaScriptこれで、
counter.min → このカウンターの最小値
counter.max → このカウンターの最大値
という情報を持てるようになりました。
ここでの大事なポイントは、
「ルール(制約)も状態と同じく、カウンターごとに持たせる」
という発想です。
ルールをグローバル変数にバラバラ書くのではなく、
「そのカウンターの特性」としてまとめるイメージです。
changeCounter を「min / max 対応版」に育てる
ロジックを1か所に集めたまま、機能を増やす
今までの changeCounter はこうでした。
function changeCounter(counter, delta) {
const next = counter.count + delta;
if (next < 0) {
return;
}
counter.count = next;
renderCounter(counter);
}
JavaScriptこれを、「min / max を見る」形に変えます。
function changeCounter(counter, delta) {
const next = counter.count + delta;
if (next < counter.min) {
return;
}
if (next > counter.max) {
return;
}
counter.count = next;
renderCounter(counter);
}
JavaScriptこれで、
A:min = 0, max = 10
B:min = 0, max = 100
という設定がそのまま効くようになります。
ここでの深掘りポイントは、
0未満防止を「next < 0」ではなく「next < counter.min」に変えたこと。
上限防止を「next > counter.max」として追加したこと。
つまり、「制約条件」を数字に固定せず、
カウンターオブジェクトに持たせた設定を参照する形にした、ということです。
UI側のこだわり:押せないときはボタンも変える
条件ロジックだけでは「ユーザーには見えない」
いまの時点で、ロジックとしてはこうなっています。
next が min 未満なら何もしない。
next が max 超えでも何もしない。
動作としては正しいですが、ユーザー目線だと
「押しても何も起きない」
という状態になります。
「なぜ何も起こらないのか」が画面から分からないのは、少し不親切です。
そこで、「押しても意味がないときはボタンを押せない見た目にする」という工夫を入れてみます。
ボタンの enabled / disabled を更新する関数を作る
カウンターの表示と同じように、「ボタンの状態も render に含める」という設計にします。
例えば、あるカウンター a に対応するボタンたちをこう取っておきます。
const deltaButtons = document.querySelectorAll("button[data-delta]");
const resetButtons = document.querySelectorAll("button[data-reset]");
JavaScriptその上で、「特定のカウンターに紐づくボタン」を探して状態を変える関数を作ります。
function updateButtons(counterId) {
const counter = counters[counterId];
if (!counter) return;
deltaButtons.forEach((btn) => {
if (btn.dataset.counterId !== counterId) {
return;
}
const delta = Number(btn.dataset.delta);
const next = counter.count + delta;
if (next < counter.min || next > counter.max) {
btn.disabled = true;
} else {
btn.disabled = false;
}
});
resetButtons.forEach((btn) => {
if (btn.dataset.counterId !== counterId) {
return;
}
// リセットボタンを無効にするかどうかは好み。
// 例えば、count がすでに min なら無効にするなどもできる。
if (counter.count === counter.min) {
btn.disabled = true;
} else {
btn.disabled = false;
}
});
}
JavaScriptここでの重要ポイントは、
「このカウンターのボタンだけを見る」ために counterId を使ってフィルタしていること。
「もし押したらどうなるか(next)」を計算して、その結果が min / max の範囲外なら disabled にしていること。
つまり、changeCounter と同じルールを UI にも反映している状態です。
renderCounter から updateButtons を呼び出す
renderCounter を、表示だけでなくボタン状態の更新も担うようにします。
function renderCounter(counterId) {
const counter = counters[counterId];
if (!counter) return;
counter.el.textContent = counter.count;
updateButtons(counterId);
}
JavaScriptchangeCounter も、counter ではなく id から呼び出す形に少しだけ変えると、
設計が揃ってきます。
function changeCounter(counterId, delta) {
const counter = counters[counterId];
if (!counter) return;
const next = counter.count + delta;
if (next < counter.min || next > counter.max) {
return;
}
counter.count = next;
renderCounter(counterId);
}
JavaScriptここでの深掘りポイントは、
ロジック(changeCounter)と UI(renderCounter, updateButtons)が、「同じルール」を共有していること。
「押せるかどうか」の視点でも、「実際に反映するかどうか」の視点でも、min / max を見ていること。
という二重チェックがきれいに設計されている点です。
イベント側はますますシンプルになる
クリック時は「id と delta を渡すだけ」
ボタンのクリック処理は、かなりシンプルになります。
deltaButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.counterId;
const delta = Number(btn.dataset.delta);
changeCounter(id, delta);
});
});
resetButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.counterId;
resetCounter(id);
});
});
JavaScriptresetCounter も id ベースにそろえます。
function resetCounter(counterId) {
const counter = counters[counterId];
if (!counter) return;
counter.count = counter.min;
renderCounter(counterId);
}
JavaScriptここでの設計ポイントは、
イベント側は「id と delta(またはリセット)」を伝えるだけ。
本当のルールや状態の変更は、すべて counters / changeCounter / resetCounter / renderCounter / updateButtons が担っている。
という役割分担です。
6日目のまとめと、7日目へのつなぎ
今日やったことを、言葉で整理します。
カウンターごとに min / max を持たせ、「0未満防止」を「min / max という設定」に一般化した。
changeCounter に min / max チェックを組み込み、ボタンの種類が増えても一貫したルールで動くようにした。
updateButtons で「押したらどうなるか」を計算し、押せないボタンは disabled にすることで、UI側にもルールを反映した。
renderCounter に「数値表示+ボタン状態更新」をまとめ、状態が変わるたびに画面全体が“正しい姿”になるようにした。
イベント側では「id と delta を渡すだけ」に徹し、ロジックを一切持たないスッキリしたコードにできた。
7日目は、この強化版カウンターアプリを「自分の頭の中の設計図」としてまとめる日になります。
なぜ counters という形にしたのか。
なぜ changeCounter / renderCounter / updateButtons に分けたのか。
なぜ data-counter-id / data-delta を使ったのか。
これらを“自分の言葉”で説明できるようになると、
次に作るどんな小さなアプリでも、この設計が土台として使えるようになります。
最後にひとつ、あなたに聞きたい。
今日の中で、「ここまでやるとちょっと気持ちいいな」と感じたのはどこでしたか?
min / max をカウンターごとに持たせた瞬間か、押せないボタンがグレーアウトしたときか。
その「気持ちいい」の感覚が、あなたと“設計のこだわり”をつなぐポイントです。
そこを大事にしたまま、7日目の仕上げに進んでいきましょう。


