タブまたはウィンドウ間の通信


176

トレースを残さずにブラウザー(CORSではなく同じドメイン上)の複数のタブまたはウィンドウ間で通信する方法を探していました。いくつかの解決策がありました:

  1. ウィンドウオブジェクトの使用
  2. postMessage
  3. クッキー
  4. ローカルストレージ

1つ目はおそらく最悪のソリューションです。現在のウィンドウからウィンドウを開く必要があり、ウィンドウを開いたままにしておく限り通信できます。いずれかのウィンドウでページをリロードすると、おそらく通信が失われます。

postMessageを使用する2番目のアプローチは、おそらくクロスオリジン通信を可能にしますが、最初のアプローチと同じ問題が発生します。ウィンドウオブジェクトを維持する必要があります。

3番目の方法では、Cookieを使用して、一部のデータをブラウザーに保存します。これは、実質的に同じドメインのすべてのウィンドウにメッセージを送信するように見えますが、問題は、すべてのタブが「メッセージ」を以前に読んだかどうかを確認できないことです。清掃。Cookieを定期的に読み取るには、なんらかのタイムアウトを実装する必要があります。さらに、Cookieの最大長(4KB)によって制限されます。

localStorageを使用する4番目のソリューションは、Cookieの制限を克服するようであり、イベントを使用してリッスンすることもできます。利用方法は、回答欄に記載されています。

2018年の編集:承認された回答は引き続き機能しますが、BroadcastChannelを使用するための最新のブラウザーの新しいソリューションがあります。BroadcastChannelを使用してタブ間でメッセージを簡単に送信する方法を説明する簡単な例については、他の回答を参照してください。



クライアント側のデータストレージを管理するために、localStorageとsessionStorageを介してライブラリを作成しました。storageManager.savePermanentData( 'data'、 'key');のようなことができます。またはstorageManager.saveSyncedSessionData( 'data'、 'key'); データの動作方法に基づいています。それは本当にプロセスを簡素化します。ここに記事全文:ebenmonney.com/blog/…– adentum '14
12/16


2
私は数年前にライブラリsysend.jsを作成しましたが、最新バージョンではBroadcastChannelを使用しています。このページをjcubic.pl/sysend.phpで2回開くことにより、ライブラリをテストできます。iframeプロキシを提供する場合は、異なるオリジンでも動作します。
jcubic

サブドメインを同じオリジンと見なしますか?つまり、私は3つ以下のドメインを持っていますが、broadcastchannel apiを介して通信しますか?alpha.firstdomain.com、beta.firstdomain.com、gama.firstdomain.com
Tejas Patel

回答:


142

2018を編集:この目的のためにBroadcastChannelを使用することをお勧めします。以下の他の回答を参照してください。それでも、タブ間の通信にlocalstorageを使用したい場合は、次のようにします。

タブが他のタブにメッセージを送信したときに通知を受けるには、単に「ストレージ」イベントにバインドする必要があります。すべてのタブで、次の操作を行います。

$(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'})

まったく同じメッセージを2回送信すると1回だけ伝播されるため、メッセージを繰り返す必要がある場合は、次のように一意の識別子を追加してください。

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

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

removeItem()の前のsetItem()呼び出しの直後にユーザーが別のWebページをロードしたり、タブを閉じたりした場合にどうなるかを尋ねることがあります。まあ、私自身のテストから、ブラウザは関数全体message_broadcast()が終了するまでアンロードを保留します。私はいくつかの非常に長いfor()サイクルを入れるようにテストしましたが、それでも、閉じる前にサイクルが完了するのを待っていました。ユーザーがその間のタブを強制終了すると、ブラウザーにはメッセージをディスクに保存する十分な時間がなくなるため、このアプローチは、トレースなしでメッセージを送信する安全な方法のように思えます。コメントを歓迎します。


1
JSON.parse()を呼び出す前に削除イベントを無視できますか?
dandavis、2015年

1
既存のlocalStorageデータを含む、イベントデータの制限に注意してください。出荷するのではなく、メッセージングのためだけにストレージイベントを使用する方が安全です。郵便局で郵便局で小包を受け取るように言われたときのように...また、localstorageはハードドライブに移動するため、不注意なキャッシュを残してログに影響を与える可能性があります。これは、実際のデータ。
dandavis 2015年

1
私はしばらく前に関連した何かをしました:danml.com/js/localstorageevents.js、イベントエミッターベースと「ローカルエコー」があるので、あらゆる場所でEEを使用できます。
dandavis 2015年

7
SafariはBroadcastChannelをサポートしていません-caniuse.com/#feat=broadcastchannel
Srikanth

1
ヘッドアップするだけで、共有ワーカーを使用することもできます:developer.mozilla.org/en-US/docs/Web/API/SharedWorker(ブラウザー間でより良いサポートがあります)
Seblor 2018年

116

この目的専用の最新のAPIがあります- ブロードキャストチャネル

それは次のように簡単です:

var bc = new BroadcastChannel('test_channel');

bc.postMessage('This is a test message.'); /* send */

bc.onmessage = function (ev) { console.log(ev); } /* receive */

メッセージがDOMStringである必要はなく、あらゆる種類のオブジェクトを送信できます。

おそらく、APIのクリーン度は別として、それはこのAPIの主な利点です-オブジェクトの文字列化はありません。

現在ChromeとFirefoxでのみサポートされていますが、localStorageを使用するポリフィルを見つけることができます。


3
待って、メッセージがどこから来たのかどうやってわかるの?同じタブからのメッセージは無視されますか?
AturSams 2017年

2
@zehelvion:たとえば、この素晴らしい概要によると、送信者は受信しません。さらに、あなたが好きなものを含めて、あなたはメッセージに何を入れることができます。必要に応じて、送信者のID。
Sz。

7
こちらのクロスブラウザライブラリにこの機能をまとめた素晴らしいプロジェクトがあります:github.com/pubkey/broadcast-channel
james2doyle

このAPIのサポートがそのブラウザーに導入されるかどうかについて、Safariからの公開シグナルはありましたか?
ケーシー

@AturSams MessageEvent.origin、MessageEvent.source、またはMessageEvent.portsが適切であることを確認します。いつものように、ドキュメントは開始するのに最適な場所です:developer.mozilla.org/en-US/docs/Web/API/MessageEvent
Stefan Mihai Stanescu

40

jQueryに基づいていないソリューションを検索する場合、これはThomas Mが提供するソリューションのプレーンJavaScriptバージョンです。

window.addEventListener("storage", message_receive);

function message_broadcast(message) {
    localStorage.setItem('message',JSON.stringify(message));
}

function message_receive(ev) {
    if (ev.key == 'message') {
        var message=JSON.parse(ev.newValue);
    }
}

1
なぜremoveItem呼び出しを省略したのですか?
トーマスM

2
私はjQueryとJavaScriptの違いに焦点を合わせていました。
ナチョコロマ

ポリフィルとサポートされていない機能の可能性があるため、常にlibを使用します!
アミンラヒミ2018

20

チェックアウトAcrossTabs - クロスオリジンのブラウザタブ間の容易な通信。postMessage API とsessionStorage APIの組み合わせを使用して、通信をより簡単で信頼性の高いものにします。


さまざまなアプローチがあり、それぞれに独自の利点と欠点があります。それぞれについて説明しましょう:

  1. ローカルストレージ

    長所

    1. WebストレージはCookieの改善と単純に見なすことができ、はるかに大きなストレージ容量を提供します。Mozillaソースコードを見ると、5120KB5MBはChromeでは250万文字に相当)がドメイン全体のデフォルトのストレージサイズであることがわかります。これにより、通常の4KBのCookieよりもかなり多くのスペースを使用できます。
    2. データは、HTTPリクエスト(HTML、画像、JavaScript、CSSなど)ごとにサーバーに送り返されないため、クライアントとサーバー間のトラフィック量が減少します。
    3. localStorageに保存されているデータは、明示的に削除されるまで存続します。加えられた変更は保存され、サイトへの現在および将来のすべてのアクセスで利用できます。

    短所

    1. これは、上で動作する同一生成元ポリシー。そのため、保存されたデータは、同じオリジンでのみ使用できます。
  2. クッキー

    長所:

    1. 他のものと比較して、私の知る限りは何もありません。

    短所:

    1. 4Kの制限は、名前、値、有効期限などを含むCookie全体に対するものです。ほとんどのブラウザをサポートするには、名前を4000バイト未満、全体のCookieサイズを4093バイト未満にします。
    2. データは、HTTPリクエスト(HTML、画像、JavaScript、CSSなど)ごとにサーバーに送り返され、クライアントとサーバー間のトラフィック量が増加します。

      通常、以下が許可されます。

      • 合計300クッキー
      • Cookieあたり4096バイト
      • ドメインあたり20個のCookie
      • ドメインあたり81920バイト(最大サイズ4096のCookieが20個ある場合= 81920バイト)
  3. sessionStorage

    長所:

    1. に似ていlocalStorageます。
    2. 変更はウィンドウ(またはChromeやFirefoxなどのブラウザーのタブ)ごとにのみ使用できます。加えられた変更は保存され、現在のページだけでなく、同じウィンドウでのサイトへの将来のアクセスにも使用できます。ウィンドウを閉じると、ストレージは削除されます

    短所:

    1. データは、それが設定されたウィンドウ/タブ内でのみ使用できます。
    2. データは永続的ではありません。つまり、ウィンドウ/タブが閉じられると失われます。
    3. のようにlocalStorage、ttは同一生成元ポリシーで動作します。そのため、保存されたデータは、同じオリジンでのみ使用できます。
  4. PostMessage

    長所:

    1. クロスオリジン通信を安全に有効にします。
    2. データポイントとして、WebKit実装(SafariとChromeで使用)は現在、制限を強制していません(メモリ不足による制限を除く)。

    短所:

    1. 現在のウィンドウからウィンドウを開く必要があり、ウィンドウを開いている間のみ通信できます。
    2. セキュリティ上の懸念 -postMessageを介して文字列を送信すると、他のJavaScriptプラグインによって公開された他のpostMessageイベントをtargetOrigin取得することになるため、メッセージリスナーに渡されるデータのおよび健全性チェックを実装してください。
  5. PostMessage + SessionStorageの組み合わせ

    postMessageを使用して複数のタブ間で通信すると同時に、新しく開かれたすべてのタブ/ウィンドウでsessionStorageを使用して、渡されるデータを永続化します。タブ/ウィンドウが開いたままである限り、データは保持されます。そのため、オープナーのタブ/ウィンドウが閉じても、開いたタブ/ウィンドウは更新後もデータ全体を保持します。

私はこのためのJavaScriptライブラリを作成しました。AcrossTabsは、postMessage APIを使用して、クロスオリジンのタブ/ウィンドウとsessionStorageの間で通信し、開いているタブ/ウィンドウのIDが存続している限り永続化します。


を使用してAcrossTabs、別のタブで別のWebサイトを開き、そこから親タブにデータを取得することはできますか?別のWebサイトの認証の詳細を取得します。
Madhur Bhaiya

1
はい、できます。@ MadhurBhaiya
softvar

Cookieの最大の利点は、クロスオリジン同じドメインを許可することです。これは、「a.target.com」、「b.target.com」などのオリジンのセットがある場合に一般的に役立ちます
StarPinkER

7

人々が使用を検討すべきもう一つの方法は、共有ワーカーです。私はそれが最先端の概念であることを知っていますが、ローカルストレージよりはるかに高速で、同じ原点にいる限り、親/子ウィンドウ間の関係を必要としない共有ワーカーでリレーを作成できます。

これについて私が行った議論については、ここで私の答えを参照してください。


7

に基づいて、同じ起源のタブ/ウィンドウ間で同期/通信する小さなオープンソースコンポーネント(免責事項-私は寄稿者の1人です!)がありlocalStorageます。

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
    DoSomething();
});

TabUtils.CallOnce("lockname", function () {
    alert("I run only once across multiple tabs");
});

https://github.com/jitbit/TabUtils

PSほとんどの「lock / mutex / sync」コンポーネントは、イベントがほぼ同時に発生するときにWebSocket接続で失敗するため、ここで自由にお勧めします


6

ライブラリsysend.jsを作成しました。非常に小さいので、そのソースコードを確認できます。ライブラリには外部の依存関係はありません。

同じブラウザとドメインのタブ/ウィンドウ間の通信に使用できます。ライブラリは、BroadcastChannel(サポートされている場合)、またはlocalStorageからのストレージイベントを使用します。

APIは非常にシンプルです。

sysend.on('foo', function(message) {
    console.log(message);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo'); // empty notification

ブラウザーがBroadcastChannelをサポートしている場合、リテラルオブジェクトを送信します(ただし、実際にはブラウザーによって自動シリアル化されます)。そうでない場合は、最初にJSONにシリアル化され、反対側で逆シリアル化されます。

最近のバージョンには、クロスドメイン通信のプロキシを作成するためのヘルパーAPIもあります。(ターゲットドメインに単一のhtmlファイルが必要です)。

こちらがデモです。

編集

ターゲットドメインに特別なファイルを含め、ソースドメインから関数を呼び出す場合、新しいバージョンはクロスドメイン通信もサポートします。proxy.htmlproxy

sysend.proxy('https://target.com');

(proxy.htmlこれは非常にシンプルなhtmlファイルで、ライブラリに1つのスクリプトタグしかありません)

双方向通信が必要な場合は、target.comドメインで同じことを行う必要があります。

:localStorageを使用して同じ機能を実装する場合、IEに問題があります。ストレージイベントは同じウィンドウに送信され、イベントをトリガーしました。他のブラウザーでは、他のタブ/ウィンドウに対してのみ呼び出されます。


2
ちょうどあなたにこれのためのいくつかの賞賛を与えたかったです。シンプルで、タブ間で通信して、ログアウト警告ソフトウェアがユーザーを蹴飛ばさないようにできる、すてきな甘い小さな追加。良くやった。使いやすいメッセージングソリューションが必要な場合は、これを強くお勧めします。
BrownPony

4

公式のBroadcastchannelと同じように機能するモジュールを作成しましたが、localstorage、indexeddb、およびunix-socketsに基づくフォールバックがあります。これにより、WebworkersやNodeJSでも常に機能します。pubkey:BroadcastChannelを参照してください


1

私は私のブログでこれに関する記事を書きました:http : //www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a- Webアプリケーション

私が作成したライブラリを使用すると、storageManagerこれを次のように実現できます。

storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data

同様に他のシナリオを処理する他の便利な方法があります


0

これは、Chrome向けstorageTomas M answerの開発部分です。リスナーを追加する必要があります

window.addEventListener("storage", (e)=> { console.log(e) } );

ストレージにアイテムをロード/保存すると、このイベントは実行されません-手動でトリガーする必要があります

window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME

そして今、開いているすべてのタブがイベントを受け取ります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.