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

JavaScript
スポンサーリンク

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

4日目のテーマは
「ミニ総合アプリに“編集機能”と“並び替え(ソート)”を加えて、アプリらしさを一段上げる」
ことです。

ここまでであなたは、

  • 入力して一覧に追加する
  • 削除する
  • 条件分岐でメッセージや強調表示を変える
  • 検索で絞り込む

というところまで来ました。

4日目はここに、

  • 一覧の各行を「編集できる」ようにする
  • 並び替え(ソート)で表示順を変えられるようにする
  • 条件分岐を「編集中かどうか」にも使う

という、“一段階大人なアプリ”の要素を足していきます。


編集機能を追加するイメージをつかむ

どんな動きにしたいか

やりたいことはシンプルです。

  • 一覧の行に「編集」ボタンを付ける
  • 押したら、その行のテキストを入力欄に戻す
  • 変更して「更新」ボタンを押すと、元のデータが書き換わる

ここで大事になるのが
「今、どのアイテムを編集しているのか」
という状態を持つことです。


「編集中の ID」を状態として持つ

状態を表す変数を用意する

let editingId = null;
JavaScript

この変数が、

  • null のとき → 新規追加モード
  • 数字(id)が入っているとき → 編集モード

という意味になります。

追加ボタンを「モードによって動きを変える」

addButton.addEventListener("click", () => {
  const text = input.value.trim();
  if (text === "") return;

  if (editingId === null) {
    addItem(text);
  } else {
    updateItem(editingId, text);
  }

  input.value = "";
  editingId = null;
  addButton.textContent = "追加";
  renderList();
});
JavaScript

ここが今日の超重要ポイントです。

同じボタンでも「状態」によって役割を変える
これがアプリっぽさの正体のひとつです。


新規追加と更新を関数に分ける

新規追加用の関数

function addItem(text) {
  const newItem = {
    id: nextId,
    text: text
  };
  items.push(newItem);
  nextId += 1;
}
JavaScript

更新用の関数

function updateItem(id, newText) {
  const target = items.find((item) => item.id === id);
  if (!target) return;
  target.text = newText;
}
JavaScript

ここで使っている find は、
「条件に合う最初の要素を返す」メソッドです。


一覧に「編集」ボタンを追加する

renderList に編集ボタンを足す

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

  if (items.length === 0) {
    message.textContent = "データがありません。";
    return;
  } else {
    message.textContent = "";
  }

  items.forEach((item) => {
    const row = document.createElement("div");
    row.textContent = item.text;

    const editButton = document.createElement("button");
    editButton.textContent = "編集";

    editButton.addEventListener("click", () => {
      startEdit(item);
    });

    const deleteButton = document.createElement("button");
    deleteButton.textContent = "削除";

    deleteButton.addEventListener("click", () => {
      deleteItem(item.id);
    });

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

編集開始の処理

function startEdit(item) {
  input.value = item.text;
  editingId = item.id;
  addButton.textContent = "更新";
}
JavaScript

ここでやっていることは、

  • 入力欄に元のテキストを入れる
  • editingId に対象の id を入れる
  • ボタンのラベルを「更新」に変える

という「編集モードへの切り替え」です。


条件分岐で「モードによって UI を変える」感覚

ボタンのラベルを変えるのも条件分岐

さっきの addButton の処理は、
実は if 文によるモード分岐です。

if (editingId === null) {
  addItem(text);
} else {
  updateItem(editingId, text);
}
JavaScript

このように、

  • 状態(editingId)
  • 条件分岐(if)
  • 振る舞い(追加 or 更新)

がセットになっているのが、
アプリの基本的な構造です。


並び替え(ソート)機能を追加する

ソートボタンを用意する

HTML にボタンを追加します。

<button id="sortAscButton">昇順</button>
<button id="sortDescButton">降順</button>

JavaScript で取得します。

const sortAscButton = document.getElementById("sortAscButton");
const sortDescButton = document.getElementById("sortDescButton");
JavaScript

ソート処理を実装する

sortAscButton.addEventListener("click", () => {
  items.sort((a, b) => {
    if (a.text < b.text) return -1;
    if (a.text > b.text) return 1;
    return 0;
  });
  renderList();
});

sortDescButton.addEventListener("click", () => {
  items.sort((a, b) => {
    if (a.text < b.text) return 1;
    if (a.text > b.text) return -1;
    return 0;
  });
  renderList();
});
JavaScript

深掘り:sort の比較関数の意味

items.sort((a, b) => { ... }) の中で返している値は、

  • 負の値 → a を先に
  • 正の値 → b を先に
  • 0 → 順番そのまま

というルールで解釈されます。

文字列の比較では
a.text < b.text を使って
「辞書順でどちらが先か」を判定しています。


条件分岐を「ソート状態」にも応用する

今どのソート状態かを持つ

状態を持つと、
「今どういう並び順なのか」を UI に反映できます。

let sortMode = "none"; // "none" | "asc" | "desc"
JavaScript

ソートボタンで状態を更新します。

sortAscButton.addEventListener("click", () => {
  sortMode = "asc";
  sortItems();
  renderList();
});

sortDescButton.addEventListener("click", () => {
  sortMode = "desc";
  sortItems();
  renderList();
});
JavaScript

ソート処理を関数にまとめます。

function sortItems() {
  if (sortMode === "asc") {
    items.sort((a, b) => (a.text < b.text ? -1 : a.text > b.text ? 1 : 0));
  } else if (sortMode === "desc") {
    items.sort((a, b) => (a.text < b.text ? 1 : a.text > b.text ? -1 : 0));
  }
}
JavaScript

このように、
状態(sortMode)と条件分岐(if / else if)
を組み合わせることで、
アプリの振る舞いをコントロールできます。


4日目の完成コード(要点まとめ)

let nextId = 1;
const items = [];
let editingId = null;
let sortMode = "none";

const input = document.getElementById("itemInput");
const addButton = document.getElementById("addButton");
const listArea = document.getElementById("listArea");
const message = document.getElementById("message");
const sortAscButton = document.getElementById("sortAscButton");
const sortDescButton = document.getElementById("sortDescButton");

addButton.addEventListener("click", () => {
  const text = input.value.trim();
  if (text === "") return;

  if (editingId === null) {
    addItem(text);
  } else {
    updateItem(editingId, text);
  }

  input.value = "";
  editingId = null;
  addButton.textContent = "追加";

  sortItems();
  renderList();
});

sortAscButton.addEventListener("click", () => {
  sortMode = "asc";
  sortItems();
  renderList();
});

sortDescButton.addEventListener("click", () => {
  sortMode = "desc";
  sortItems();
  renderList();
});

function addItem(text) {
  const newItem = {
    id: nextId,
    text: text
  };
  items.push(newItem);
  nextId += 1;
}

function updateItem(id, newText) {
  const target = items.find((item) => item.id === id);
  if (!target) return;
  target.text = newText;
}

function deleteItem(id) {
  const newItems = items.filter((item) => item.id !== id);
  items.length = 0;
  newItems.forEach((i) => items.push(i));
  sortItems();
  renderList();
}

function sortItems() {
  if (sortMode === "asc") {
    items.sort((a, b) => (a.text < b.text ? -1 : a.text > b.text ? 1 : 0));
  } else if (sortMode === "desc") {
    items.sort((a, b) => (a.text < b.text ? 1 : a.text > b.text ? -1 : 0));
  }
}

function startEdit(item) {
  input.value = item.text;
  editingId = item.id;
  addButton.textContent = "更新";
}

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

  if (items.length === 0) {
    message.textContent = "データがありません。";
    return;
  } else {
    message.textContent = "";
  }

  items.forEach((item) => {
    const row = document.createElement("div");
    row.textContent = item.text;

    const editButton = document.createElement("button");
    editButton.textContent = "編集";
    editButton.addEventListener("click", () => {
      startEdit(item);
    });

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

    row.appendChild(editButton);
    row.appendChild(deleteButton);
    listArea.appendChild(row);
  });
}

renderList();
JavaScript

今日いちばん深く理解してほしいこと

アプリは「状態 × 条件分岐 × 振る舞い」でできている

今日出てきた状態は、

  • editingId(編集中かどうか)
  • sortMode(並び順)

これらを if 文で分岐し、
追加・更新・ソート・表示を切り替えました。

状態を持つ → 条件分岐で振る舞いを変える
このパターンを掴めると、
アプリの設計が一気に楽しくなります。

明日はここから、
「複数項目の入力」「複合条件の分岐」
に進んでいきます。
もう完全に“アプリを作っている人の思考”になっていますよ。

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