localStorage で保存する
このレッスンで分かること
localStorage.setItem(キー, 値)でブラウザに値を残し、getItem(キー)で読み出せます- 保存できるのは文字列だけなので、配列やオブジェクトは
JSON.stringifyとJSON.parseを挟みます- 読み込んだ直後に保存値を画面へ反映すると、リロードしても設定が残ります
localStorage で保存する とは
ブラウザに小さなメモを残しておく仕組みです。ダークモードの設定を覚えさせて、次に開いたときも同じ見た目で始まるようにします。
変数はリロードで消える
第9章で作った #theme-toggle のダークモード切り替えは、押せばちゃんと色が変わります。ところがページを読み込み直すと、いつも明るい見た目に戻ってしまいます。当たり前で、変数も classList の状態も、ページが開いているあいだだけメモリの上にあるものだからです。リロードすると JavaScript は最初の 1 行から動き直し、前の状態はどこにも残っていません。
設定を覚えさせるには、ページの外に置き場所が要ります。その置き場所のうち、いちばん手軽なのが localStorage です。ブラウザがサイトごとに用意してくれる小さな保管庫で、書いた値はタブを閉じても、パソコンを再起動しても残ります。ログイン処理もサーバーも要らず、JavaScript から 1 行で書き込めます。
| 呼び出し | すること | 戻り値 |
|---|---|---|
| localStorage.setItem(キー, 値) | キーの名前で値を保存する | なし |
| localStorage.getItem(キー) | キーの値を読み出す | 文字列。無ければ null |
| localStorage.removeItem(キー) | そのキーを消す | なし |
| localStorage.clear() | このサイトの保存を全部消す | なし |
localStorage.setItem("theme", "dark");
const saved = localStorage.getItem("theme");
console.log(saved);
localStorage.removeItem("theme");キーは自分で決める名前です。theme memos のように、何が入っているか分かる短い英単語にしておきます。同じキーに setItem すると前の値は上書きされます。
localStorageはサイトごとに分かれています。自分のサイトで保存した値をよそのサイトから読むことはできませんし、その逆もできません。ただし同じサイトを開いている人なら開発者ツールから中身を見られるので、パスワードや個人情報を入れる場所ではありません。覚えさせてよいのは、テーマの設定や下書きのような、見られても困らないものだけです。
入るのは文字列だけ
localStorage に入れられるのは文字列だけです。数値や真偽値を渡すと、勝手に文字列へ変換されて入ります。
localStorage.setItem("count", 3);
console.log(localStorage.getItem("count")); // "3" 文字列
console.log(localStorage.getItem("count") + 1); // "31" になってしまう数値として使いたいなら Number(...) で戻します。第2章でやった型変換が、ここで効いてきます。
配列やオブジェクトはもっと厄介で、そのまま渡すと [object Object] という役に立たない文字列になります。そこで JSON.stringify で文字列に直してから保存し、読むときに JSON.parse でオブジェクトへ戻します。
const works = [{ title: "プロフィールサイト", year: 2026 }];
localStorage.setItem("works", JSON.stringify(works));
const raw = localStorage.getItem("works");
const restored = JSON.parse(raw);
console.log(restored[0].title); // プロフィールサイトgetItem は、そのキーが無いときに null を返します。null を JSON.parse に渡すとエラーになるので、読んだ直後に「無かったとき」の分岐を必ず書きます。第3章の ?? を使って JSON.parse(raw ?? "[]") と書く手もあります。
保存が使えない環境もある
localStorage は必ず使えるとは限りません。ブラウザの設定で保存を切っている人もいますし、プライベートウィンドウや、このレッスンのプレビューのように制限された枠の中では、触った瞬間にエラーになることがあります。エラーが出ると、その行から下の JavaScript が全部止まります。時計もフォームも動かなくなる、というのは割に合いません。
なので、保存まわりは try で囲んで、失敗しても先へ進めるようにします。
function saveTheme(value) {
try {
localStorage.setItem("theme", value);
} catch (error) {
console.log("この環境では保存できません");
}
}
function loadTheme() {
try {
return localStorage.getItem("theme");
} catch (error) {
return null;
}
}読めなかったときは null を返しておけば、呼ぶ側は「まだ何も保存されていない」ときと同じ扱いで済みます。保存できてもできなくても、ページの見た目と操作は壊れません。この形はふだんの仕事でもそのまま使えます。
読み込んだ直後に反映する
保存は書くだけでは意味がなく、読み込んだときに反映して初めて記憶になります。手順は 3 つです。まず読み出し、次に画面へ当て、最後にボタンで変えたときにまた保存する、という流れです。
function applyTheme(value) {
if (value === "dark") {
document.body.classList.add("dark");
themeToggle.textContent = "ライトモード";
themeStatus.textContent = "いまはダークモードです";
return;
}
document.body.classList.remove("dark");
themeToggle.textContent = "ダークモード";
themeStatus.textContent = "いまはライトモードです";
}
applyTheme(loadTheme());
themeToggle.addEventListener("click", function () {
const next = document.body.classList.contains("dark") ? "light" : "dark";
applyTheme(next);
saveTheme(next);
});見た目を変える仕事を applyTheme に 1 か所だけ持たせたのがポイントです。読み込んだときも、ボタンを押したときも、同じ関数を通ります。第6章でやった「同じ処理は関数にまとめる」がそのまま効いていて、あとから色を増やしたくなっても直す場所は 1 つで済みます。
removeItem を呼ぶボタンも用意しておくと、覚えた設定を捨てて最初の状態に戻せます。設定を消せる出口があると、試すのがぐっと楽になります。
よくある間違い
保存した値を読んだつもりで、真偽値として使ってしまう間違いです。
localStorage.setItem("dark", false);
// 間違い "false" という文字列は truthy なので、必ず中へ入る
if (localStorage.getItem("dark")) {
document.body.classList.add("dark");
}
// 正しい 中身の文字列で比べる
if (localStorage.getItem("dark") === "true") {
document.body.classList.add("dark");
}オブジェクトをそのまま渡すのも定番です。
// 間違い "[object Object]" が保存される
localStorage.setItem("works", works);
// 正しい 文字列にしてから渡す
localStorage.setItem("works", JSON.stringify(works));保存はしたのに読み込み時の反映を書き忘れる、というのもよくあります。ボタンを押すと切り替わるのにリロードで戻ってしまうときは、applyTheme(loadTheme()) のような 1 行が抜けていないか確かめてください。
課題
- ヘッダーに、設定を消す id が theme-reset の button と、いまの状態を出す id が theme-status の p を足す
- saveTheme と loadTheme と clearTheme の 3 つの関数を作り、中で setItem と getItem と removeItem を try で囲んで呼ぶ
- applyTheme 関数を作り、渡された値が dark なら body に dark を付け、そうでなければ外す。あわせてボタンの文字と theme-status を書き換える
- 読み込んだ直後に applyTheme に loadTheme の結果を渡して呼び、theme-toggle を押したら切り替えて保存する
- theme-reset を押したら保存を消し、ライトモードに戻す