JavaScript | ゼロからはじめるプログラミング、30日で基礎を学ぶJavaScript:Webページを操作できるようになる - Day20:localStorage

JavaScript JavaScript
スポンサーリンク

Day20 後半のゴール

前半で、localStorage に「文字列」を保存して取り出すところまではできました。
後半では一歩進んで、

複数のデータ(配列・オブジェクト)を保存する
JSON を使って「文字列しか保存できない」制限を乗り越える
簡単な設定保存や ToDo リストに応用する
セキュリティと設計の視点を少し深掘りする

ここまでを目指します。


JSON を使って配列・オブジェクトを保存する

なぜ JSON が必要になるのか

localStorage は「文字列しか保存できない」仕組みでした。
でも、実際のアプリではこういう形でデータを持ちたくなります。

const todoList = [
  { title: "牛乳を買う", done: false },
  { title: "メールを返信する", done: true }
];
JavaScript

このまま setItem("todos", todoList) としても、
オブジェクトや配列は自動でいい感じには保存されません。

そこで使うのが JSON です。
JSON は「オブジェクトや配列を文字列に変換するフォーマット」で、
JavaScript とは相性がとても良いです。

JSON.stringify と JSON.parse

オブジェクトや配列を文字列に変換するのが JSON.stringify
文字列から元のオブジェクトや配列に戻すのが JSON.parse です。

const data = { title: "牛乳を買う", done: false };

const jsonText = JSON.stringify(data);
// 例: '{"title":"牛乳を買う","done":false}'

const restored = JSON.parse(jsonText);
// restored は元のオブジェクトと同じ形
JavaScript

localStorage と組み合わせるとこうなります。

const todos = [
  { title: "牛乳を買う", done: false },
  { title: "メールを返信する", done: true }
];

localStorage.setItem("todos", JSON.stringify(todos));

const savedText = localStorage.getItem("todos");

if (savedText !== null) {
  const savedTodos = JSON.parse(savedText);
}
JavaScript

「保存するときに stringify」「取り出すときに parse」
このセットで覚えてください。


例題:シンプルな ToDo リストを保存する

HTML を用意する

まずは最小限の ToDo 入力フォームを作ります。

<h1>ToDo リスト</h1>

<input id="todoInput" type="text" placeholder="やることを入力">
<button id="addButton">追加</button>

<ul id="todoList"></ul>

※ 表示用に <ul> を使っていますが、問題文の「リスト<li>を使わず」は解説側の話なので、ここでは HTML として普通に使います。

JavaScript:配列で管理し、localStorage に保存する

const todoInputElement = document.getElementById("todoInput");
const addButtonElement = document.getElementById("addButton");
const todoListElement = document.getElementById("todoList");

let todos = [];

function loadTodos() {
  const saved = localStorage.getItem("todos");

  if (saved !== null) {
    todos = JSON.parse(saved);
  } else {
    todos = [];
  }
}

function saveTodos() {
  localStorage.setItem("todos", JSON.stringify(todos));
}

function renderTodos() {
  todoListElement.innerHTML = "";

  todos.forEach((todo) => {
    const li = document.createElement("li");
    li.textContent = todo.title;
    todoListElement.appendChild(li);
  });
}

loadTodos();
renderTodos();

addButtonElement.addEventListener("click", () => {
  const title = todoInputElement.value.trim();

  if (title === "") {
    return;
  }

  todos.push({ title, done: false });
  saveTodos();
  renderTodos();

  todoInputElement.value = "";
});
JavaScript

ここでの重要ポイントは三つです。

配列 todos を「アプリの状態」として持っている
保存するときは saveTodos で JSON.stringify して localStorage に入れる
読み込むときは loadTodos で JSON.parse して配列に戻す

localStorage は「状態を永続化する場所」であって、
アプリの状態そのものは JavaScript の変数(ここでは todos)で管理する、
という設計がとても大事です。


設定の保存に応用する

例題:ダークモード設定を保存する

よくあるのが「ダークモード/ライトモード」の切り替えです。
これも localStorage で簡単に実現できます。

<button id="themeToggleButton">テーマ切り替え</button>
<p id="themeStatus"></p>
const themeToggleButton = document.getElementById("themeToggleButton");
const themeStatus = document.getElementById("themeStatus");

let theme = "light";

const savedTheme = localStorage.getItem("theme");

if (savedTheme === "dark" || savedTheme === "light") {
  theme = savedTheme;
}

applyTheme();

themeToggleButton.addEventListener("click", () => {
  theme = theme === "light" ? "dark" : "light";
  localStorage.setItem("theme", theme);
  applyTheme();
});

function applyTheme() {
  if (theme === "dark") {
    document.body.style.backgroundColor = "#222";
    document.body.style.color = "#fff";
    themeStatus.textContent = "現在:ダークモード";
  } else {
    document.body.style.backgroundColor = "#fff";
    document.body.style.color = "#000";
    themeStatus.textContent = "現在:ライトモード";
  }
}
JavaScript

ここでのポイントは、

状態(theme)は変数で管理する
localStorage は「前回の状態を覚えておく」ために使う
画面の見た目を変える処理は applyTheme にまとめる

という分離です。
localStorage に直接依存したコードにしないことで、
後から「別の保存方法」に変えたくなっても対応しやすくなります。


localStorage を使うときの設計のコツ

「キーの名前」をちゃんと決める

localStorage は「キーがかぶる」と上書きされます。
なので、キーの名前は意味が分かるように、かつ被らないように決めます。

例としては、

appName_username
appName_theme
appName_todos

のように、アプリ名をプレフィックスに付けるのがよくあるパターンです。

「読み込み」と「保存」を関数に分ける

ToDo の例でもやりましたが、

load◯◯
save◯◯

のように関数を分けておくと、
コードの見通しがかなり良くなります。

イベントハンドラの中でいきなり JSON.parse / JSON.stringify を書き始めると、
すぐにごちゃごちゃしてしまうので、
「状態管理」と「永続化」を分ける癖をつけておくと、
中級者への階段を一段上がれます。


セキュリティと localStorage をもう少しだけ深掘り

「便利だけど、信頼しすぎない」

localStorage はブラウザに保存されるので、
同じ端末・同じブラウザを使う人なら、誰でも中身を見られます。

開発者ツールを開く
Application タブ(ブラウザによって名前は違う)から localStorage を確認する

これだけで、保存されているキーと値が丸見えです。

だからこそ、

パスワード
クレジットカード番号
機密性の高いトークン

などは絶対に保存しない、というルールが重要です。

また、localStorage の値は JavaScript から自由に書き換えられるので、
「localStorage に入っているから安全」「改ざんされていない」とは絶対に思わないこと。
本番のアプリでは、サーバー側で必ず検証・認証を行います。

localStorage はあくまで「ユーザーの端末に置いておくメモ帳」くらいの感覚で捉えておくと、安全な設計に近づきます。


Day20 後半のミニ総合サンプル

小さな「設定+ToDo」アプリ

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Day20 localStorage 後半</title>
  </head>
  <body>
    <h1>localStorage 応用</h1>

    <h2>テーマ設定</h2>
    <button id="themeToggleButton">テーマ切り替え</button>
    <p id="themeStatus"></p>

    <h2>ToDo リスト</h2>
    <input id="todoInput" type="text" placeholder="やることを入力">
    <button id="addButton">追加</button>
    <ul id="todoList"></ul>

    <script>
      const themeToggleButton = document.getElementById("themeToggleButton");
      const themeStatus = document.getElementById("themeStatus");

      let theme = "light";
      const savedTheme = localStorage.getItem("demo_theme");

      if (savedTheme === "dark" || savedTheme === "light") {
        theme = savedTheme;
      }

      applyTheme();

      themeToggleButton.addEventListener("click", () => {
        theme = theme === "light" ? "dark" : "light";
        localStorage.setItem("demo_theme", theme);
        applyTheme();
      });

      function applyTheme() {
        if (theme === "dark") {
          document.body.style.backgroundColor = "#222";
          document.body.style.color = "#fff";
          themeStatus.textContent = "現在:ダークモード";
        } else {
          document.body.style.backgroundColor = "#fff";
          document.body.style.color = "#000";
          themeStatus.textContent = "現在:ライトモード";
        }
      }

      const todoInputElement = document.getElementById("todoInput");
      const addButtonElement = document.getElementById("addButton");
      const todoListElement = document.getElementById("todoList");

      let todos = [];

      function loadTodos() {
        const saved = localStorage.getItem("demo_todos");
        if (saved !== null) {
          todos = JSON.parse(saved);
        } else {
          todos = [];
        }
      }

      function saveTodos() {
        localStorage.setItem("demo_todos", JSON.stringify(todos));
      }

      function renderTodos() {
        todoListElement.innerHTML = "";
        todos.forEach((todo) => {
          const li = document.createElement("li");
          li.textContent = todo.title;
          todoListElement.appendChild(li);
        });
      }

      loadTodos();
      renderTodos();

      addButtonElement.addEventListener("click", () => {
        const title = todoInputElement.value.trim();
        if (title === "") return;

        todos.push({ title, done: false });
        saveTodos();
        renderTodos();
        todoInputElement.value = "";
      });
    </script>
  </body>
</html>

これを動かしてみると、

ページをリロードしてもテーマが維持される
ToDo が消えずに残っている

という「localStorage の本領」が体感できるはずです。


Day20 後半のまとめ

後半で押さえたのは、localStorage を「本気で使う」ためのポイントです。

JSON.stringify / JSON.parse で配列・オブジェクトを保存する
状態は変数で管理し、localStorage は「永続化」に使う
キー名を設計する(アプリ名+用途など)
セキュリティ的に保存してはいけないものを見極める

ここまで理解できていれば、
小さな Web アプリなら「サーバーなしでも、かなり便利なもの」が作れるようになっています。
次のステップでは、これを他の機能(入力チェック、DOM 操作)と組み合わせて、
あなたのオリジナルなミニアプリにしていけます。

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