フリースローの練習は、本数を重ねるほど記録が大事になります。何本打って何本決めたか、成功率がどれくらいかをすぐ見られるだけで、練習の振り返りがしやすくなります。
この記事では、成功・失敗ボタンで履歴を追加し、成功率を自動計算するフリースロー記録表を作ります。シュート練習全体の成功率を見たい場合は、シュート成功率ツールも使えます。
作るもの
- 成功ボタンと失敗ボタンで1本ずつ記録する
- 本数、成功数、成功率を自動で表示する
- 履歴を新しい順に一覧表示する
- localStorageでブラウザに記録を保存する
- リセットボタンで記録を消せる
HTML
成功率だけでなく、履歴を表示するol要素も用意します。記録が増えるほど成果が見えるので、練習ツールとして少し楽しくなります。
<main class="free-throw-tracker">
<img
class="nines-logo"
src="https://kjnine.com/wp-content/themes/kjnines_theme/images/svg2/nines_logo.svg"
alt="NINES"
>
<p class="eyebrow">FREE THROW LOG</p>
<h1>フリースロー記録表</h1>
<div class="actions">
<button data-shot="make">成功</button>
<button data-shot="miss">失敗</button>
<button id="reset">リセット</button>
</div>
<dl class="stats">
<div>
<dt>本数</dt>
<dd id="attempts">0</dd>
</div>
<div>
<dt>成功</dt>
<dd id="makes">0</dd>
</div>
<div>
<dt>成功率</dt>
<dd id="rate">0%</dd>
</div>
</dl>
<ol id="history" class="history"></ol>
</main>CSS
記録表はスマホで使うことが多いので、ボタンを押しやすく、数字を大きく表示します。成功と失敗は色で分けると、履歴が読みやすくなります。
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
background: #fff;
font-family: system-ui, sans-serif;
color: #111;
}
.free-throw-tracker {
position: relative;
overflow: hidden;
width: min(92vw, 620px);
padding: 28px;
border: 3px solid #111;
border-radius: 22px;
background: #fff;
}
.nines-logo {
position: absolute;
top: 18px;
right: 18px;
width: 92px;
max-width: 28%;
opacity: .08;
pointer-events: none;
}
.eyebrow {
margin: 0 0 8px;
color: #ff2f63;
font-weight: 900;
}
h1 {
margin: 0 0 22px;
font-size: clamp(30px, 6vw, 54px);
line-height: 1.05;
}
.actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 22px;
}
button {
padding: 12px 18px;
border: 2px solid #111;
border-radius: 999px;
background: #111;
color: #fff;
font: inherit;
font-weight: 800;
cursor: pointer;
}
button[data-shot="miss"] {
background: #fff;
color: #111;
}
#reset {
border-color: #ff2f63;
background: #ff2f63;
}
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin: 0 0 22px;
}
.stats div {
padding: 16px;
border: 2px solid #111;
background: #ffd93d;
}
dt {
font-size: 13px;
font-weight: 800;
}
dd {
margin: 0;
font-size: 34px;
font-weight: 900;
}
.history {
display: grid;
gap: 8px;
max-height: 260px;
overflow: auto;
margin: 0;
padding-left: 24px;
}
.history li {
padding: 10px 12px;
border: 1px solid #ddd;
}
.history .make {
border-left: 8px solid #12c2a9;
}
.history .miss {
border-left: 8px solid #ff2f63;
}
@media (max-width: 560px) {
.stats {
grid-template-columns: 1fr;
}
}JavaScript
配列に成功・失敗の履歴を保存し、その配列から本数、成功数、成功率を毎回計算します。localStorageを使うので、ページを閉じても同じブラウザなら記録が残ります。
const STORAGE_KEY = "free-throw-history";
const buttons = document.querySelectorAll("[data-shot]");
const resetButton = document.querySelector("#reset");
const attemptsEl = document.querySelector("#attempts");
const makesEl = document.querySelector("#makes");
const rateEl = document.querySelector("#rate");
const historyEl = document.querySelector("#history");
let shots = JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
function saveShots() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(shots));
}
function render() {
const attempts = shots.length;
const makes = shots.filter((shot) => shot === "make").length;
const rate = attempts === 0 ? 0 : (makes / attempts) * 100;
attemptsEl.textContent = attempts;
makesEl.textContent = makes;
rateEl.textContent = `${rate.toFixed(1)}%`;
historyEl.innerHTML = "";
shots.slice().reverse().forEach((shot, index) => {
const item = document.createElement("li");
item.className = shot;
item.textContent = `${attempts - index}本目: ${shot === "make" ? "成功" : "失敗"}`;
historyEl.append(item);
});
}
buttons.forEach((button) => {
button.addEventListener("click", () => {
shots.push(button.dataset.shot);
saveShots();
render();
});
});
resetButton.addEventListener("click", () => {
shots = [];
saveShots();
render();
});
render();CodePen埋め込み用エリア
CodePenでは、HTML、CSS、JSの各パネルに分けて貼り付けます。localStorageもCodePen上で動くので、ブラウザをリロードして保存されるか確認できます。
よくある失敗
- 履歴をHTMLだけで増やそうとして、集計用のデータが残らない
- 成功率を表示するたびに古い履歴を消し忘れる
- localStorageから取り出した値をJSON.parseし忘れる
- リセット時に画面だけ消して、保存データを消し忘れる
応用
- 10本ごとにセットを分けて記録する
- 日付ごとの成功率を保存する
- 成功率が目標を超えたらメッセージを出す
- CSVとして書き出せるようにする
関連リンク
まとめ
フリースロー記録表は、配列、DOM更新、localStorageをまとめて練習できる実用的な題材です。成功・失敗を押すだけの小さなツールでも、日々の練習に合わせて育てれば、かなり使いやすい記録アプリになります。
