【STEP.36】
ストレージ・クッキーの操作方法をマスターしよう!

ストレージとは

Web Storage(以下ストレージ)とは、ブラウザー標準で備わったデータストアの一種で、キー(名前)/値の組み合わせでデータを管理できます。

従来、似たようなデータストアとしてクッキー(Cookie)がありますが、双方には次に掲げる表のような違いがあります。

まずは用途としてそれで事足りるのであれば、制限の緩いストレージを優先して利用すべきです。

【Web Storageとクッキーの違い】
ストレージクッキー
データサイズの上限大(5MB)小(4KB)
データの有効期限なしあり
通信発生しないリクエスト都度、サーバーに送信

ストレージにデータを保存し、保存したデータを取得する

以下に、ストレージにデータを出し入れする例を示します。

<div>greet1 : <span id="result1"></span></div>
<div>greet2 : <span id="result2"></span></div>
<div>greet3 : <span id="result3"></span></div>
let result1 = document.getElementById('result1');
let result2 = document.getElementById('result2');
let result3 = document.getElementById('result3');
	
// ローカルストレージを取得
let st = localStorage;
// ストレージに値を保存
st.setItem('greet1','おはよう');
st.greet2 = 'こんにちは';
st['greet3'] = 'こんばんは';
// ストレージから値を取得
result1.textContent = st.getItem('greet1');
result2.textContent = st.greet2;
result3.textContent = st['greet3'];

ストレージには、localStorage、またはsessionStorageプロパティでアクセスできます。(let st = localStorage;

両者の違いは、データの有効期限の違いです。

ローカルストレージ(localStorage)は、オリジン単位でデータを管理します。

ブラウザーを閉じてもデータが消えることはありませんし、異なるウィンドウ/タブからも同じデータを共有できます。

一方、セッションストレージ(sessionStorage)は、ブラウザーが起動している間だけ有効です。

異なるタブ/ウィンドウの間でも共有できません。

MEMO
オリジンとは
「https://yutamanshop.com:81」のように、「プロトコル://ホスト:ポート番号」の組み合わせのことを、オリジンといいます。

ストレージは、オリジンの単位で独立してデータを管理しますので、現在のホストで保存したデータを異なるホストから読み込むことはできません。

ローカルストレージ/セッションストレージは、取得するためのプロパティが異なるだけで、以降、データを出し入れする手段は共通です。

そのため、「let st = localStorage;」のように、最初から変数に退避させておくことをお勧めします。

これによって、あとからストレージを切り替えたいという場合にも、「let st = localStorage;」だけを書き換えれば済むからです。

【ストレージにアクセスする手段】
記法構文
プロパティ構文
storage.名前
ハッシュ構文
storage['名前']
メソッド構文
storage.getItem('名前')
storage.setItem('名前')

あとは、データの出し入れは、上のJavaScriptのコード7~14行目のように表せます。

一般的にはプロパティ構文を利用します。

ただし、名前に識別子で利用できない文字(たとえば「-」など)が含まれている場合にはハッシュ構文、またはメソッド構文を利用してください。

ストレージの内容を確認する方法

Chromeであれば、メニューバーの(Google Chromeの設定)ボタンから[その他のツール][デベロッパーツール]を起動します。

その[Application]タブを開き、左ツリーから[Local Storage](または[Session Storage])[http://<IPアドレス、またはlocalhost>]を選択してください。

ストレージの内容が表示され、グリッドからデータを編集/削除できます。

ストレージにオブジェクトを出し入れする

現在のストレージに保存できる型は、文字列が基本です。(仕様のうえではオブジェクトも保存できることになっていますが、現時点でこれに対応するブラウザーはありません。)

オブジェクト保存してもエラーにはなませんが、内部的にはtoStringメソッドで文字列化したものを記録するだけなので、オブジェクトとして復元するとこはできません。(一般的には「[objectObject]」のような文字列です。)

そこで、オブジェクトをストレージに保存する際には、以下のようなコードで「復元可能な文字列」に変換する必要があります。

<div>氏名:<span id="result1"></span></div>
<div>年齢:<span id="result2"></span></div>
<div>職業:<span id="result3"></span></div>
let result1 = document.getElementById('result1');
let result2 = document.getElementById('result2');
let result3 = document.getElementById('result3');
	
let st = localStorage;
// オブジェクトをJSON文字列に変換&ストレージに保存
let info = {name:'横浜流星', age:23, occupation:'俳優'};
st.setItem('data',JSON.stringify(info));
	
// ストレージから取得した文字列をオブジェクトとして解析
let parsed = JSON.parse(st.getItem('data'));
result1.textContent = parsed.name;
result2.textContent = parsed.age;
result3.textContent = parsed.occupation;

「復元可能な文字列」に変換するのはJSON.stringifyメソッドの役割です。(st.setItem('data',JSON.stringify(info));

JSON形式に変換された文字列は、JSON.parseメソッドでオブジェクトとして復元できます。(let parsed = JSON.parse(st.getItem('data'));

データをアプリ単位にまとめる

ローカルストレージはオリジン単位でデータを管理します。

その性質上、1つのオリジンで複数のアプリが稼働している場合、名前衝突の危険もおのずと高まります。

これを避けるために、一般的には、1つのアプリで利用するデータは1つのオブジェクトに束ねてしまうことをお勧めします。

ストレージの内容をすべて参照する

lengthkeyプロパティを利用します。

lengthプロパティでストレージ配下のデータの個数、keyプロパティでn番目のキーを得られます。

以下のサンプルでは、これらのプロパティを利用して、データの個数分だけforループを繰り返し、それぞれのキーと対応する値を列挙しています。

<form>
  <div>
    <label for="name">名前:</label>
    <input id="name" type="text" value="" />
  </div>
  <div>
    <input id="remove" type="button" value="削除" />
    <input id="clear" type="button" value="全削除" />
  </div>
</form>
<table id="result"></table>
let st = localStorage;
	
// ストレージの内容を表示するshow関数
let show = function () {
  let frag = document.createDocumentFragment();
  // データの個数だけループを繰り返し
  for (let i = 0; i < st.length; i++) {
    let k = st.key(i);
    let tr = document.createElement('tr');
    let th = document.createElement('th');
    th.textContent = k;
    let td = document.createElement('td');
    td.textContent = st.getItem(k);
    tr.appendChild(th);
    tr.appendChild(td);
    frag.appendChild(tr);
  }
  let result = document.getElementById('result');
  result.textContent = null;
  result.appendChild(frag);
}
show();
	
// [削除]ボタンで指定されたキーだけを削除
document.getElementById('remove').addEventListener('click', function () {
  st.removeItem(document.getElementById('name').value);
  show();
}, false);
	
// [全削除]ボタンでストレージを完全にクリア
document.getElementById('clear').addEventListener('click', function () {
  st.clear();
  show();
}, false);

ストレージの内容を削除する

ストレージから既存のデータを削除するには、removeItemメソッドを利用します。

すべてのデータをまとめてクリアするならば、clearメソッドを利用してください。

<form>
  <div>
    <label for="name">名前:</label>
    <input id="name" type="text" value="" />
  </div>
  <div>
    <input id="remove" type="button" value="削除" />
    <input id="clear" type="button" value="全削除" />
</div>
</form>
<table id="result"></table>
let st = localStorage;

// ストレージの内容を表示するshow関数
let show = function () {
  let frag = document.createDocumentFragment();
  // データの個数だけループを繰り返し
  for (let i = 0; i < st.length; i++) {
    let k = st.key(i);
    let tr = document.createElement('tr'); 
    let th = document.createElement('th');
    th.textContent = k;
    let td = document.createElement('td');
    td.textContent = st.getItem(k);
    tr.appendChild(th);
    tr.appendChild(td);
    frag.appendChild(tr);
  }
  let result = document.getElementById('result');
  result.textContent = null;
  result.appendChild(frag);
};
show();
	
// [削除]ボタンで指定されたキーだけを削除
document.getElementById('remove').addEventListener('click', function () {
  st.removeItem(document.getElementById('name').value);
  show();
}, false);
	
// [全削除]ボタンでストレージを完全にクリア
document.getElementById('clear').addEventListener('click', function () {
  st.clear();
  show();
}, false);

ただし、ストレージではオリジン単位にデータを管理します。

1つのオリジンで複数のアプリを運用している場合には、誤って他のアプリで利用しているデータまで破棄してしまわないように注意してください。

ストレージの変更を検知する

storageイベントを利用することで、別ウィンドウ/タブで発生したストレージの変更を検知できます。

たとえば以下は、ストレージへの変更内容をログに出力する例です。

一般的には、変更を受けて、表示中のコンテンツを更新することになるでしょう。

<div>greet1 : <span id="result1"></span></div>
<div>greet2 : <span id="result2"></span></div>
<div>greet3 : <span id="result3"></span></div>
window.addEventListener('storage', function (e) {
  console.log('変更されたキー:' + e.key);
  console.log('元のキー:' + e.oldValue);
  console.log('新しい値:' + e.newValue);
  console.log('発生元のURL:' + e.url);
}, false);

storageイベントリスナーの配下では、イベントオブジェクトを介して、ストレージの変更情報にアクセスできます。

【イベントオブジェクトで取得できるストレージ関連の情報】
プロパティ概要
key変更されたキー
newValue新しい値
oldValue元の値
storageArea影響を受けたStorage(localStorage/sessionStorage)
url変更の発生元となるページURL

サンプルを試す際には、上のstorage_event.htmlを起動した状態で、別ウィンドウから(たとえば)以下のstorage.htmlなどのサンプルにアクセスしてください。

<div>greet1 : <span id="result1"></span></div>
<div>greet2 : <span id="result2"></span></div>
<div>greet3 : <span id="result3"></span></div>
let result1 = document.getElementById('result1');
let result2 = document.getElementById('result2');
let result3 = document.getElementById('result3');

// ローカルストレージを取得
let st = localStorage;

// ストレージに値を保存
st.setItem('greet1','おはよう');
st.greet2 = 'こんにちは';
st['greet3'] = 'こんばんは';

// ストレージから値を取得
result1.textContent = st.getItem('greet1');
result2.textContent = st.greet2;
result3.textContent = st['greet3'];

クッキーとは

JavaScriptの世界では、原則として、スクリプトからコンピューターに勝手に書き込むことは許されていません。

ユーザーがサイトにアクセスした途端に、コンピューター内のファイルを書き換えられてしまったら大変ですから、これは当然のことです。

その例外となる手段として、ブラウザーでは初期の頃から「クッキー(Cookie)」と呼ばれる仕組みを提供しています。

Webアプリでは、クッキーを利用することで、クライアントに対して、小さなテキストを保存できるようになります。

もっとも、このクッキーは、JavaScriptからは操作しにくく、サイズも制限されます。

そのため現在では、その代替手段として、上記で解説したWeb Storage(ストレージ)を利用するのがお勧めです。

クッキーを設定する

document.cookieプロパティを利用します。

ただし、設定値は、

クッキー名= 値; expires= 有効期限; domain= ドメイン名; path= パス; secure

のように、少し複雑です。(クッキー名と値だけが必須で、あとは任意です。)

セミコロン区切りの「パラメーター名=値」の組みで、必要な情報を記述します。

それぞれのパラメーターの意味は、次に掲げる表の通りです。

【クッキー文字列を構成するパラメーター】
パラメーター概要
クッキー名クッキーを識別する名前とその値。英数字以外の文字列を含む場合はencodeURIComponent関数 であらかじめエンコードしておくこと
max-ageクッキーの有効期限(秒数)
expiresクッキーの有効期限(協定世界時)。max-age/expires共に省略時は、ブラウザーを閉じたときにクッキーを破棄
domain有効なドメイン。指定ドメインのページのみクッキーを利用可能(省略時は現在のホスト)
path有効なパス。指定パスの配下でのみクッキーが利用可能(省略時は現在のパス)
securehttps通信でのみクッキーを送信

これらの文字列をその都度組み立てるのは面倒なので、クッキー値を設定するためのsetCookie関数を準備しておきます。

引数は、先頭から「クッキー名」「値」「その他のオプション(名前: 値)のハッシュ値」を受け取れるものとします。

function setCookie(name, value, opts) {
  let cook = '';
  // [名前=値]を追加(値はあらかじめエンコード処理)
  cook += name + '=' + encodeURIComponent(value);
  // 引数expiresが空でない場合、expires日後に有効期限を設定
  if (opts.expires) {
    let exp = new Date();
    exp.setDate(exp.getDate() + opts.expires);
    cook += ';expires=' + exp.toUTCString();
  }
  // その他のオプションも空でない場合は設定
  if (opts.maxAge) { cook += '; max-age=' + opts.maxAge; }
  if (opts.domain) { cook += '; domain=' + opts.domain; }
  if (opts.path) { cook += '; path=' + opts.path; }
  if (opts.secure) { cook += '; secure'; }
  // 組み立てたクッキー文字列を設定
  document.cookie = cook;
}

// クッキーAuthorを設定(有効期限は90日)
setCookie('Author', '横浜流星', {expires: 90});

上に掲げる表でも示しているように、expiresパラメーターは協定世界時で指定します。

ソースコードの6~10行目では、引数opts.expiresをもとに、opts.expires日後の日付を生成し、これをtoUTCStringメソッドで協定世界時に変換しています。

クッキーを取得する

document.cookieプロパティで、クッキーを取得できます。

ただし、その戻り値は値そのものではなく、「パラメーター=値」の組みです。

Author=%E6%A8%AA%E6%B5%9C%E6%B5%81%E6%98%9F;expires=Wed, 13 May 2020 12:56:35 GMT

これをその都度処理するのは面倒なので、getCookie関数を用意しておきます。

function getCookie (name) {
  let value = null;
  // 取得したクッキー文字列を「;」で分割
  let cookies = document.cookie.split(';');
  cookies.forEach(function (c) {
  // 「名前=値」を「=」で分割
  let kv = c.split('=');
  if (kv[0] === name) {
    value = decodeURIComponent(kv[1]);
  }
});
return value;
}
console.log(getCookie('Author'));
// 結果:横浜流星

クッキーの内容を確認する方法

Google Chromeであれば、メニューバーのgoogle chrome設定ボタン(Google Chromeの設定)ボタンから[その他のツール][デベロッパーツール]を起動します。

その[Application]タブを開き、左ツリーから[Cookies][http://<IPアドレス、またはlocalhost>]を選択してください。

クッキーの内容が表示され、グリッドからデータを編集/削除できます。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です