5日目のゴールと全体像
サイコロアプリ 5日目のテーマは 「履歴を“並び替え・詳細表示・色分け・アニメーション付き削除”できるようにし、DOM操作の応用力を一段階引き上げること」 です。
ここまでで、みなさんは
- 2つのサイコロを振る
- 合計値を計算する
- 合計値に応じて背景色・メッセージを変える
- 履歴を保存・一覧表示・削除する
という「動くサイコロアプリ」を作ってきました。
5日目ではここに 並び替え・詳細表示・カード色分け・削除アニメーション を追加して、 より“使いやすく楽しい”アプリへ進化させます。
履歴機能を強化するための新しいUI
まずは、HTMLに新しいボタンや表示エリアを追加します。
<h3>履歴</h3>
<!-- 並び替えボタン -->
<button id="sortNewBtn">新しい順</button>
<button id="sortOldBtn">古い順</button>
<!-- 履歴一覧 -->
<div id="historyList"></div>
<!-- 全削除 -->
<button id="clearHistoryBtn">履歴をすべて削除</button>
<h3>詳細</h3>
<div id="detailBox"></div>
深掘りポイント
- 並び替えは「配列の順番を変える」ことで実現します。
- 詳細表示は「履歴1件のデータを専用エリアに描画する」だけ。
- DOM操作は「HTML側に置き場所を作る → JSで中身を入れる」が基本。
CSSでカードの見た目とアニメーションを整える
.history-card {
background: #fff;
padding: 12px;
margin: 10px 0;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.2s, opacity 0.4s;
}
.history-card:hover {
transform: scale(1.02);
}
.fade-out {
opacity: 0;
transform: translateY(-10px);
}
.detail-card {
background: #fefefe;
padding: 15px;
margin-top: 15px;
border: 1px solid #ddd;
border-radius: 8px;
}
深掘りポイント
.fade-outを追加することで削除時にふわっと消える演出ができる。.history-card:hoverでカードに軽い動きをつけると、UIが一気に洗練される。- CSSアニメーションは「クラスを付け替えるだけ」で発動する。
JavaScript:並び替え機能を追加する
並び替えボタンの要素を取得
const sortNewBtn = document.getElementById("sortNewBtn");
const sortOldBtn = document.getElementById("sortOldBtn");
JavaScript新しい順に並び替える
sortNewBtn.addEventListener("click", function() {
historyArray.sort((a, b) => b.time - a.time);
renderHistory(historyArray);
});
JavaScript古い順に並び替える
sortOldBtn.addEventListener("click", function() {
historyArray.sort((a, b) => a.time - b.time);
renderHistory(historyArray);
});
JavaScript深掘りポイント
sort()は「元の配列を並び替える」メソッド。(a, b) => b.time - a.timeは「比較関数」。- 並び替えたら必ず
renderHistory()を呼んで画面を更新する。
詳細表示機能を追加する
履歴カードをクリックしたときに、 その1件分の情報を「詳細エリア」に表示します。
詳細表示関数
function showDetail(item) {
const detailBox = document.getElementById("detailBox");
detailBox.innerHTML = `
<div class="detail-card">
<p>日時:${item.dateTime}</p>
<p>サイコロ1:${item.dice1}</p>
<p>サイコロ2:${item.dice2}</p>
<p>合計:${item.sum}</p>
<p>メッセージ:${item.message}</p>
</div>
`;
}
JavaScript深掘りポイント
- 詳細表示は「1件のオブジェクトをそのまま描画する」だけ。
- 関数に
itemを渡すことで、どの履歴でも同じ処理で表示できる。 - DOM操作は「専用エリアにHTMLを流し込む」だけで実現できる。
カード色分け(合計値によって背景色を変える)
合計値が高いほど明るい色にするなど、 視覚的にわかりやすくします。
function getCardColor(sum) {
if (sum <= 4) return "#f8d7da"; // 赤
if (sum <= 8) return "#cce5ff"; // 青
return "#fff8b3"; // 金色
}
JavaScript削除アニメーション付きの削除処理
function deleteHistory(index, cardElement) {
cardElement.classList.add("fade-out");
setTimeout(() => {
historyArray.splice(index, 1);
saveHistory(historyArray);
renderHistory(historyArray);
}, 400);
}
JavaScript深掘りポイント
.classList.add("fade-out")でCSSアニメーションを発動。setTimeout()でアニメーション後に削除処理を実行。- UIが滑らかになるとアプリの満足度が大きく上がる。
履歴描画関数を強化する(並び替え・詳細・色分け・削除対応)
function renderHistory(historyArray) {
const list = document.getElementById("historyList");
list.innerHTML = "";
historyArray.forEach((item, index) => {
const card = document.createElement("div");
card.className = "history-card";
card.style.backgroundColor = getCardColor(item.sum);
card.innerHTML = `
<p>日時:${item.dateTime}</p>
<p>サイコロ1:${item.dice1}</p>
<p>サイコロ2:${item.dice2}</p>
<p>合計:${item.sum}</p>
<p>メッセージ:${item.message}</p>
<button class="history-detail-btn">詳細</button>
<button class="history-delete-btn">削除</button>
`;
card.querySelector(".history-detail-btn").addEventListener("click", function() {
showDetail(item);
});
card.querySelector(".history-delete-btn").addEventListener("click", function() {
deleteHistory(index, card);
});
list.appendChild(card);
});
}
JavaScript5日目の完成コード(script.js 全体)
const rollBtn = document.getElementById("rollBtn");
const dice1 = document.getElementById("dice1");
const dice2 = document.getElementById("dice2");
const diceSum = document.getElementById("diceSum");
const diceMessage = document.getElementById("diceMessage");
const clearHistoryBtn = document.getElementById("clearHistoryBtn");
const sortNewBtn = document.getElementById("sortNewBtn");
const sortOldBtn = document.getElementById("sortOldBtn");
let historyArray = loadHistory();
function rollDice() {
return Math.floor(Math.random() * 6) + 1;
}
function updateBackground(sum) {
if (sum <= 4) document.body.style.backgroundColor = "#f8d7da";
else if (sum <= 8) document.body.style.backgroundColor = "#cce5ff";
else document.body.style.backgroundColor = "#fff8b3";
}
function getSumMessage(sum) {
if (sum <= 4) return "今日は慎重にいきましょう。";
if (sum <= 8) return "まずまずの運勢。落ち着いて進めればOK。";
return "最高の運勢!何をしても楽しめそうです!";
}
function getCardColor(sum) {
if (sum <= 4) return "#f8d7da";
if (sum <= 8) return "#cce5ff";
return "#fff8b3";
}
function saveHistory(arr) {
localStorage.setItem("diceHistory", JSON.stringify(arr));
}
function loadHistory() {
const data = localStorage.getItem("diceHistory");
return data ? JSON.parse(data) : [];
}
function showDetail(item) {
const detailBox = document.getElementById("detailBox");
detailBox.innerHTML = `
<div class="detail-card">
<p>日時:${item.dateTime}</p>
<p>サイコロ1:${item.dice1}</p>
<p>サイコロ2:${item.dice2}</p>
<p>合計:${item.sum}</p>
<p>メッセージ:${item.message}</p>
</div>
`;
}
function deleteHistory(index, cardElement) {
cardElement.classList.add("fade-out");
setTimeout(() => {
historyArray.splice(index, 1);
saveHistory(historyArray);
renderHistory(historyArray);
}, 400);
}
function renderHistory(historyArray) {
const list = document.getElementById("historyList");
list.innerHTML = "";
historyArray.forEach((item, index) => {
const card = document.createElement("div");
card.className = "history-card";
card.style.backgroundColor = getCardColor(item.sum);
card.innerHTML = `
<p>日時:${item.dateTime}</p>
<p>サイコロ1:${item.dice1}</p>
<p>サイコロ2:${item.dice2}</p>
<p>合計:${item.sum}</p>
<p>メッセージ:${item.message}</p>
<button class="history-detail-btn">詳細</button>
<button class="history-delete-btn">削除</button>
`;
card.querySelector(".history-detail-btn").addEventListener("click", function() {
showDetail(item);
});
card.querySelector(".history-delete-btn").addEventListener("click", function() {
deleteHistory(index, card);
});
list.appendChild(card);
});
}
rollBtn.addEventListener("click", function() {
const n1 = rollDice();
const n2 = rollDice();
const sum = n1 + n2;
dice1.textContent = `サイコロ1:${n1}`;
dice2.textContent = `サイコロ2:${n2}`;
diceSum.textContent = `合計:${sum}`;
updateBackground(sum);
const msg = getSumMessage(sum);
diceMessage.textContent = msg;
const now = new Date();
const dateTimeText =
`${now.getFullYear()}-${now.getMonth()+1}-${now.getDate()} ${now.getHours()}:${now.getMinutes()}`;
const historyItem = {
dice1: n1,
dice2: n2,
sum: sum,
dateTime: dateTimeText,
message: msg,
time: now.getTime()
};
historyArray.push(historyItem);
saveHistory(historyArray);
renderHistory(historyArray);
});
clearHistoryBtn.addEventListener("click", function() {
localStorage.removeItem("diceHistory");
historyArray = [];
renderHistory(historyArray);
});
sortNewBtn.addEventListener("click", function() {
historyArray.sort((a, b) => b.time - a.time);
renderHistory(historyArray);
});
sortOldBtn.addEventListener("click", function() {
historyArray.sort((a, b) => a.time - b.time);
renderHistory(historyArray);
});
renderHistory(historyArray);
JavaScript5日目で本当に掴んでほしいこと
● DOM操作は「画面を作り変える力」
カードUI、詳細表示、削除アニメーションなど、 DOMを自在に扱えるとアプリが一気に本格的になる。
● ローカルストレージ+並び替えで“データ管理アプリ”になる
履歴が残り、並び替えられ、詳細が見られることで、 サイコロアプリは「自分の運勢ログ」に進化する。
● イベント処理は複数組み合わせると強力
- click
- DOM追加
- ローカルストレージ保存
- アニメーション
これらが組み合わさると、 ユーザーが気持ちよく使えるアプリになる。
次のステップ(6日目)
6日目ではさらに一歩進んで、
- メモ編集機能
- カードの色分け強化
- 削除アニメーションの改善
など、UI/UXをさらに磨いていきます。
次に進む前に、 もし「並び替えの仕組みをもっと深掘りしたい」なら 並び替えの仕組み を選ぶとさらに理解が深まります。


