JavaScript; Communication Between Tabs/Windows With Same Origin

Communication between tabs or windows

You may better use BroadcastChannel for this purpose. See other answers below. Yet if you still prefer to use localstorage for communication between tabs, do it this way:

In order to get notified when a tab sends a message to other tabs, you simply need to bind on 'storage' event. In all tabs, do this:

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

The function message_receive will be called every time you set any value of localStorage in any other tab. The event listener contains also the data newly set to localStorage, so you don't even need to parse localStorage object itself. This is very handy because you can reset the value just right after it was set, to effectively clean up any traces. Here are functions for messaging:

// 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.
}

So now once your tabs bind on the onstorage event, and you have these two functions implemented, you can simply broadcast a message to other tabs calling, for example:

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

Remember that sending the exact same message twice will be propagated only once, so if you need to repeat messages, add some unique identifier to them, like

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

Also remember that the current tab which broadcasts the message doesn't actually receive it, only other tabs or windows on the same domain.

You may ask what happens if the user loads a different webpage or closes his tab just after the setItem() call before the removeItem(). Well, from my own testing the browser puts unloading on hold until the entire function message_broadcast() is finished. I tested to put some very long for() cycle in there and it still waited for the cycle to finish before closing. If the user kills the tab just in-between, then the browser won't have enough time to save the message to disk, thus this approach seems to me like safe way how to send messages without any traces.

JavaScript; communication between tabs/windows with same origin

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.

References can be found here:

Webstorage

Webstorage - storage event

Communication between browser tabs/windows using JavaScript

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>

Communication between browser tabs

That is strange. Opening a new window with window.open("newtab.html") should give the newly opened window the ability to reference the opener via window.opener.

Anyway, there are several other options for communicating between two windows on the same domain. I think that the following two are the easiest to implement:

  1. Use localStorage. If you store some data under some key into localStorage in the new tab window, then the opener window will get a storage event. If you catch the event with a handler, you can read the data from localStorage that you wrote from the new tab window. Note that events are fired only if both pages are on the same domain, and if there are actually two windows in question (events are not fired within the same window that wrote data). For more info on how to do this -- see for example: http://diveintohtml5.info/storage.html

  2. Use shared Web workers. A web worker is a JS script that is executed in the background on a separate thread. Shared web workers are a special type of Web workers that allow any number of parent documents to communicate with a single worker. So, you could use the worker to relay messages between the opener window and the new tab window. The API for communicating with a workers is very similar to the postMessage API. For more info on how to do this -- see for example: http://www.sitepoint.com/javascript-shared-web-workers-html5/.

Communicate between tabs dom without window ref

I faced a similar issue and build a small lib to make function calls via localStorage with paramter possible. You can find it here.
Service worker are currently not supported by all browsers.

Here is an example how to use it:

//Register a function: 
RegisterLocalStorageFunction("test", function(param){
return param+param;
});

//Call a function:
let str = "testing";
CallLocalStorageFunction("test",str,function(para){
console.log(para);
});

In your context:

RegisterLocalStorageFunction("CallAlert", function(param){ 
alert(param);
return "Success";
});
var p = document.getElementById("myElement");
var a = document.createElement('a');
a.setAttribute('href',".../mypage.html");
a.setAttribute('rel',"noreferrer");
a.setAttribute('target',"_blank");
p.appendChild(a);
a.click();

In your other window:

<!DOCTYPE html>
<html>
<body>
<h1> Heading</h1>
<p> paragraph.</p>
<button type="button" onclick="btnClick()">Click Me!</button>
<script>
function btnclick(){
CallLocalStorageFunction("CallAlert","Hello from the other tab",function(para){
console.log("para");
});
}
</script>
</body>
</html>

Both sides must be on the same domain, otherwise they cant access the same localStorage. With my currrent code on github I use setInterval to cycle through the storage. There is a storage event, which is fired to all other tabs and windows, but not to the same tab. I'll rewrite the lib to use the much cleaner approach with the event, but for now this should do the trick.

Update

In the repository you can find communicator2, which is based on the 'storage' event.

Update

Here is a working example hosted. Keep in mind to allow popups.

Javascript: Communication beetween browser tab on same top domain

No, there is no way to do this only in the frontend. You need a server (backend) where booth tabs talks with. So tab 1 can send something to the server and tab 2 get it from the server.
Everything else would be a big security issue!

Communicating between different windows on the same domain

In case any one else finds this, I've come up with a solution. It is somewhat hacky and requires further testing. But so far it is working. It works cross domain if that is needed.

It uses a combination of two tricks.

The first is to use

remote_window = window.open("", "remote_window_name");

to fetch a reference to the window. This works because if a window is already open with the given name then a reference is returned to it rather than opening a new window.

It does however have the problem that if the iframe does not exist then a new window will pop up. Local storage is used in order to prevent this. When a window/tab loads, it checks localStorage to see if there is another page already with a shared iframe. If not it inserts the the iframe and sets a flag in local storage to say that it is available.

As a last ditched resort, if the window still opens, a try block is used to close the newly opened window. The try block prevents cross domain errors. This means that the worst that will happen is the user sees a window pop up and disappear or they will see the 'enable pop-ups' message. I've yet to manage to trigger this in testing - it is only an edge case fall back.

try {
if(store_window.location.href === "about:blank" ){
remote_window.close();
remote_window = insertIfame();
}
} catch(err) {
}

An onunload event is added which removes the flag should the page be closed.

Also a setInterval is created that constantly refreshes a timeout flag. I have it running 4 times a second; when a second window/tab is loaded it checks that the iframe flag has not timed out before trying to communicate with it. This is a small overhead, but far less than the cost to me of having that second iframe loading. This serves the purpose of preventing stale data if the browser crashes or the the onunload does not fire for any reason. I include a small leeway when checking the timeout - currently 1 second - in case the main window is stuck in a loop. The leeway is only on the timeout, not the unload event which removes the flag entirely.

The flag needs to be checked every time a message is sent in case the original window with the iframe has closed. When this happens the iframe is reinserted in the first open window that requires it and the flag is reset.

Sending messages back is easy. Just use the event.source property of the receiveMessage -this points to the sending window.

One final edge case to account for is if the primary window closes whilst it's iframe is mid process for a secondary window. Theoretically this could be dealt with by using an onunload event in the iframe to send a message back to any windows with data in process. But I've yet to implement it and it may not finish before the page unloads. Another way of dealing with it would be by having a timeout in the secondary window which checks the flag and retries, I'll probably go this route as the messages already have timeouts attached.



Related Topics



Leave a reply



Submit