localStorage で保存する
このレッスンで分かること
localStorage.setItem(キー, 値)で残し、getItem(キー)で読み出します- 保存できるのは文字列だけなので、配列やオブジェクトは
JSON.stringifyとJSON.parseを挟みます
リロードすると、選んだ設定が毎回もとに戻る
前回のダークモード切り替えは、押せばちゃんと色が変わります。ところがページを読み込み直すと、いつも明るい見た目から始まってしまいます。変数も classList の状態も、ページが開いているあいだだけメモリの上にあるものだからです。リロードすると JavaScript は 1 行目から動き直し、前の状態はどこにも残っていません。
設定を覚えさせるには、ページの外に置き場所が要ります。いちばん手軽なのが localStorage です。ブラウザがサイトごとに用意する小さな保管庫で、書いた値はタブを閉じてもパソコンを再起動しても残ります。
| 呼び出し | すること | 戻り値 |
|---|---|---|
| setItem(キー, 値) | キーの名前で値を保存する | なし |
| getItem(キー) | キーの値を読み出す | 文字列。無ければ null |
| removeItem(キー) | そのキーを消す | なし |
JavaScript
localStorage.setItem("draft", "書きかけの本文");
console.log(localStorage.getItem("draft")); // 書きかけの本文
localStorage.removeItem("draft");キーは自分で決める名前です。同じキーに setItem すると前の値は上書きされます。中身は開発者ツールから誰でも見られるので、覚えさせてよいのは、テーマの設定や下書きのような、見られても困らないものだけです。
入るのは文字列だけ
localStorage に入れられるのは文字列だけです。数値や真偽値は勝手に文字列へ変換され、配列やオブジェクトに至っては [object Object] という役に立たない文字列になります。
JavaScript
localStorage.setItem("count", 3);
console.log(localStorage.getItem("count") + 1); // "31" になってしまうそこで、まとまったデータは JSON.stringify で文字列に直してから保存し、読むときに JSON.parse でもとの形に戻します。
JavaScript
const settings = { fontSize: 16, compact: true };
localStorage.setItem("settings", JSON.stringify(settings));
const raw = localStorage.getItem("settings");
const restored = JSON.parse(raw);
console.log(restored.fontSize); // 16getItem は、そのキーが無いときに null を返します。null を JSON.parse に渡すとエラーになるので、読んだ直後に「まだ何も保存されていないとき」の分岐を必ず書きます。
保存に失敗すると、その先が全部止まる
localStorage は必ず使えるとは限りません。ブラウザの設定で保存を切っている人もいますし、プライベートウィンドウや、このレッスンのプレビューのように制限された枠の中では、触った瞬間にエラーになることがあります。エラーが出ると、その行から下の JavaScript が全部止まります。設定が覚えられないだけならまだしも、時計もフォームも動かなくなるのでは割に合いません。
なので、保存まわりは try で囲んで、失敗しても先へ進めるようにします。
JavaScript
function saveDraft(text) {
try {
localStorage.setItem("draft", text);
} catch (error) {
console.log("この環境では保存できません");
}
}
function loadDraft() {
try {
return localStorage.getItem("draft");
} catch (error) {
return null;
}
}読めなかったときに null を返しておけば、呼ぶ側は「まだ何も保存されていない」ときと同じ扱いで済みます。保存できてもできなくても、ページの操作は壊れません。
保存は書くだけでは記憶になりません。読み出す、画面へ当てる、変えたらまた保存する。この 3 つがそろって初めて、リロードしても同じ状態で始まります。
演習の前に保存と例外の役割を分ける
下書きの例で API と try/catch を確認してから演習へ進みます。演習には例外時の処理を用意してあります。保存の3つの API と、それを呼ぶ場所を自分で書いてください。
次のメモ制作では、削除ボタンが作られたときの番号を使います。小さな練習として色名の一覧を考えます。colorList は取得済みの空の ul とします。
JavaScript
const colors = ["赤", "青", "緑"];
function drawColors() {
colorList.textContent = "";
colors.forEach(function (color, position) {
const choice = document.createElement("button");
choice.textContent = color;
choice.addEventListener("click", function () {
colors.splice(position, 1);
drawColors();
});
colorList.append(choice);
});
}
drawColors();splice(position, 1) はその番号から1件だけ取り除きます。内側の関数は、そのボタンを作ったときの position を覚えています。削除後に一覧を作り直すと、残ったボタンも新しい番号を覚え直します。
よくある間違い
- 読み出した文字列を真偽値として使う —
setItem("dark", false)で入るのは"false"という文字列で、これは truthy です。if (getItem("dark"))は必ず中へ入るので、=== "true"のように中身の文字列で比べます - 読み込み時の反映を書き忘れる — 押すと切り替わるのにリロードで戻ってしまうときは、保存を読んで画面へ当てる 1 行が抜けています
課題
saveThemeのtry内で theme キーにvalueをsetItemするloadThemeのtry内で theme キーをgetItemし、その戻り値をreturnするclearThemeのtry内で theme キーをremoveItemする- 読み込み時に
loadThemeの結果をapplyThemeへ渡す - 切替時は
nextをsaveThemeへ渡す。リセット時はclearThemeを呼び、applyThemeでライトモードに戻す。HTML と CSS は編集しない