はじめに
localStorageは基本的に同じドメイン内(厳密にはポート番号なども一致している必要がある)でしか有効ではありませんが、postMessage API(Web Messaging API)を使用することでクロスドメインでも共有することができます。
調べてみても古い記事が多かったり、サンプルのコードが無かったりだったので自分用の備忘録も兼ねてやり方をご紹介します。
構成
【ドメインA】
・localstorageのデータを持つ側。
・ドメインBからの要求に応じてlocalstorageの操作を行う。
【ドメインB】
・ドメインAに保存されているlocalstorageの値の参照や更新を要求する側。
(実際の動作確認はA側をGithub Pages、B側をローカルサーバーに置いた状態で行いました。)
ドメインA側
ドメインB側からのメッセージをトリガーにlocalstorageの操作を行っています。
(厳密にメッセージの中身を検証して処理を切り分けたりまではしていません。)
<body>
<script>
(function() {
var origin = 'http://B.com';
window.addEventListener('message', function(event) {
// 送信元が指定のオリジンと一致していれば処理を行う
if(event.origin === origin) {
var message = event.data;
// メッセージが'get'ならlocalstorageの値を返す
if(message === 'get') {
var storageData = localStorage.getItem('test');
event.source.postMessage(storageData, event.origin);
}
// getでなければメッセージを分割してlocalstorageに保存する
else {
var messageArray = message.split(',');
var key = messageArray[0];
var value = messageArray[1];
localStorage.setItem(key, value);
}
}
});
})();
</script>
</body>
ドメインB側
display:none
で非表示にしたiframe
でドメインA側のindex.htmlを読み込み、iframeObject.contentWindow
でWindowオブジェクトを取得して、それに対してpostMessage
でメッセージを送っています。
<body>
<iframe id="iframe" src="http://A.com/index.html" style="display: none;"></iframe>
<input type="text" name="text" id="text">
<button id="set">ストレージに値を入れる</button>
<button id="get">ストレージの値を取得する</button>
<script>
(function() {
var iframeWindow = document.querySelector('#iframe').contentWindow;
var $text = document.querySelector('#text');
var $setButton = document.querySelector('#set');
var $getButton = document.querySelector('#get');
var origin = 'http://A.com';
window.addEventListener('message', function(event) {
// 送信元が指定のオリジンと一致していれば処理を行う
if(event.origin === origin) {
alert(event.data);
}
});
$setButton.addEventListener('click', function() {
// テキストエリアに入力されている値を送信
iframeWindow.postMessage(`test,${$text.value}`, origin);
});
$getButton.addEventListener('click', function() {
// localstorageの値の取得を要求するメッセージ送信
iframeWindow.postMessage('get', origin);
});
})();
</script>
</body>
以上になります。
もう少しややこしいロジックが必要かと思っていましたが、postMessage APIのおかげで意外と楽にできました。