Communication between multiple user opened windows with same url [duplicate] - javascript

This question already has answers here:
Communication between tabs or windows
(9 answers)
Closed 6 years ago.
I want to place a music player at the homepage (even maybe every page within same application/domain) and play songs on document load. But the user may open more than one instance of the page that contains this player at the same time.
So, if the user opened the windows w1, w2, w3 with same url, how to let only one window play songs at a time? For example, w1 plays, w2 and w3 do not. And when w1 is closed, w2 and w3 should be able to find this event, then elect for playing songs, and only the winner will be able to play songs.
Update:according the giving hints, I think this should be able to work:
// LOGICAL CODE
// Cookie["playerId"] - record which player instance is running
// Cookie["lastRefreshTime"] - record the running player last update time
// when running player instance is destoried (window is closed), and timeout,
// the left player instances should be able to modify Cookie["playerId"] for running.
function Palyer(){
this.playerId = randomString();
var _this = this;
this.refreshHandle = setInterval(function(){
// non-running players to check whether timeout, then competing
if( Cookie["playerId"] != _this.playerId
&& sysTime - Cookie["lastRefreshTime"] > 1000*2){
// competing for run, may have small probability going wrong
Cookie["playerId"] = _this.palyerId;
}
// running player to update timestamp for avoding timeout
if( Cookie["playerId"] == _this.playerId){
Cookie["lastRefreshTime"] = sysTime;
if (!_this.isInitialized()) {
_this.init();
}
}
}, 1000);
...
}
var player = new Player();

actually, you can do a workaround with cookies.
you check the cookies on load if you set a cookie that says a player is open.
you can declare a window closed cookie using onbeforeunload or onunload.
you can let the other windows poll the cookie value to check if a window was closed. then they can pick up from there.
but the problem is the accuracy of the cookie as well as timing.
what if the browser "skipped a beat" and forgot to declare a closed window?
what if the other tabs picked-up the close value event at the same time? both players will play?
what if the browser crashed and the window status cookie is left "open". how would you know if you came from a crash?
another way you can do this is via storage events using localStorage.
One LocalStorage per web application, with a max size of 5MB, is available for a given browser and is shared by all windows and tabs of that browser... If you run MyWebApp in multiple tabs and windows, they all share the same LocalStorage data , subject to a max limit of 5MB.(Chrome)
When data is added to, modified, or removed from LocalStorage or SessionStorage, a StorageEvent is fired within the current browser tab or window. That Storage event contains the storage object in which the event occurred, the URL of the document to which this storage applies, and both the old and the new values of the key that was changed. Any listener registered for this event can handle it.
but, the caveat, besides that it's an HTML5 tech:
Although the HTML5 spec calls for Storage events to be fired in all tabs of the same browser or all windows of the same browser, few browsers currently implement this.

#adeneo answered it: you have to use cookies to manage who has the power to play. It is complex but possible.

Related

SessionStorage browser wide (scope) or LocalStorage deleted on exit

Is there any mechanism that would allow me handle the values on a browser level? What I mean is either:
sessionStorage that I can access values in ANY tab in browser (something like server side sessions)
localStorage that would be removed on session end (when closing the browser, not tab)
For example, the video starts in player in one tab. Some flag is stored in that kind of storage. When user opens another tab with the same URL, app should read that flag and dissalow playing the video. Of course it should be removed on exit, otherwise flag would dissalow all the future requests in that browser. Any suggestions?
every tab or window data can save/read to local/session storage but it's limited to that domain only.
The question that you asked about video handling over two tabs, it can be pulled of, but that is very tricky to handle, and I would not suggest to go that road! You can periodicaly save timestamps of video to browser storage but it also depends on server that is sending the video to browser, and you could end up not serving the video to the user at all!
For clearing the data when browser window close I think there is no event for that but there is event for window loosing focus so you can use that I guess.
hth,
$(window).on('beforeunload', function DecideAction() {
if (('localStorage' in window) && window['localStorage'] !== null) {
//get value from localstorage using getItem and allow/deny the further access
}
});
If the requirement is to have this last "as long as the browser window is open", you're going to hit repeated issues as browsers don't work on that level any more - there is tab-level and domain-level (persists like cookies). The "Browser Window" is just a collection of tabs, unless you specifically set up your browser in a certain way (to remove cookies and session data on closing and not share data between window instances). This however is browser setup (and isn't even standard across different browsers), and not something you can control client-side.
If you're willing to consider some alternatives that will provide the end result you seem to require, if not in the specific manner you have specified, read on:
To Expand on AkshayJ's original comment, use localStorage as sessionStorage is only ever tab-specific (it can't be shared).
In order to clear the flag, as part of the same functionality that sets the flag, add an onunload event to the tab playing the video that will clear it when the tab is closed or the window location moves away from the video. This will allow greater functionality than you originally requested, because in your original case the user would have to close down the browser entirely before they could play the video again, even if the tab that was playing the video was long gone or had moved on to another page.
UPDATE:
If the security/authorization around this is of paramount importance (rather than just wanting to stop it happening "by accident"), then using localStorage is completely the wrong approach - this data and its existence is ultimately controlled by the user. They can remove it, or set up their browser so that window instances don't share the data, so all they need to do is open a new window to view your video twice at the same time. A determined user would find their way around this in minutes.
If you want to control it absolutely, you have to take this domain side rather than relying on browser storage, and use some other tag like a list of currently-accessing IPs, or some other method of identifying a unique user, to determine whether the video can be played or not. Bear in mind that you would have the same issues as before regarding when to clear the flag whether it's browser side of domain side.
UPDATE:
re: what event to use, it appears that onunload and onbeforeunload are both fully supported across all common browsers (ref: Here and Here). This Answer recommends using both in order to be on the safe side.
UPDATE:
The OP has expressed worry that unload events are unreliable and that the user might remain locked out forever if something goes wrong. Personally I haven't experienced any unreliability here, but if you're worried, then introduce a timeout aspect. Have the tab playing the video update the flag (wherever it is stored) with a timestamp every 30 seconds/1 minute/whatever. Then when a new instance of the page loads, have it check the timestamp. if something has happened to the existing page such that it froze and unload events didn't run, the timestamp will be out of date because it will have also stopped updating, so you just have to check whether the timestamp is out of date as well as checking for presence.
Finally I gave up of the server side sessions because it raised other issues, and solved it with this workflow:
After page load, localStorage value is set if it hasn't been before, as well as flag that the player is opened in this tab. If the localStorage is already set, flag is set to false.
If flag is set, play video, otherwise prohibit.
On page unload, only if the flag is set (that is, if user opened video in this tab), remove localStorage value.
$(function () {
if (localStorage.playerTabOpened) {
var dateNow = Date.now();
var diffSinceLastTabOpened = (dateNow - localStorage.playerTabOpened) / 1000;
// if playerTabOpened value was stored more than 1 day ago, delete it anyway because it could be left by chance
if (diffSinceLastTabOpened > 86400) {
localStorage.removeItem("playerTabOpened");
};
}
if (!localStorage.playerTabOpened) {
shared.playerTabOpenedHere = true;
localStorage.setItem("playerTabOpened", Date.now());
} else {
shared.playerTabOpenedHere = false;
}
});
$(window).on("beforeunload", function () {
if (shared.playerTabOpenedHere) {
localStorage.removeItem("playerTabOpened");
}
});
if (shared.playerTabOpenedHere) {
// play
} else {
// throw error
}

JS IPC; send messages from one tab to another?

For example with youtube center (a userscript) I can have a youtube video playing in one tab and I open another video the first one will pause. If I switch tabs and hit play the other will now pause. How does the userscript do this? I tried looking at the source
It appears to use a socket but it looks like socket is a plain object. It says it mimics socket.io which I don't know either but I believe this one doesn't connect to a site while socket.io does?
The comment says what it's happening. It's using localstorage (HTML5 functionality) to write and notify:
/**
* A cross-window broadcast service built on top
* of the HTML5 localStorage API. The interface
* mimic socket.io in design.
*
The following lines set up the listeners.
if (identifier === 6) {
session_addEventListener("storage", storageHandler);
} else if (window.attachEvent) {
document.attachEvent('onstorage', storageHandler);
ytcenter.unload(function(){
document.detachEvent("storage", storageHandler);
});
} else {
window.addEventListener('storage', storageHandler, false);
ytcenter.unload(function(){
window.removeEventListener("storage", storageHandler, false);
});
}
Clearly, there are listeners on both tabs such that every time you write/read to localstorage the other tab is notified.
So in short, no, the data doesn't go to a server. It simply writes to localstorage and relies on the browser to make available changes to localstorage across all tabs simultaneously.
You can read more about the storage event here: http://dev.w3.org/html5/webstorage/#the-storage-event
Worth noting events aren't immediately available, only on activation of the Document (i.e. your events don't / might not get processed until the tab is active). I'd assume different browsers handle the 'activation' differently.
Such a Document object is not necessarily fully active, but events fired on such objects are ignored by the event loop until the Document becomes fully active again.

Communicating between different windows on the same domain [duplicate]

This question already has answers here:
Communication between tabs or windows
(9 answers)
Closed 6 years ago.
I am building an app that performs a lot of client side data downloading and processing. The data processing is isolated from the main app by being processed in an iframe that resides on a sub domain. It is this iframe that downloads the data. Communication is via postMessage.
Everything works fine, except it could be better.
If the user opens extra tabs/windows, the app currently reloads all the data and may even do duplicate processing work, which isn't a problem other than that it slows everything down and pages take longer to load.
What I would like to do is have each top level tab/window communicate with just the one processing iframe, which could be reinstated if the original window is closed. The trouble is, these are not opened via javascript, but via the normal browser methods to open links in tabs so I can't get a reference to the iframe that is needed to send a message.
Is there anyway I can communicate the window reference for the iframe to the other tabs so that they can communicate with it via a postMessage? Could this in someway be achieved using shared workers?
I realize I could use shared workers for the whole processing task, but this would have it's own problems as the the data comes from third party domains, which can't be accessed from within a worker.
Only compatibility with the latest versions of all major browsers is needed.
Edit: I've just discovered that SharedWorker is not yet implemented in firefox, so I guess that is not going to work. Any other way I could achieve this?
Edit 2: I've discovered that you can use :
var win = window.open('', 'my_window_name');
to capture a reference to an iframe from any other window. However, if the iframe does not already exist then it will open it as a window. Even if it is closed immediately, it causes a flicker and causes the 'popup blocked' messages, making it unusable.
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.

Play sound only in one window of which is active (within one website) using javascript (possibly jquery)

Alright, I've found a bunch of answers concerning native functions like window.onblur and window.onfocus... But they won't help so I'd like to be more specific
Say you open several tabs of one website
Say you receive a message and there's a sound to announce the message
As you have several tabs opened, you will hear the sound the number of opened tabs. Which makes a how'd'u'callit symphony
Best solutions I've found so far, but which don't work
1. window.onfocus and window.onblur
2. Play sound if var infocus evaluates to true, don't play if not
3. It is crossbrowser
4. It is simple
5. It does not work
Why the best solution won't work? Say you switch focus to another tab of a different website, your website loses focus so you won't hear the sound. Even worse, say you switch to another program, then the browser itsel loses focus and you won't hear the sound
So what shall I do?
You could save the timestamp of the last onFocus() event in a JavaScript variable and in a cookie (access set to your website root). Then when you want to play the alert sound, you compare the current values of the variable and the cookie and only play the sound if those two match.
Alright, two weeks after it seems like I've found the real solution. Which actually proves that if you want to do something, don't ask for help, just do it
This is what I did:
Create a cookie with a randon id and the current time (winid + t1). The cookie is created by each opened tab on loading.
document.cookie = 'winid='+winid+t1;
Create a function which will update the current time in the set cookie, say, every 3 seconds (I kindda don't like to overflow clients, so it's 3 secs not 1). If the function finds out that the winid in the cookie and the winid of the current tab don't match and 3 secs have elapsed, then the tab was closed, redefine the primary tab inside the same function.
window.setInterval(setwinid,3000);
This is it, every time you need to, say, play a sound, you should check first, whether it is the tab which is to play it
The trick is that each tab has its own winid. But only one winid is stored in cookie, is updated and thus allows the one tab to perform actions. Pretty simple. I actually started using this method for updating messages in the box across all tabs not only for playing music
One solution would be to have a server-side solution that would play the notification only once. You don't specify how the site receives the messages, but I assume it's some form of AJAX call that gets the message from the server and the messages are saved in a database.
Add a flag to the database that signifies that the message has been sent. Set the flag the first time the user's browser queries for new messages. On the page itself play the sound only if the flag has not been set, otherwise don't play the sound. Now only the first page that fetches the message will play the sound.

Chrome - Detect When Browser Exits

I am wondering if it is possible to detect whether the user exits the Chrome browser?
EDIT - Sorry, I wasn't being very clear so I'll explain my situation. I am storing some variables in the browser's localstorage. When the user closes the browser, I want to delete some of these variables.
Executing some JavaScript before the window is unloaded
You can hook the OnBeforeUnload event of the window
<script type="text/javascript">
$(window).bind('beforeunload', function() {
if (iWantTo) {
return "Don't leave me!";
}
});
</script>
Using a heartbeat to know when the user has left
Or create a JavaScript timer that pings your sever every XX seconds. When the pings stop, you can assume the user has closed the browser or navigated away.
http://ajaxpatterns.org/archive/Heartbeat.php
They have lots of good stuff in their documentation. onRemoved of the window object would seem to do it.
https://developer.chrome.com/docs/extensions/reference/windows/#event-onRemoved
Or perhaps you mean tabs. In which case the onRemoved for the tab object would do it.
https://developer.chrome.com/docs/extensions/reference/tabs/#event-onRemoved
API Index

Categories

Resources