JavaScript | 1 日 60 分 × 7 日アプリ学習:JavaScript基礎・DOM操作 サイコロ編

Web APP JavaScript
スポンサーリンク
スポンサーリンク

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);
  });
}
JavaScript

5日目の完成コード(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);
JavaScript

5日目で本当に掴んでほしいこと

● DOM操作は「画面を作り変える力」

カードUI、詳細表示、削除アニメーションなど、 DOMを自在に扱えるとアプリが一気に本格的になる。

● ローカルストレージ+並び替えで“データ管理アプリ”になる

履歴が残り、並び替えられ、詳細が見られることで、 サイコロアプリは「自分の運勢ログ」に進化する。

● イベント処理は複数組み合わせると強力

  • click
  • DOM追加
  • ローカルストレージ保存
  • アニメーション

これらが組み合わさると、 ユーザーが気持ちよく使えるアプリになる。

次のステップ(6日目)

6日目ではさらに一歩進んで、

  • メモ編集機能
  • カードの色分け強化
  • 削除アニメーションの改善

など、UI/UXをさらに磨いていきます。

次に進む前に、 もし「並び替えの仕組みをもっと深掘りしたい」なら 並び替えの仕組み を選ぶとさらに理解が深まります。

タイトルとURLをコピーしました