1. ホーム
  2. ジャバスクリプト

[解決済み】タブやウィンドウ間の通信について

2022-04-05 15:15:55

質問

ブラウザの複数のタブやウィンドウの間で通信する方法を探していました(同一ドメイン内。 CORS を、痕跡を残さずに行うことができます。いくつかの解決策がありました。

  1. を使用しています。 ウィンドウ オブジェクト
  2. ポストメッセージ
  3. クッキー
  4. ローカルストレージ

最初の方法は、おそらく最悪の解決策です。現在のウィンドウからウィンドウを開く必要があり、ウィンドウを開いている間だけ通信が可能です。もし、いずれかのウィンドウでページを再読み込みすると、ほとんどの場合、通信が途絶えます。

2つ目のpostMessageを使う方法は、おそらくクロスオリジン通信を可能にしますが、1つ目の方法と同じ問題が生じます。ウィンドウ・オブジェクトを維持する必要があるのです。

3番目の方法は、クッキーを使用して、ブラウザにいくつかのデータを保存します。これは、効果的に同じドメイン上のすべてのウィンドウにメッセージを送信するように見えますが、問題は、すべてのタブがすでに"メッセージ"を読んだかどうかをクリーンアップ前に知ることができないことです。定期的にクッキーを読み込むために、ある種のタイムアウトを実装しなければなりません。さらに、クッキーの最大長は4キロバイトという制限があります。

4つ目の解決策は、localStorageを使用することで、クッキーの制限を克服し、さらにイベントを使用してリッスンすることができるようになったようです。その使い方は、採用された回答に記載されています。

2018年現在、受け入れた回答はまだ機能しますが、モダンブラウザのための新しいソリューション、BroadcastChannelを使用する方法があります。BroadcastChannelを使用して、タブ間で簡単にメッセージを送信する方法を説明する簡単な例については、他の回答を参照してください。

どのように解決するのですか?

ブロードキャストチャンネルを使用するとよいでしょう。以下の他の回答を参照してください。それでもまだタブ間の通信にlocalstorageを使用したい場合は、この方法で行ってください。

あるタブが他のタブにメッセージを送信したときに通知を受けるには、単に 'storage' イベントをバインドする必要があります。すべてのタブで、次のようにしてください。

$(window).on('storage', message_receive);

機能 message_receive は、他のタブでlocalStorageの値を設定するたびに呼び出されます。イベントリスナーはlocalStorageに新しく設定されたデータも含むので、localStorageオブジェクト自体をパースする必要はありません。これは非常に便利な機能で、値を設定した直後にリセットすることで、痕跡を効果的に消すことができます。以下はメッセージングのための関数です。

// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
    localStorage.setItem('message',JSON.stringify(message));
    localStorage.removeItem('message');
}


// receive message
//
function message_receive(ev)
{
    if (ev.originalEvent.key!='message') return; // ignore other keys
    var message=JSON.parse(ev.originalEvent.newValue);
    if (!message) return; // ignore empty msg or msg reset

    // here you act on messages.
    // you can send objects like { 'command': 'doit', 'data': 'abcd' }
    if (message.command == 'doit') alert(message.data);

    // etc.
}

これで、タブが onstorage イベントにバインドされ、これら 2 つの関数が実装されると、他のタブにメッセージをブロードキャストして、たとえば、次のように呼び出すことができます。

message_broadcast({'command':'reset'})

全く同じメッセージを二度送信しても、伝搬されるのは一度だけなので、メッセージを繰り返す必要がある場合は、以下のように一意の識別子を追加してください。

message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})

また、メッセージをブロードキャストした現在のタブは実際にはメッセージを受信せず、同じドメイン上の他のタブまたはウィンドウのみがメッセージを受信することを覚えておいてください。

setItem()の後にremoveItem()を呼び出した直後に、ユーザーが別のウェブページをロードしたり、タブを閉じたりしたらどうなるのか、という質問があるかもしれません。私自身のテストでは、ブラウザは関数全体が終了するまでアンロードを保留にします。 message_broadcast() が終了します。非常に長い for() サイクルを入れてテストしてみましたが、やはりサイクルが終了するのを待ってから閉じました。もしユーザーがその間にタブを閉じてしまったら、ブラウザはメッセージをディスクに保存するのに十分な時間がないでしょうから、この方法は痕跡を残さずにメッセージを送信する安全な方法だと思います。