[HTML5] Web Storage よく使う検索条件を保存する

82件のシェア(ちょっぴり話題の記事)

こんにちは。たごまさゆきです。
HTML5特集として記事を書くことになりました。HTML5の仕様にもいろいろありますが、この記事ではWeb Storageについて書いてみたいと思います。

Web Storage

Web Storageは、ブラウザにデータを保存するための仕組みです。データはkey-valueペアで扱われ、JavaScriptで保存したり取り出したりできます。

よくCookieと比較されますが、それより保存できるデータ量がかなり大きく、毎回のリクエストでサーバーに自動送信されないなどの違いがあります。

Web Storageにはデータの保存期間が違う2種類のストレージがあり、用途に応じて使い分けができます。

  • Session Storageは、ブラウザのウインドウ(またはタブ)が閉じられると削除されます。
  • Local Storageは、保存したデータは削除しない限り残り続けます。データにアクセスできるのは同一オリジン(プロトコル://ドメイン名:ポート番号)のみです。オリジンごとに保存領域が用意されるということになります。

なおWeb Storageは、現在のメジャーなPC用ブラウザにはほぼ実装されています。IEも8から使用可能です。(参考

以上、Web Storageについてのごく簡単なまとめでした。

どういった使い道があるのか

何か使い道を考えてみます。

まず思いつくのはWebアプリケーションのオフライン動作の実現です。オフライン時に作業したデータをローカルに保持しておいて、オンラインになったときにサーバーに送信します。

それ以外だとサーバーに送信する必要のないようなデータ、たとえばWebアプリケーションの個別カスタマイズデータなどを保持しておくのもよさそうです。カスタマイズした画面レイアウトや設定情報などを保存しておき、あとからアクセスしたときに反映できるようにできれば便利です。

ただし、同じ端末を複数人で共有するような場合は、他の人のデータが漏れてこないようにデータの持ち方を工夫する必要があります。
また、最近のブラウザには開発者ツールが標準でついていることが多いので、ストレージの中身はわりと簡単にのぞけてしまいます。
便利な仕組みですが、保存する情報の内容には気を使うべきだと思います。

よく使う検索条件を保存する

業務アプリでは複数の検索条件を組み合わせて必要な情報を検索するという場面が多いですが、検索条件が多いと毎回条件を指定するのは大変です。またよく使う検索条件は覚えておきたくなりますね。

そういった機能は従来だとCookieで実現することが多かったと思いますが、ここではWeb Storageを利用し、お気に入りの検索条件を保存して後から使えるような仕組みを作ってみました。

仕様は以下の通りです。

  • 入力した検索条件の値を、名前をつけてお気に入りに保存できる。
  • お気に入りは複数保存できる。
  • お気に入りを選んで、検索条件を再現させることができる。
  • いらなくなったお気に入りは削除することができる。

動作の確認

画面はこんな感じです。なお今回はテーマから外れるので検索機能自体は実装していません。

お気に入り検索条件

適当に検索条件を入力し[条件をお気に入り登録]ボタンをクリックすると、名前の入力ダイアログを表示します。

お気に入り検索条件2 お気に入り検索条件3

名前を入力して[OK]ボタンをクリックすると、検索条件を保存します。

お気に入り検索条件4

お気に入りリストに、保存した「お気に入り1」が表示されます。

お気に入り検索条件5

開発者ツールでLocal Storageを除いてみると、確かに保存されているようです。

お気に入り検索条件6

別の検索条件を登録することもできます。

お気に入り検索条件7

一度ウインドウを閉じて、再度アクセスします。検索条件はクリアされてしまいましたが、お気に入りは残っています。
「お気に入り1」を選択して[お気に入りを使う]ボタンをクリックすると、保存していた検索条件が再現されました。

お気に入り検索条件8お気に入り検索条件9

いらなくなった検索条件は削除してしまいましょう。
「お気に入り1」を選択して[お気に入りを削除する]ボタンをクリックし確認ダイアログで[OK]すると、お気に入りリストから「お気に入り1」がなくなりました。

お気に入り検索条件10お気に入り検索条件11

ソースコードの解説

HTMLソースは以下のようになります。

<!DOCTYPE html>
<html lang='ja'>
	<head>
		<meta charset='UTF-8'>
		<title>お気に入り検索条件</title>
		<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
		<script type='text/javascript'  src='FavSearchCondition.js' charset='UTF-8'></script>
	</head>
	<body>
		<div id='searchCondition'>
			<h4>検索条件</h4>
			<form id='searchConditionForm'>
				<dl>
					<dt>会員ID</dt><dd><input name='memberId' id='memberId' type='text'/></dd>
					<dt>名前</dt><dd><input name='memberName' id='memberName' type='text'/></dd>
					<dt>名前(カナ)</dt><dd><input name='memberNameKana' id='memberNameKana' type='text'/></dd>
					<dt>性別</dt>
					<dd>
						<input name='gender' id='gender0' type='radio' value='0' checked><label for='gender0'>未選択</label>
						<input name='gender' id='gender1' type='radio' value='1'><label for='gender1'>男</label>
						<input name='gender' id='gender2' type='radio' value='2'><label for='gender2'>女</label>
					</dd>
					<dt>生年月日</dt><dd><input name='birthDay' id='birthDay' type='date'/></dd>
					<dt>電話番号</dt><dd><input name='phoneNumber' id='phoneNumber' type='tel'/></dd>
					<dt>会員種別</dt>
					<dd>
						<select name='memberType' id='memberType' >
							<option value=''>*未選択*</option>
							<option value='1'>ライト会員</option>
							<option value='2'>スタンダード会員</option>
							<option value='3'>プレミアム会員</option>
						</select>
					</dd>
				</dl>
				<input id='searchBtn' type='submit' value='検索実行'/><br/>
				<input id='clearBtn' type='reset' value='条件をクリア'/>
				<input id='favoriteBtn' type='button' value='条件をお気に入り登録'/>
			</form>
		</div>
		<hr/>
		<div id='favorite'>
			<h4>お気に入り検索条件</h4>
			<form id='favoriteForm'>
				<select name='favoriteList' id='favoriteList'  size=10>
				</select>
			</form>
			<input id='useBtn' type='button' value='お気に入りを使う'/>
			<input id='deleteBtn' type='button' value='お気に入りを削除する'/>
		</div>
	</body>
</html>

JavaScriptソースは以下のようになります。

// お気に入りデータを保持する変数
var favoriteList = {};

/**
 * 検索条件を名前を付けてお気に入りに登録する。(条件をお気に入り登録ボタン)
 */
function registerFavorite() {
	var name = prompt('お気に入りの名前を入力してください。','お気に入り');
	if (name && name != '') {
		var condition = getCondition();

		favoriteList[name] = condition;
		savaConditionList();
		setFavoriteList();
		alert('お気に入りを保存しました。:' + name);
	}
}

/**
 * 検索条件フォームから値を取得する。
 */
function getCondition() {
	var condition = {};

	condition.memberId = $('#memberId').val();
	condition.memberName = $('#memberName').val();
	condition.memberNameKana = $('#memberNameKana').val();
	condition.gender = $('#searchConditionForm input[name="gender"]:radio:checked').val();
	condition.birthDay = $('#birthDay').val();
	condition.phoneNumber = $('#phoneNumber').val();
	condition.memberType = $('#memberType').val();

	return condition;
}

/**
 * 検索条件フォームに値を設定する。(お気に入りを使うボタン)
 */
function setCondition() {
	var favName = $('#favoriteList').val();
	if(favName) {
		var condition = favoriteList[favName];
		if(condition) {
			$('#memberId').val(condition.memberId);
			$('#memberName').val(condition.memberName);
			$('#memberNameKana').val(condition.memberNameKana);
			$('#searchConditionForm input[name="gender"]').val([condition.gender]);
			$('#birthDay').val(condition.birthDay);
			$('#phoneNumber').val(condition.phoneNumber);
			$('#memberType').val(condition.memberType);
		}
	}
}

/**
 * お気に入りリストから指定した名前の検索条件を削除する。(お気に入りを削除するボタン)
 */
function removeFavorite() {
	var favName = $('#favoriteList').val();
	if(favName) {
		if(confirm('お気に入りを削除します。:' + favName)) {
			delete favoriteList[favName];
			setFavoriteList();

			if(existFavorite()) {
				savaConditionList();
			} else {
				localStorage.removeItem('favConditionList');
			}
		}
	}
}

/**
 * お気に入りがあるか調べる。
 */
function existFavorite() {
	var favExist = false;
	for (var name in favoriteList) {
		favExist = true;
		break;
	}
	return favExist;
}

/**
 * お気に入りリストをローカルストレージに保存する。
 */
function savaConditionList() {
	localStorage.setItem('favConditionList', JSON.stringify(favoriteList));
}

/**
 * お気に入りリストをセットする。
 */
function setFavoriteList() {
	$('#favoriteList').empty();
	for (var name in favoriteList) {
		$('#favoriteList').append('<option value="' + name + '">' + name + '</option>');
	}
}

$(function() {
	// ローカルストレージの利用可否確認
	if(!localStorage) {
		alert('ローカルストレージが使えません。');
	}

	// ローカルストレージからお気に入りリストを取り出し
	var favoriteStr = localStorage.getItem('favConditionList');
	if(favoriteStr){
		favoriteList = JSON.parse(favoriteStr);
		setFavoriteList();
	}

	// イベントハンドラ追加
	$('#favoriteBtn').bind('click', registerFavorite);
	$('#useBtn').bind('click', setCondition);
	$('#deleteBtn').bind('click', removeFavorite);
});

簡単なソースですが、少し解説します。
一度ブラウザを閉じて再度アクセスした場合でも保存したお気に入りは残しておきたいので、データの保存先としてLocal Storageを選択しました。

Webアプリにアクセスすると103行目からの関数を実行します。
まず、105行のif文でLocal Storageが使えるかどうかを確認しています。前述の通り最新のブラウザではLocal Storageはほぼ使用可能ですので、ここは問題なく通過します。

110行目で、favConditionListというキー名で保存してあるお気に入りデータを、Local Storageから取り出しています。お気に入りデータは以下の形式で保存しています。

{
	"お気に入り名":{
		"memberId":value,
		"memberName":value,
		"memberNameKana":value,
		"gender":value,
		"birthDay":value,
		"phoneNumber":value,
		"memberType":value
	},
	...
}

データが取得できたらオブジェクトに変換し変数favoriteListに保持します。
また、113行目のsetFavoriteList()でお気に入りリストにお気に入り名を追加します。

117-119行目で各ボタンにイベントハンドラを設定します。

[条件をお気に入り登録]ボタンをクリックすると、7行目のregisterFavorite()を実行します。
現在検索条件フォームに入力されている値を取得し、ダイアログで入力したお気に入り名をキーにしてfavoriteListに保持します。
13行目で呼び出しているsavaConditionList()の中でfavoriteListをLocal Storageに保存しています。Local Storageは文字列のみ保存できるため、そのとき前述のJSON形式に変換しています(90行目)。

[お気に入りを使う]ボタンをクリックすると、39行目のsetCondition()を実行します。favoriteListから選択した名前のお気に入りデータを取得し、検索結果フォームに設定します。

[お気に入りを削除する]ボタンをクリックすると、58行目のremoveFavorite()を実行します。
選択した名前で保持されているお気に入りを削除し(62行目)、続いてLocal Storageに保持している値を更新します。
お気に入りが他にも保存されているならLocal Storageの内容を更新します(66行目)。もしお気に入り一つもがなくなっているなら、Local Storageからお気に入りデータを削除しています(68行目)。

今回のサンプルでは、Local Storageに対するデータの保存(setItem())、取得(getItem())、削除(removeItem())を実行しました。その他にもclear()など、いくつか使用できるメソッドがあります。
今回はLocal Storageを使いましたが、Session Storageでも同じメソッドが使用できます。

storageイベント

Local Storage(またはSession Storage)に対して更新を行うと、windowオブジェクトからstorageイベントが発生します。eventオブジェクトからは更新した内容に関する値が取得できるので、イベントを起点に何らかの処理を実行させる事ができます(この記事では詳細は割愛します)。

おわりに

今回はLocal Storageを使った機能を実装してみました。
Web Storageは、シンプルが故に工夫次第でいろいろな用途に使えそうです。
この記事が、少しでも皆さんのお役にたてれば幸いです。