JavaScript | 1 日 60 分 × 7 日アプリ学習:JavaScript基礎・DOM操作 おみくじ編

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

7日目のゴールと全体像

おみくじアプリ 7日目のテーマは 「6日間で積み上げてきた機能を“完成版”として整理し、アプリ全体の構造を理解しながら総復習すること」 です。

ここまでで、みなさんは

  • ランダム結果の生成
  • 結果ごとのメッセージ・背景色・ラッキーアイテム
  • 履歴保存(ローカルストレージ)
  • 履歴一覧表示・削除
  • 並び替え(新しい順・古い順)
  • 詳細表示
  • メモ編集
  • カード色分け
  • 削除アニメーション

という、初級者が学ぶべき JavaScript の基礎をすべて体験 してきました。

7日目では、これらを ひとつの完成したアプリとしてまとめる ことで、 「JavaScript初級者から Webアプリを作れる人」へステップアップします。

アプリ全体の構造を理解する

完成版のおみくじアプリは、次の3つのレイヤーで構成されています。

● HTML(画面の骨格)

  • ボタン
  • 結果表示エリア
  • メッセージ表示エリア
  • ラッキーアイテム表示エリア
  • 履歴一覧
  • 詳細表示エリア

● CSS(見た目・アニメーション)

  • 結果の色分け
  • 背景色変更
  • 履歴カードのデザイン
  • 削除アニメーション
  • 詳細カードのデザイン

● JavaScript(動き・データ管理)

  • ランダム処理
  • DOM操作
  • イベント処理
  • ローカルストレージ
  • 履歴管理(追加・削除・編集)
  • 並び替え
  • 詳細表示

この3つが組み合わさることで、 「入力 → 計算 → 表示 → 保存 → 編集 → 削除」 という一連の流れが成立します。

完成版HTML(最終形)

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>おみくじアプリ 完成版</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h2>おみくじ</h2>

  <button id="omikujiBtn">おみくじを引く</button>

  <p id="result"></p>
  <p id="message"></p>
  <p id="luckyItem"></p>

  <h3>履歴</h3>
  <button id="sortNewBtn">新しい順</button>
  <button id="sortOldBtn">古い順</button>

  <div id="historyList"></div>

  <button id="clearHistoryBtn">履歴をすべて削除</button>

  <h3>詳細</h3>
  <div id="detailBox"></div>

  <script src="script.js"></script>
</body>
</html>

完成版CSS(最終形)

body {
  font-family: sans-serif;
  padding: 20px;
  transition: background-color 0.5s;
}

#result {
  font-size: 24px;
  margin-top: 20px;
}

#message {
  font-size: 16px;
  margin-top: 10px;
}

#luckyItem {
  font-size: 18px;
  margin-top: 15px;
  font-weight: bold;
}

.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;
}

完成版JavaScript(最終形)

const omikujiBtn = document.getElementById("omikujiBtn");
const result = document.getElementById("result");
const message = document.getElementById("message");
const luckyItem = document.getElementById("luckyItem");
const clearHistoryBtn = document.getElementById("clearHistoryBtn");
const sortNewBtn = document.getElementById("sortNewBtn");
const sortOldBtn = document.getElementById("sortOldBtn");

let historyArray = loadHistory();

// 結果候補
const results = ["大吉", "中吉", "小吉", "吉", "末吉", "凶"];

// 結果データ
const omikujiData = {
  "大吉": { rank: 5, message: "最高の一日になりそうです。" },
  "中吉": { rank: 4, message: "良い流れが来ています。" },
  "小吉": { rank: 3, message: "少しずつ前に進める日。" },
  "吉":   { rank: 2, message: "穏やかな一日。" },
  "末吉": { rank: 1, message: "準備をしておくと後で役に立ちます。" },
  "凶":   { rank: 0, message: "慎重に行動してみましょう。" }
};

// ラッキーアイテム
const luckyItems = [
  "赤いペン", "お気に入りの本", "コーヒー", "新しいノート",
  "青い服", "散歩", "音楽プレイリスト", "ストレッチ", "おいしいお菓子"
];

// ランダム結果
function getRandomResult() {
  const index = Math.floor(Math.random() * results.length);
  return results[index];
}

// ラッキーアイテム
function getRandomItem() {
  const index = Math.floor(Math.random() * luckyItems.length);
  return luckyItems[index];
}

// 背景色変更
function updateBackgroundByRank(rank) {
  if (rank === 5) document.body.style.backgroundColor = "#fff8b3";
  else if (rank === 4) document.body.style.backgroundColor = "#cce5ff";
  else if (rank === 3) document.body.style.backgroundColor = "#d4edda";
  else if (rank === 2) document.body.style.backgroundColor = "#e2d4ff";
  else if (rank === 1) document.body.style.backgroundColor = "#ffe5b4";
  else document.body.style.backgroundColor = "#f8d7da";
}

// カード色分け
function getCardColor(rank) {
  if (rank === 5) return "#fff8b3";
  if (rank === 4) return "#cce5ff";
  if (rank === 3) return "#d4edda";
  if (rank === 2) return "#e2d4ff";
  if (rank === 1) return "#ffe5b4";
  return "#f8d7da";
}

// 履歴保存
function saveHistory(arr) {
  localStorage.setItem("omikujiHistory", JSON.stringify(arr));
}

// 履歴読み込み
function loadHistory() {
  const data = localStorage.getItem("omikujiHistory");
  return data ? JSON.parse(data) : [];
}

// メモ編集
function editMemo(index) {
  const newMemo = prompt("メモを入力してください:");
  if (newMemo === null) return;

  historyArray[index].memo = newMemo;
  saveHistory(historyArray);
  renderHistory(historyArray);
}

// 詳細表示
function showDetail(item) {
  const detailBox = document.getElementById("detailBox");

  detailBox.innerHTML = `
    <div class="detail-card">
      <p>日時:${item.dateTime}</p>
      <p>結果:${item.result}</p>
      <p>ランク:${item.rank}</p>
      <p>ラッキーアイテム:${item.luckyItem}</p>
      <p>メモ:${item.memo || "なし"}</p>
    </div>
  `;
}

// 履歴描画
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.rank);

    card.innerHTML = `
      <p>日時:${item.dateTime}</p>
      <p>結果:${item.result}(ランク:${item.rank})</p>
      <p>ラッキーアイテム:${item.luckyItem}</p>
      <p>メモ:${item.memo || "なし"}</p>
      <button class="history-detail-btn">詳細</button>
      <button class="history-edit-btn">メモ編集</button>
      <button class="history-delete-btn">削除</button>
    `;

    card.querySelector(".history-detail-btn").addEventListener("click", function() {
      showDetail(item);
    });

    card.querySelector(".history-edit-btn").addEventListener("click", function() {
      editMemo(index);
    });

    card.querySelector(".history-delete-btn").addEventListener("click", function() {
      deleteHistory(index, card);
    });

    list.appendChild(card);
  });
}

// 削除(アニメーション付き)
function deleteHistory(index, cardElement) {
  cardElement.classList.add("fade-out");

  setTimeout(() => {
    historyArray.splice(index, 1);
    saveHistory(historyArray);
    renderHistory(historyArray);
  }, 400);
}

// おみくじボタン
omikujiBtn.addEventListener("click", function() {
  const randomResult = getRandomResult();
  const data = omikujiData[randomResult];

  result.textContent = `今日の運勢は「${randomResult}」です`;
  message.textContent = data.message;

  updateBackgroundByRank(data.rank);

  const item = getRandomItem();
  luckyItem.textContent = `今日のラッキーアイテム:${item}`;

  const now = new Date();
  const dateTimeText = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ${now.getHours()}:${now.getMinutes()}`;

  const historyItem = {
    dateTime: dateTimeText,
    time: now.getTime(),
    result: randomResult,
    rank: data.rank,
    luckyItem: item,
    memo: ""
  };

  historyArray.push(historyItem);
  saveHistory(historyArray);
  renderHistory(historyArray);
});

// 全履歴削除
clearHistoryBtn.addEventListener("click", function() {
  localStorage.removeItem("omikujiHistory");
  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

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

● JavaScriptの基礎が「ひとつのアプリ」で全部つながった

  • 変数
  • 条件分岐
  • 繰り返し
  • 関数
  • DOM操作
  • イベント処理
  • ローカルストレージ

これらがすべて「おみくじアプリ」という具体的な形の中で自然につながっています。

● Webアプリは「画面 × 動き × データ」でできている

  • HTML → 画面
  • CSS → 見た目
  • JavaScript → 動き・データ管理

この3つを組み合わせることで、 どんなWebアプリでも作れるようになります。

● DOM操作はアプリの“手足”

カード追加、削除、編集、アニメーションなど、 DOMを自在に扱えるとアプリが一気に本格的になる。

次のステップ(応用編への橋)

このおみくじアプリはまだまだ育てられます。

  • 結果の統計(大吉の割合など)
  • グラフ表示(結果の推移)
  • 履歴のCSVダウンロード
  • Webアプリ化(React / Vue)

初級編を終えたみなさんは、 もう 「JavaScriptで実用的なWebアプリを作れる人」 です。

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