JavaScript | 1 日 90 分 × 7 日アプリ学習:ミニ総合アプリ(初級編)

JavaScript
スポンサーリンク

7日目のゴールと今日やること

7日目のテーマは
「ミニ総合アプリを“自分で説明できる完成作品”にする」
ことです。

ここまで 6 日間であなたは、かなり多くのことを積み上げてきました。

入力
一覧
削除
条件分岐

というキーワードからスタートして、

オブジェクト配列でのデータ管理
ID 管理
編集・完了フラグ・表示モード
バリデーション
localStorage による保存
件数の集計と状態メッセージ

まで、一通りの「ミニ Web アプリの構成要素」を体験しています。

7日目は、

アプリ全体の流れを整理して言語化する
コードを読みやすく整える(コメント・命名)
「自分なりの仕様」を一つだけ追加してみる

という、“仕上げと振り返り”の日にします。


このミニ総合アプリは何をしているアプリかを言葉にする

アプリの機能を自分の言葉で説明してみる

まずは、あなた自身の言葉でこう説明できるかを意識してみてください。

タイトルとカテゴリを入力してタスクを追加できる
一覧でタスクを確認できる
タスクを完了・未完了に切り替えられる
タスクを削除できる
表示モードを切り替えて「すべて/未完了/完了のみ」を見られる
件数の集計が表示される
ブラウザを閉じてもデータが残る

これを「人に説明できる」ようになることが、
実はコードを書くことと同じくらい大事です。


アプリ全体の構造をもう一度整理する

データ(モデル)の部分

このアプリの“心臓”は items 配列です。

一件はこういう形でした。

{
  id: 1,
  title: "資料作成",
  category: "仕事",
  done: false
}
JavaScript

ここに、

タイトル(表示のメイン)
カテゴリ(分類)
完了フラグ(状態)

という「意味のある情報」がまとまっています。

さらに、
items 自体は localStorage に保存されることで、
「一時的な変数」ではなく
「アプリの状態」として扱われています。

画面(ビュー)の部分

画面側は、大きく分けて次のような領域があります。

入力欄(タイトル・カテゴリ・追加ボタン)
一覧表示エリア
エラー表示エリア
集計表示エリア
状態メッセージエリア
表示モード切り替えボタン

JavaScript は、
items の中身をもとに
この画面を「毎回描き直す」スタイルで動いています。

ロジック(制御)の部分

制御の中心は、次のような関数たちです。

addItem
updateItem(もし編集を入れていれば)
toggleDone
deleteItem
renderList
renderStats
updateStatusMessage
saveItems / loadItems

これらが、

入力イベント
ボタンクリック
ページ読み込み

といったタイミングで呼ばれ、
アプリ全体が動いています。


アプリの「1サイクル」を追ってみる

追加の流れを頭の中でトレースする

タイトルとカテゴリを入力して「追加」を押したとき、
何が起きているかを順番に追ってみます。

入力欄から値を取り出す
バリデーション関数でチェックする
問題なければ newItem オブジェクトを作る
items に push する
saveItems() で localStorage に保存する
renderList() で一覧を描き直す
renderStats() で件数を更新する
updateStatusMessage() で状態メッセージを更新する

この「流れを説明できる」ことが、
かなり大きな成長ポイントです。

完了切り替えの流れ

完了ボタンを押したときはこうです。

どのタスクかを id で特定する
対象の item を find で見つける
done フラグを反転させる
saveItems() で保存する
renderList() で見た目(取り消し線など)を更新する
renderStats() と updateStatusMessage() で集計とメッセージを更新する

ここでもやはり、

状態を変える
保存する
画面を更新する

というパターンが繰り返されています。


コードを「他人に渡せるレベル」に整える

コメントで「意図」を残す

7日目では、
コードの中に「なぜそうしているのか」を
コメントとして残していきます。

例として、重要な部分だけコメント付きで書いてみます。

// タスク一覧を保持する配列(アプリのメインデータ)
const items = [];
let nextId = 1;          // 次に使う ID
let viewMode = "all";    // 表示モード: all | todo | done
const STORAGE_KEY = "miniAppItems"; // localStorage 用キー

// データをブラウザに保存する
function saveItems() {
  const json = JSON.stringify(items); // 配列を JSON 文字列に変換
  localStorage.setItem(STORAGE_KEY, json);
}

// ブラウザからデータを読み込む
function loadItems() {
  const json = localStorage.getItem(STORAGE_KEY);
  if (!json) return;

  try {
    const data = JSON.parse(json); // JSON 文字列を配列に戻す
    if (Array.isArray(data)) {
      items.length = 0;
      data.forEach((item) => items.push(item));
      // nextId を復元(最大の id + 1 にしておく)
      const maxId = items.reduce((max, item) => Math.max(max, item.id), 0);
      nextId = maxId + 1;
    }
  } catch (e) {
    console.error("保存データの読み込みに失敗しました", e);
  }
}

// 新しいタスクを追加する
function addItem(title, category) {
  const newItem = {
    id: nextId,
    title,
    category,
    done: false
  };
  items.push(newItem);
  nextId += 1;
  saveItems();
}

// 完了フラグを切り替える
function toggleDone(id) {
  const target = items.find((item) => item.id === id);
  if (!target) return;
  target.done = !target.done;
  saveItems();
}

// タスクを削除する
function deleteItem(id) {
  const newItems = items.filter((item) => item.id !== id);
  items.length = 0;
  newItems.forEach((i) => items.push(i));
  saveItems();
}

// 集計情報を計算する
function getStats() {
  const total = items.length;
  const doneCount = items.filter((item) => item.done).length;
  const todoCount = total - doneCount;
  return { total, doneCount, todoCount };
}
JavaScript

こうやってコメントを付けていくと、
「自分が何をしているのか」がよりクリアになります。


自分なりの仕様を一つだけ足してみる

例として「カテゴリごとの色分け」を足す

せっかくなので、
7日目の仕上げとして「自分のアイデア」を一つだけ足してみましょう。

例えば、カテゴリごとに色を変える、というのはどうでしょう。

仕事 → 青系
プライベート → 緑系
その他 → グレー

renderList の中で、カテゴリに応じて色を変えられます。

function getCategoryColor(category) {
  if (category === "仕事") return "#1e90ff";
  if (category === "プライベート") return "#228b22";
  return "#555555";
}

function renderList() {
  listArea.innerHTML = "";

  let visibleItems = items;
  if (viewMode === "todo") {
    visibleItems = items.filter((item) => !item.done);
  } else if (viewMode === "done") {
    visibleItems = items.filter((item) => item.done);
  }

  if (visibleItems.length === 0) {
    const p = document.createElement("p");
    p.textContent = "表示できるデータがありません。";
    listArea.appendChild(p);
    return;
  }

  visibleItems.forEach((item) => {
    const row = document.createElement("div");

    const titleSpan = document.createElement("span");
    titleSpan.textContent = item.title;

    if (item.done) {
      titleSpan.style.textDecoration = "line-through";
      titleSpan.style.color = "#888";
    }

    const categorySpan = document.createElement("span");
    categorySpan.textContent = `(${item.category})`;
    categorySpan.style.marginLeft = "8px";
    categorySpan.style.color = getCategoryColor(item.category);

    const doneButton = document.createElement("button");
    doneButton.textContent = item.done ? "未完了に戻す" : "完了";
    doneButton.addEventListener("click", () => {
      toggleDone(item.id);
      renderList();
      renderStats();
      updateStatusMessage();
    });

    const deleteButton = document.createElement("button");
    deleteButton.textContent = "削除";
    deleteButton.addEventListener("click", () => {
      deleteItem(item.id);
      renderList();
      renderStats();
      updateStatusMessage();
    });

    row.appendChild(titleSpan);
    row.appendChild(categorySpan);
    row.appendChild(doneButton);
    row.appendChild(deleteButton);

    listArea.appendChild(row);
  });
}
JavaScript

こういう「自分の好み」を一つ入れるだけで、
それはもう「教材のコード」ではなく
あなたの作品になります。


7日間を通して、あなたが身につけたこと

ここまでの 7 日間で、あなたは確実にこういう力を身につけています。

配列とオブジェクトでデータを設計する力
DOM を使って画面を組み立てる力
イベント(クリック・入力)をトリガーに動きを作る力
条件分岐で「状態に応じた振る舞い」を決める力
localStorage で状態を保存・復元する力
コードを整理し、コメントで意図を残す力

そして何より、

「入力・一覧・削除・条件分岐」という
一見バラバラな要素が、
一つのアプリとしてつながる感覚

を、ちゃんと体で掴んでいます。

ここから先は、
このミニ総合アプリをベースにして、

入力項目を増やす
デザインを整える
別の種類のアプリに作り変えてみる

など、いくらでも遊べます。

最後に一つだけ聞きたいのは、

この 7 日間で作った中で、
「自分的に一番気に入っているポイント」はどこか、
ということです。

そこに、あなたの“開発者としての好み”が
もうちゃんと顔を出しています。

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