ページを閉じても設定や書きかけの内容を覚えておきたいとき、ブラウザの中にデータを残せます。方法は 4 つあり、向いている使いどころが違います。
| 方法 | 残る期間 | サーバへ | 書き方 | 使いどころ |
|---|---|---|---|---|
| cookie | 期限まで | 毎回送られる | document.cookie | サーバが知る必要のある情報 |
| localStorage | 消すまで | 送られない | localStorage.setItem | 設定 ・ 小さなデータ |
| sessionStorage | タブを閉じるまで | 送られない | sessionStorage.setItem | そのタブだけの一時的な状態 |
| IndexedDB | 消すまで | 送られない | 非同期の API(ライブラリ経由が多い) | 大量のデータ ・ オフライン |
注意: どの方法も、ブラウザを使う人が開発者ツールで中身を見たり書き換えたりできます。パスワードのような秘密は残しません。また、同じオリジン(18 章)のページは、すべて同じ置き場を共有します。
メモ: 昔は、ブラウザの中で SQL を使える Web SQL Database という仕組みもありましたが、標準になる前に打ち切られ、いまのブラウザからは取り除かれています。古い資料で見かけても使いません。
どちらも、キーと値の組を文字列で保存します。書き方は同じで、残る期間だけが違います。
| 書き方 | すること |
|---|---|
setItem(キー, 値) | 保存する。値は文字列に変わる |
getItem(キー) | 読む。無ければ null |
removeItem(キー) | 1 つ消す |
スクリプト
localStorage.removeItem("jsl-visits"); // 例を何度動かしても同じ結果になるように、まず消す
const before = Number(localStorage.getItem("jsl-visits") ?? 0);
localStorage.setItem("jsl-visits", before + 1);
console.log(localStorage.getItem("jsl-visits"));
console.log(typeof localStorage.getItem("jsl-visits"));
const settings = { theme: "dark", fontSize: 16 };
localStorage.setItem("jsl-settings", JSON.stringify(settings));
const loaded = JSON.parse(localStorage.getItem("jsl-settings"));
console.log(loaded.theme, loaded.fontSize);
console.log(localStorage.getItem("jsl-nothing"));
1
string
dark 16
null
Number()(04 章)JSON.stringify で文字列にして保存し、JSON.parse で戻す(05 章)次の例は、開いた回数を数えます。「▶ 実行」を何度か押してみてください。ページを開き直しても、数は続きから増えます。
body の中身
<p id="count"></p>
<button id="reset">数え直す</button>
スクリプト
const KEY = "jsl-open-count";
const count = Number(localStorage.getItem(KEY) ?? 0) + 1;
localStorage.setItem(KEY, count);
document.querySelector("#count").textContent = `この画面を開いた回数: ${count}`;
document.querySelector("#reset").addEventListener("click", () => {
localStorage.removeItem(KEY);
document.querySelector("#count").textContent = "数え直します。もう一度「▶ 実行」を押してください";
});
sessionStorage は、タブを閉じると消えます。同じページを 2 つのタブで開くと、タブごとに別々です。
スクリプト
sessionStorage.setItem("jsl-draft", "書きかけの文章");
console.log(sessionStorage.getItem("jsl-draft"));
sessionStorage.removeItem("jsl-draft");
console.log(sessionStorage.getItem("jsl-draft"));
書きかけの文章
null
cookie は、document.cookie に「名前=値; 設定」の形の文字列を代入して保存します。読むと、そのページで使えるすべての cookie が「名前=値; 名前=値」とつながった 1 つの文字列で返ります。
スクリプト
document.cookie = "jsl_theme=dark; max-age=60; path=/";
console.log(document.cookie.includes("jsl_theme=dark"));
document.cookie = "jsl_theme=; max-age=0; path=/"; // 期限を 0 にすると消える
console.log(document.cookie.includes("jsl_theme=dark"));
true
false
cookie はリクエストのたびにサーバへ送られるので、ブラウザの中だけで使うデータには向きません。ブラウザだけで使うなら localStorage を使います。ログイン状態のように、サーバが決めてサーバが読む情報は、サーバ側が cookie を設定するのがふつうです。
IndexedDB は、ブラウザの中のデータベースです。オブジェクトをそのまま大量に保存でき、キーで検索できます。ただし、API が古い形の非同期(イベントで結果を受け取る)で書きにくいため、小さなライブラリを通して使うのがふつうです。この資料では名前と使いどころだけを紹介します。
時刻は Date で扱います。表示の形は toLocaleString や Intl.DateTimeFormat で決めます。timeZone を指定すると、見ている人のパソコンの設定に関係なく、日本時間で表示できます。
const d = new Date("2026-10-10T09:30:05+09:00");
console.log(d.toLocaleString("ja-JP", { timeZone: "Asia/Tokyo" }));
const fmt = new Intl.DateTimeFormat("ja-JP", {
timeZone: "Asia/Tokyo",
year: "numeric",
month: "long",
day: "numeric",
weekday: "short",
});
console.log(fmt.format(d));
const later = new Date(d.getTime() + 90 * 60 * 1000); // 90 分後
console.log(later.toLocaleTimeString("ja-JP", { timeZone: "Asia/Tokyo" }));
2026/10/10 9:30:05
2026年10月10日(土)
11:00:05
11 章のタイマーを画面に使うと、時計が作れます。「動かす」を押すと 1 秒ごとに表示が変わり、「止める」で止まります。
body の中身
<p id="clock" style="font-size: 32px; font-weight: bold;">--:--:--</p>
<button id="start">動かす</button>
<button id="stop">止める</button>
スクリプト
const clock = document.querySelector("#clock");
const format = (d) => d.toLocaleTimeString("ja-JP", { timeZone: "Asia/Tokyo" });
let timerId = null;
document.querySelector("#start").addEventListener("click", () => {
if (timerId !== null) return;
clock.textContent = format(new Date());
timerId = setInterval(() => {
clock.textContent = format(new Date());
}, 1000);
});
document.querySelector("#stop").addEventListener("click", () => {
clearInterval(timerId);
timerId = null;
});
console.log(format(new Date("2026-10-10T09:30:05+09:00")));
9:30:05
timerId を覚えておくのは、止めるときに必要だからです。「動かす」を 2 回押してもタイマーが 2 つにならないよう、動いているときは何もしません。
new URL(文字列) で、URL を部品に分けて読み書きできます。? の後ろの検索条件は searchParams で扱います。文字列をつなげて組み立てるより、記号の書き間違いや、日本語のエンコードの漏れを防げます。
const url = new URL("https://example.com/search?q=JavaScript&page=2#top");
console.log(url.hostname, url.pathname, url.hash);
console.log(url.searchParams.get("q"), Number(url.searchParams.get("page")));
url.searchParams.set("page", "3");
url.searchParams.set("tag", "入門");
console.log(url.href);
example.com /search #top
JavaScript 2
https://example.com/search?q=JavaScript&page=3&tag=%E5%85%A5%E9%96%80#top
いま開いているページの URL は location.href で読めます。ページを移動せずに URL だけを変える history.pushState という仕組みもあり、1 つのページの中で画面を切り替えるアプリで使われます。
navigator.clipboard.writeText(文字) で、文字をクリップボードにコピーできます。悪用を防ぐため、ボタンを押すなど、人が操作したときにしか使えません。結果は Promise で返ります。
body の中身
<input id="text" value="コピーする文字" size="20">
<button id="copy">コピー</button>
<span id="done"></span>
スクリプト
document.querySelector("#copy").addEventListener("click", async () => {
const text = document.querySelector("#text").value;
try {
await navigator.clipboard.writeText(text);
document.querySelector("#done").textContent = "コピーしました";
} catch (e) {
document.querySelector("#done").textContent = `コピーできませんでした(${e.name})`;
}
});
この例は、画面の「コピー」を押したときだけ動きます。押した後、メモ帳などに貼り付けて確かめてください。