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

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

5日目のゴールと全体像

おみくじアプリ 5日目のテーマは 「履歴を“並び替え・詳細表示”できるようにして、DOM操作とイベント処理を一段階レベルアップさせること」 です。

ここまでで、

  • おみくじ結果をランダムに表示
  • 結果ごとにメッセージ・背景色・ラッキーアイテムを表示
  • 履歴をローカルストレージに保存
  • 履歴をカードとして一覧表示・削除

という「データを扱うおみくじアプリ」の基礎ができました。

5日目ではここに

  • 履歴の並び替え(新しい順・古い順)
  • 履歴の詳細表示(1件を拡大して表示)

を追加して、 「ちょっとした占いサービス」のようなアプリに近づけていきます。

並び替え機能のイメージと設計

履歴には「日時」が入っています。 これを使って、

  • 新しい順(最近引いたものが上)
  • 古い順(昔のものが上)

に並び替えられるようにします。

並び替えボタンをHTMLに追加する

<h3>履歴</h3>

<!-- 並び替えボタン -->
<button id="sortNewBtn">新しい順</button>
<button id="sortOldBtn">古い順</button>

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

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

押さえておきたいポイント

  • id="sortNewBtn"id="sortOldBtn" をJavaScriptから取得して、 クリックイベントを登録します。
  • 並び替えは「配列の順番を変える」ことで実現します。

並び替えの基本:配列の sort メソッド

JavaScriptの配列には sort() というメソッドがあります。

historyArray.sort((a, b) => b.dateTimeValue - a.dateTimeValue);
JavaScript

のように書くと、 dateTimeValue を基準に並び替えができます。

5日目では、履歴オブジェクトに「数値の時間」を持たせておき、 それを使って並び替えます。

履歴オブジェクトに「time(数値)」を追加する

4日目の履歴オブジェクトはこうでした。

const historyItem = {
  dateTime: dateTimeText,
  result: randomResult,
  rank: data.rank,
  luckyItem: item
};
JavaScript

ここに「time」を追加します。

const historyItem = {
  dateTime: dateTimeText,      // 画面表示用の文字列
  time: now.getTime(),         // 並び替え用の数値(1970年からのミリ秒)
  result: randomResult,
  rank: data.rank,
  luckyItem: item
};
JavaScript

深掘りポイント

  • new Date().getTime() は「1970年1月1日からのミリ秒」を返します。
  • この数値は「時間が進むほど大きくなる」ので、 並び替えにとても使いやすいです。
  • 「人間向けの文字列」と「機械向けの数値」を両方持たせるのは、 実際のアプリでもよくあるパターンです。

並び替えボタンのイベント処理

要素の取得

const sortNewBtn = document.getElementById("sortNewBtn");
const sortOldBtn = document.getElementById("sortOldBtn");
JavaScript

新しい順に並び替える処理

sortNewBtn.addEventListener("click", function() {
  // timeが大きいもの(新しいもの)を前に
  historyArray.sort((a, b) => b.time - a.time);

  // 並び替え後の配列を画面に反映
  renderHistory(historyArray);
});
JavaScript

古い順に並び替える処理

sortOldBtn.addEventListener("click", function() {
  // timeが小さいもの(古いもの)を前に
  historyArray.sort((a, b) => a.time - b.time);

  renderHistory(historyArray);
});
JavaScript

深掘りポイント

  • sort() は「元の配列を並び替える」メソッドです。
  • (a, b) => b.time - a.time は「比較関数」と呼ばれます。
    • 結果が正の数 → aよりbが前
    • 結果が負の数 → bよりaが前
  • 並び替えたら必ず renderHistory() を呼んで画面を更新します。

詳細表示機能のイメージ

履歴カードをクリックしたときに、 その1件分の情報を「詳細エリア」に表示します。

  • 日時
  • 結果
  • ランク
  • ラッキーアイテム
  • 簡単なコメント(固定文でもOK)

HTMLに詳細表示エリアを追加する

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

詳細表示用の関数を作る

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>コメント:この結果をきっかけに、少しだけいつもと違う行動をしてみると面白いかもしれません。</p>
    </div>
  `;
}
JavaScript

CSSで詳細カードを整える

.detail-card {
  background: #f7f7f7;
  padding: 12px;
  margin-top: 10px;
  border-radius: 6px;
  border: 1px solid #ddd;
}

深掘りポイント

  • 詳細表示は「1件分のデータを、専用のエリアに描画する」だけ。
  • item オブジェクトをそのまま使うことで、 どの履歴をクリックしても同じ関数で表示できる。

履歴カードに「詳細ボタン」を追加する

renderHistory() の中で、 カードに「詳細を見る」ボタンを追加します。

function renderHistory(historyArray) {
  const list = document.getElementById("historyList");
  list.innerHTML = "";

  historyArray.forEach((item, index) => {
    const card = document.createElement("div");
    card.className = "history-card";

    card.innerHTML = `
      <p>日時:${item.dateTime}</p>
      <p>結果:${item.result}(ランク:${item.rank})</p>
      <p>ラッキーアイテム:${item.luckyItem}</p>
      <button class="history-detail-btn">詳細を見る</button>
      <button class="history-delete-btn">この履歴を削除</button>
    `;

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

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

    list.appendChild(card);
  });
}
JavaScript

深掘りポイント

  • showDetail(item) に「その履歴のオブジェクト」を渡している。
  • こうすることで、詳細表示関数は「どの履歴か」を意識せずに書ける。
  • イベント処理の中で「別の関数を呼ぶ」パターンは、 コードを読みやすくするためにとても重要です。

5日目の完成コード(script.js 全体)

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");

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

// 結果データ
const omikujiData = {
  "大吉": { rank: 5, message: "最高の一日になりそうです。思い切って新しいことに挑戦してみましょう!" },
  "中吉": { rank: 4, message: "良い流れが来ています。コツコツ続けていることが実を結びそうです。" },
  "小吉": { rank: 3, message: "少しずつ前に進める日。焦らず、目の前のことを丁寧にこなしましょう。" },
  "吉":   { rank: 2, message: "穏やかな一日。周りの人との会話を大切にすると、良いヒントが得られます。" },
  "末吉": { rank: 1, message: "まだ結果は見えないけれど、準備をしておくと後で役に立ちます。" },
  "凶":   { rank: 0, message: "無理をせず休むことも大事。今日は慎重に行動してみましょう。" }
};

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

// 履歴配列
let historyArray = loadHistory();

// ランダムに結果を選ぶ
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 saveHistory(arr) {
  localStorage.setItem("omikujiHistory", JSON.stringify(arr));
}

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

// 詳細表示
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>コメント:この結果をきっかけに、少しだけいつもと違う行動をしてみると面白いかもしれません。</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.innerHTML = `
      <p>日時:${item.dateTime}</p>
      <p>結果:${item.result}(ランク:${item.rank})</p>
      <p>ラッキーアイテム:${item.luckyItem}</p>
      <button class="history-detail-btn">詳細を見る</button>
      <button class="history-delete-btn">この履歴を削除</button>
    `;

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

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

    list.appendChild(card);
  });
}

// 履歴削除
function deleteHistory(index) {
  historyArray.splice(index, 1);
  saveHistory(historyArray);
  renderHistory(historyArray);
}

// おみくじボタン
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
  };

  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

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

  • 配列の sort() を使って「データの順番を変える」感覚
  • オブジェクトを渡して「詳細表示」をするパターン
  • 「データを変える → 保存する → DOMで画面を更新する」という一連の流れ

ここまで来ると、 おみくじアプリはもう立派な「小さなWebサービス」です。

6日目・7日目では、 このアプリを「完成版」として整理しながら、 JavaScript初級の総復習をしていきます。

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