日向夏特殊応援部隊

俺様向けメモ

Ex DOM Storage をリリースしました

結構前に作っていたんだけど、IE6, IE7 でも動作する DOM Storage を作ったので、きちんと告知します。

追記

  • ちなみにサーバーにファイル置くだけで動きます。ユーザーに何かインストールさせる必要はありません。(2008-09-24T11:45:56+09:00)
  • CodeReposにソースを移動しました。(2008-09-24T12:37:24+09:00)

Ex DOM Storage

dist
http://svn.coderepos.org/share/lang/javascript/exdomstorage/tags/0.01/
source
http://svn.coderepos.org/share/lang/javascript/exdomstorage
sample
http://svn.coderepos.org/share/lang/javascript/exdomstorage/trunk/sample/index.html (Fx2, 3 でも動くようにしました。まだちょっとサンプルにバグあるっぽぃ)

ソースはいずれ CodeRepos に移動しようかなと思います。

DOM Storage ってなんだよ

HTML5 で仕様化されているクライアントサイドストレージに関する仕様が DOM Storage です。

具体的には、

  • localStorage
  • sessionStorage

と言う二つのストレージが存在して、共にドメイン単位でクライアントサイドにデータを保存出来ますが、

localStorage
永続的に保存
sessionStorage
ブラウザを閉じると消える

と言う違いがあります。

使い方は簡単で、

localStorage.foo = "zigorou";

のように代入しておくと、そのドメイン単位で foo に代入した "test" と言う値は保存され、ドメイン単位で共有され、以降は明示的に消さない限りは、localStorage.foo と言う参照で "test" と言う値を取得する事が出来ます。

現在これを実装しているブラウザは IE8 と、部分的に Fx2, Fx3 *1 となっています。

それと Safari の trunk でも使えるそうです。(id:amachang 談)

使い方

サンプル を見て頂くのが一番早いのですが、詳しく解説しておきます。

Content-Type を設定する

Ex DOM Storage では IE 独自機能である DHTML Behavior を使っています。これについては別のエントリで解説しようと思いますが、配布ファイルにある exdomstorage.htc に正しい Content-Type を設定する必要があるので、例えば Apache であれば、httpd.conf などで、

AddType text/x-component .htc

と設定して置く必要が(おそらく)あります。

ファイルを置く

という二つのファイルを必ず同じディレクトに配置して下さい。

Ex DOM Storage を読み込む

IE6, 7 だけに読ませたいので、条件付きコメント を用いてロードします。

<!--[if lt IE 8]>
<script type="text/javascript" src="/path/to/exdomstorage.js"></script>
<![endif]-->

これで使えるようになるはずです。

特徴と制約に関して

onstorage イベント

document オブジェクトに対して fire されます。つまり、

localStorage.setItem("name", "ZIGOROu");

などをした際に、

document.attachEvent("onstorage", function(evt) {
  alert(evt.key + " was changed to " + evt.newValue + " from " + evt.oldValue);
});

としておく事で、onstorage イベントを捕捉する事が出来ます。

但し、これは制約ですが本来の DOM Storage は同一ドメインのページを開くウインドウが複数存在している場合、そのうちの一つで onstorage イベントが fire されると他のウインドウの document オブジェクトに対しても onstorage イベントが fire されますが、Ex DOM Storage ではされません。*2

remainingSpace プロパティ

Storage - Web APIs | MDN にありますが、IE8 には Storage オブジェクトに対して remainingSpace プロパティが存在します。

これは後どれくらい Storage に保存出来るかと言う目安(byte単位)になるのですが、Ex DOM Storage でも一応実装してます。

Ex DOM Storage のバックエンドは userData behavior なので、おおよそ 64Kbyte 保存出来るのに対して、IE8 での nativeDOM Storage は 5,000,000 byte (約5MB) 保存出来ます。

実は userData behavior ベースでももっと容量を増やせる事は分かっているんですが、速度重視にしたので現在の制約のままにしています。

swf ベースのクライアントサイドストレージとの違い

これは id:amachang に教えて貰ったんだけど、onload イベント後じゃないと swf にアクセス出来ないのに対して、Ex DOM Storage はこのライブラリを読み込んだ直後から使えるようになります。

余計な要素が増えます>< (追記:2008-09-24T11:45:56+09:00)

実は Storage オブジェクトの実態は script 要素になってるので、head要素内に二つ余計な script 要素が増えてしまいます。

と言う訳で

手軽に使えるようになってるんで、是非お試し下さい。またバグ報告等ございましたら、お近くの id:ZIGOROu までお声掛け下さい。

*1:http://developer.mozilla.org/Ja/DOM/Storage を参照の事。簡単に言えば古い仕様に則っていて、localStorage が実装されてない代わりに globalStorage が実装されている。globalStorage で localStorage は代替可能

*2:と言うか HTC と JS だけじゃ不可能

Inside Ex DOM Storage

Ex DOM Storage の中の実装ですが、結構苦労したので折角だから解説しちゃうぞ的なエントリです。

なので興味のある人以外にはだいぶニッチですw

Ex DOM Storage の技術的概観

簡潔に書いてしまうと以下の二点につきます。

  • データの格納先は userData behavior
  • Storage オブジェクトの実態は script 要素に適用した element behavior

userData behavior とは?

userData Behavior とは、IE が標準で用意しているクライアントサイドストレージです。

使い方は非常に簡単で、

element.addBehavior("#default#userData");
element.load("myStorage");
element.setAttribute("myData", "blah blah");
element.save("myStorage");

のようにすると、element の myData と言う属性に対してデータを永続的に保存出来ます。

userData behavior もドメイン単位での保存なので、保存先はこれを使っています。

Storage オブジェクトプロパティに対する代入と onstorage イベント

プロパティへの代入の検出

そもそも Storage オブジェクトには getItem(key), setItem(key, newValue) と言うメソッドがあり、こちらを律儀に叩いてくれるなら問題は無かったのですが、

sessionStorage.hoge = "fuga";

と言うような、ただの代入に対しても onstorage イベントが fire するのですが、素の JS ではこれを実現する手だてが無いです。

Object#watch() がかなり近いですが、IEJScript には無いし、watch はそもそも指定したプロパティへの代入を監視なので、任意のプロパティに対してはどうにもなりません。

そこで思いついたのが onpropertychange イベント。つまり Storage オブジェクトの実態を HTMLElement にしてしまえば補足出来るよねと。

具体的には、

element.attachEvent("onpropertychange", function(evt) {
  alert("key: " + evt.key);
  alert("newValue: " + element[key]);
});

のように取得出来る感じです。

Behavior で作ったカスタムイベントの制約

onstorage イベントですが、当初は Behavior でカスタムイベントを使ってました。つまり、htcで

<public:event id="storegeEvent" name="onstorage" />

のようにしておいて、

var evt = document.createEventObject();
// evt のプロパティとか設定
storageEvent.fire(evt);

な感じでやってたのですが、カスタムイベントってイベントバブリングしないし、この要素のonstorage属性に直接ハンドラを定義しないと捕捉出来ないと言う制約があります。*1

さらに追い打ちをかける話で、document.createEventObject() で作ったイベントの type プロパティの値が IE にとって未知の値 (例えば storage と言う値) だとやはり attachEvent 出来ないと言う罠。

これは悩んだ末に attachEvent を hack すると言う、多少 evil な手法を取ってます。この割り切りは互換性重視のためです。

HTC にもレンダリングモードがある

document.compatMode の値ですね。でこれが互換モードの場合だと onpropertychange イベントを扱えないので、htc の冒頭に DOCTYPE 宣言が付いてます。これ、地味だけど凄い重要。

sessionStorage の実装

ブラウザを閉じると消える と言う挙動を実現する為に、cookie を使ってます。これは userData ではどうにも出来ない事が判明したので苦肉の策です。

length, remainingSpace プロパティの実装

これは HTML Component の getter 設定を使ってます。つまり、htc で

<public:property name="length" get="getLength" />
<public:property name="remainingSpace" get="getRemainingSpace" />

と設定してあるように、プロパティの取得の時に指定したメソッドが走るようになってます。実態は length だったら、

function getLength() {
  var length = 0;
  for (var p in storage)
    length++;
  return length;
}

みたいになってます。

まとめ

ここまで読んでくれたあなたは、だいぶニッチです。

*1:つまりattachEventが使えない

StorageEvent の非互換性メモ

Fx3 で確認したけど、StorageEvent のプロパティに差異があるようで、sessionStorage 由来で onstorage イベントが fire した場合、IE8 だと

event.uri; // #session

で取得出来るのに対して、Fx3 だと、

event.domain; // #session

で取れるみたい。ただ HTML5 の spec 的には event.url が正解のようです。