153

What's the most reliable way to have JavaScript communicate between tabs/windows of the same browser? For example, when Tab 2 starts audio playback, Tab 1 somehow knows about this and can pause its player.

I'm building a site with a music player... so at the moment if you open two tabs to the site, you could start music on both. This is obviously bad, so I'm trying to find a solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
adamJLev
  • 13,713
  • 11
  • 60
  • 65
  • 2
    Auto-playing the audio is bad no matter what. Why not just let the users click a "play" button, and manually pause the other tab if they hit this situation? – Mike Ruhlin Nov 02 '10 at 15:39
  • 9
    There's no autoplay. But it would be nice if the user didn't have to manually pause the other tab. Youtube does this for example (with flash) – adamJLev Nov 02 '10 at 15:54
  • http://stackoverflow.com/questions/19125823/how-is-it-possible-to-share-single-js-resource-between-browser-tabs/19165781#19165781 – inf3rno Oct 03 '13 at 22:24
  • There are other options, like shared webworkers and localstore storage event... – inf3rno Oct 03 '13 at 23:11
  • See [this library](https://github.com/ScarletsFiction/SFIntercom) – StefansArya Jul 29 '18 at 11:50

10 Answers10

149

For a more modern solution check out https://stackoverflow.com/a/12514384/270274

Quote:

I'm sticking to the shared local data solution mentioned in the question using localStorage. It seems to be the best solution in terms of reliability, performance, and browser compatibility.

localStorage is implemented in all modern browsers.

The storage event fires when other tabs makes changes to localStorage. This is quite handy for communication purposes.

Reference:
http://dev.w3.org/html5/webstorage/
http://dev.w3.org/html5/webstorage/#the-storage-event

Community
  • 1
  • 1
brillout
  • 7,804
  • 11
  • 72
  • 84
  • 5
    This is better than the accepted solution. This doesn't require you to check constantly for new informations, doesn't have a delay and enables you to receive all events. – Stephane Mathis Oct 16 '15 at 14:11
  • 2
    I have issues with localStorage on IE11, see this post (i faced point# 3) http://blogs.msdn.com/b/ieinternals/archive/2009/09/16/bugs-in-ie8-support-for-html5-postmessage-sessionstorage-and-localstorage.aspx so the cockie soliution is better (for IE at least). Also i tried to disable cockies but it still working (this is on IE 11). – Anas Nov 03 '15 at 10:33
  • 3
    Good demo page - http://html5demos.com/storage-events#view-source – JsAndDotNet Jun 09 '16 at 08:02
  • 1
    @Anas: Link is dead. New URL: https://blogs.msdn.microsoft.com/ieinternals/2009/09/15/html5-implementation-issues-in-ie8-and-later/ – Tim Down Jul 20 '17 at 08:18
  • 1
    This solve a lot of issues (are is wider implemented than BroadcastChannel), but be careful that you will have some surprises if user opens 2 "sender" tabs in the same browser: having 1 common communication channel might raise up some surprises – Xenos Jan 16 '19 at 10:04
93

Update to a modern solution, leaving the old one below for historical reasons.

You can use Broadcast Channel API to send and receive messages https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API

// Connection to a broadcast channel
const bc = new BroadcastChannel('test_channel');

// Example of sending of a very simple message
// It doesn't have to be a string, it could be a JS object
bc.postMessage('This is a test message.');

To receive the message:

// A handler that only logs the event to the console:
bc.onmessage = function (ev) {
  console.log(ev);
}

and to close the channel:

// Disconnect the channel
bc.close();

THIS IS HISTORICAL OLD WAY TO DO IT, USE THE METHOD ABOVE FOR MODERN BROWSERS!

You can communicate between browser windows (and tabs too) using cookies.

Here is an example of sender and receiver:

sender.html

<h1>Sender</h1>

<p>Type into the text box below and watch the text 
   appear automatically in the receiver.</p>

<form name="sender">
<input type="text" name="message" size="30" value="">
<input type="reset" value="Clean">
</form>

<script type="text/javascript"><!--
function setCookie(value) {
    document.cookie = "cookie-msg-test=" + value + "; path=/";
    return true;
}
function updateMessage() {
    var t = document.forms['sender'].elements['message'];
    setCookie(t.value);
    setTimeout(updateMessage, 100);
}
updateMessage();
//--></script>

receiver.html:

<h1>Receiver</h1>

<p>Watch the text appear in the text box below as you type it in the sender.</p>

<form name="receiver">
<input type="text" name="message" size="30" value="" readonly disabled>
</form>

<script type="text/javascript"><!--
function getCookie() {
    var cname = "cookie-msg-test=";
    var ca = document.cookie.split(';');
    for (var i=0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(cname) == 0) {
            return c.substring(cname.length, c.length);
        }
    }
    return null;
}
function updateMessage() {
    var text = getCookie();
    document.forms['receiver'].elements['message'].value = text;
    setTimeout(updateMessage, 100);
}
updateMessage();
//--></script>
Roman Goyenko
  • 6,965
  • 5
  • 48
  • 81
  • 2
    I thought of something like this too, but was hoping for a better solution than cookies/setTimeout. This might just be the only solution however. Thx – adamJLev Nov 02 '10 at 16:03
  • what kind of player are you using? If it's a flash player, you can do stuff with flash that might be more elegant. – Roman Goyenko Nov 02 '10 at 16:16
  • 16
    Don't pass a string to `setTimeout` - you're using `eval` by doing that. Instead, pass the function in directly with `setTimeout(updateMessage, 100)` – Yi Jiang Nov 06 '10 at 02:40
  • changed the setTimeout as per Yi suggestion. – Roman Goyenko Feb 28 '12 at 16:03
  • I open sourced a project based on this answer, in case anyone is interested: https://github.com/jeremyharris/local_connection – jeremyharris Aug 24 '12 at 01:41
  • I can see that setTimeout every 100 ms is consuming the javascript specially in Chrome when you open more than 4 browser tabs. – moderns May 28 '14 at 18:13
  • 2
    I would also recommend using `setInterval()` – Julian F. Weinert Jul 03 '14 at 15:49
  • 1
    I have issues with localStorage on IE11, see this post (i faced point# 3) http://blogs.msdn.com/b/ieinternals/archive/2009/09/16/bugs-in-ie8-support-for-html5-postmessage-sessionstorage-and-localstorage.aspx so the cockie soliution is better (for IE at least). Also i tried to disable cockies but it still working (this is on IE 11, IE 10, IE 9). – Anas Nov 03 '15 at 12:43
  • This only works if both tabs have same host. – Olle Härstedt Jul 29 '16 at 12:24
  • This is so bad answer I can't even... – Tomáš Zato Feb 03 '17 at 18:00
  • 1
    Tomáš Zato, be aware the answer is from 2010 - when HTML5 was not supported by all the browsers and the share of IE6 and IE7 was pretty high. There are better solutions now. – Roman Goyenko Feb 07 '17 at 15:42
  • That's now a very bad usage of cookies: 1) they are uselessly sent to server 2) `value` must be escaped when put in the cookie 3) having 2 "sender" tabs opened will be a mess 4) you are limited by cookie size 5) you are not applying Hollywood principle (heavy useless computations) 6) attacker can mess with this client-side only communication from the server side if they manage to alter cookies (=architecture is leaking). So see `BroadcastChannel` instead – Xenos Jan 16 '19 at 10:01
16

I don't think you need cookies. Each document's JavaScript code can access the other document elements. So you can use them directly to share data.

Your first window w1 opens w2 and save the reference

var w2 = window.open(...)

In w2 you can access w1 using the opener property of window.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
donkeydown
  • 698
  • 7
  • 16
  • 2
    USING COOKIES? Eat them and enjoy! There is a MUCH easier way! Just access a var of the other window. Got a value var in w1, access it from w2 whit window.opener.value ! – donkeydown Feb 07 '12 at 18:03
  • 2
    Let's say that the user opens them all. Any similar solution in that case? – Fardin K. Dec 02 '12 at 08:26
  • I think that you may open as many windows you want and use this solution. The reference to any new window must be registered somewhere. The reference to the parent window is always available. I don't see any limitation so far. @Fardinak – donkeydown Feb 08 '13 at 10:53
  • 27
    Just so everyone knows, this is answer is wrong, as @Ferdinak already tried to say. You don't have a reference to a tab the user opens. – DDS Jun 22 '13 at 21:53
  • 1
    @DDS: you are right, as an answer this is wrong, but it seems to be interesting, so I let it there. Using localStorage I think is the right solution. – donkeydown Jun 23 '13 at 08:47
  • 4
    Interesting is irrelevant. This is a Q&A site first and foremost. People come to this page to look for answers to the asker's question. If you want to share something interesting, you should consider writing a new wiki-style question. – ivanjonas Dec 04 '14 at 20:21
  • a little bit different question, is it possible for the child window to call a method from parent window? by parent window I mean the one which created child window using `window.open`... – Marek Feb 23 '15 at 18:43
  • Marek: of course you can, using window.opener.methodName from the child. – donkeydown Mar 05 '15 at 17:28
  • Marek: of course you can, using window.opener.methodName from the child. – donkeydown Mar 05 '15 at 17:28
  • 1
    @Marek - It is possible but it's bad software design, doing so you are tightly coupling the two windows. A better way is to fire a specific EVENT in the parent passing any data along with that event object and then let the parent respond to that EVENT. You can fire EVENTs or Messages using the postMessage API and use a polyfill for earlier browsers however you will need a ref' to the window where you want to send the Event in the same way you need a ref' to the window if you invoke a function directly. This though doesn't help the original question so LocalStorage is a better mechanism. – Nick Middleweek Apr 16 '15 at 12:28
  • 6
    @jonas.ninja IMO the question was not 100% clear on how the tabs are opened, so this is a perfectly valid answer even if it's not a universal one. – Superole Apr 19 '16 at 11:33
  • 1
    @Superole I'm slightly embarrassed that I wrote that. I must have been angry that day or something. My apologies. – ivanjonas May 05 '16 at 20:17
  • Please not the you cannot access properties of the pop up if they are in different origin – Ayush Nov 05 '20 at 15:29
15

You can do this via the local storage API. Note that this works only between two tabs. You can't put both sender and receiver on the same page:

On the sender page:

localStorage.setItem("someKey", "someValue");

On the receiver page:

    $(document).ready(function () {

        window.addEventListener('storage', storageEventHandler, false);

        function storageEventHandler(evt) {
            alert("storage event called key: " + evt.key);
        }
    });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Dehghan
  • 22,159
  • 10
  • 107
  • 95
  • I was going to use this method until I found out that the webbrowser control does not fire the "storage" event handler method. Not sure why. Bug perhaps. – Brain2000 Jun 15 '15 at 22:23
  • 2
    Thanks for this solution. Made my day. It didn't work with file:/// protocol but works with a valid domain. Another similar demo http://html5demos.com/storage-events – Vikram Kumar Mar 28 '16 at 20:28
  • 3
    You don't need to wait for DOM readiness in order to access localStorage – meandre Aug 13 '17 at 23:30
15

There is also an experimental technology called Broadcast Channel API that is designed specifically for communication between different browser contexts with same origin. You can post messages to and recieve messages from another browser context without having a reference to it:

var channel = new BroadcastChannel("foo");
channel.onmessage = function( e ) {
  // Process messages from other contexts.
};
// Send message to other listening contexts.
channel.postMessage({ value: 42, type: "bar"});

Obviously this is experiental technology and is not supported accross all browsers yet.

Leonid Vasilev
  • 11,910
  • 4
  • 36
  • 50
12

Below window(w1) opens another window(w2). Any window can send/receive message to/from another window. So we should ideally verify that the message originated from the window(w2) we opened.

In w1

var w2 = window.open("abc.do");
window.addEventListener("message", function(event){
    console.log(event.data);
});

In w2(abc.do)

window.opener.postMessage("Hi! I'm w2", "*");
Ashwin Aggarwal
  • 649
  • 8
  • 21
10

Communicating between different JavaScript execution context was supported even before HTML5 if the documents was of the same origin.

If not or you have no reference to the other Window object, then you could use the new postMessage API introduced with HTML5. I elaborated a bit on both approaches in this Stack Overflow answer.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martin Andersson
  • 18,072
  • 9
  • 87
  • 115
  • 5
    postMessage API is not designed for that http://stackoverflow.com/a/1100416/470117 You need the reference of targeted window to post a message for that specific window – mems Jul 23 '14 at 08:41
9

You can communicate between windows (tabbed or not) if they have a child-parent relationship.

Create and update a child window:

<html>
<head>
<title>Cross window test script</title>
<script>
var i = 0;
function open_and_run() {
    var w2 = window.open("", "winCounter"); 
    var myVar=setInterval(function(){myTimer(w2)},1000);
}

function myTimer(w2) {
    i++;
    w2.document.body.innerHTML="<center><h1>" + i + "</h1><p></center>";
}
</script>
</head>
<body>
Click to open a new window 
<button onclick="open_and_run();">Test This!</button>    
</body>
</html>

Child windows can use the parent object to communicate with the parent that spawned it, so you could control the music player from either window.

See it in action here: https://jsbin.com/cokipotajo/edit?html,js,output

Victor Stoddard
  • 3,582
  • 2
  • 27
  • 27
  • One problem here is, that we can not (without being hacky) share a link to our synced view... – yckart Mar 10 '15 at 13:41
  • @yckart There are so many ways to do this but the most common way I've seen is to send the string to an input box in the other window. You can make an event listener for a change of value. Example: http://stackoverflow.com/questions/9994120/javascript-pass-selected-value-from-popup-window-to-parent-window-input-box . I think it would be better to just call a javascript function in the other window, passing it the URL. E.g., from the child window or from the parent window. – Victor Stoddard Mar 23 '16 at 01:15
2

I found a different way using HTML5 localstorage. I've created a library with events like API:

sysend.on('foo', function(message) {
    console.log(message);
});
var input = document.getElementsByTagName('input')[0];
document.getElementsByTagName('button')[0].onclick = function() {
    sysend.broadcast('foo', {message: input.value});
};

https://github.com/jcubic/sysend.js

It will send messages to all other pages, but not to the current one.

EDIT:

The library in the newest version also supports broadcast channel communication, but still, it works in IE11 that only supports local Storage. It also supports cross-origin communication (different domains) but a little bit of code.

The latest API also supports the emit function that executes events also on the same page.

Even latest version, also suport managing the windows, send message to particular window or get list of widows/tabs.

jcubic
  • 61,973
  • 54
  • 229
  • 402
-1

With Flash you can communicate between any window, any browser (yes, from Firefox to Internet Explorer at runtime) ...any form of instance of Flash (Shockwave or ActiveX).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131