JavaScript: sharing data between tabs - javascript

What is the best way to share data between open tabs in a browser?

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, efficiency, 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

If the first tab opens the second tab automagically, you can do something like this:
First tab:
//open the first tab
var child_window = window.open( ...params... );
Second tab:
// get reference to first tab
var parent_window = window.opener;
Then, you can call functions and do all sorts of stuff between tabs:
// copy var from child window
var var_from_child = child_window.some_var;
// call function in child window
child_window.do_something( 'with', 'these', 'params' )
// copy var from parent window
var var_from_parent = parent_window.some_var;
// call function in child window
parent_window.do_something( 'with', 'these', 'params' )

The BroadcastChannel standard allows doing this. see MDN BroadcastChannel
// Connection to a broadcast channel
const bc = new BroadcastChannel('test_channel');
// Example of sending of a very simple message
bc.postMessage('This is a test message.');
// A handler that only logs the event to the console:
bc.onmessage = function (ev) { console.log(ev); }
// Disconnect the channel
bc.close();

See also another StackOverflow thread: Javascript communication between browser tabs/windows.
In my opinion there are two good methods. One may suit you better depending on what you need.
If any of these are true...
you can't store information server side,
you can't make many http requests,
you want to store only a little bit of information[1],
you want to be pure javascript / client side,
you only need it to work between tabs/windows in the same browser.
-> Then use cookies (setCookie for sending, getCookie/setTimeout for receiving).
A good library that does this is http://theprivateland.com/bncconnector/index.htm
If any of these are true...
you want to store information server side
you want to store a lot of information or store it in a related matter (i.e. tables or multi-dimensional arrays[2])
you also need it to across different browsers (not just between tabs/windows in the same browser) or even different computers/users.
-> Then use Comet (long-held HTTP request allows a web server to basically push data to a browser) for receiving data. And short POST requests to send data.
Etherpad and Facebook Chat currently use the Comet technique.
[1] When using localStorage more data can be stored obviously, but since you'd fallback on cookies one can't rely on this yet. Unless you application is for modern browsers only in which case this is just fine.
[2] Complicated data can be stored in cookies as well (JSON encoded), but this is not very clean (and needs fallback methods for browsers without JSON.stringify/JSON.parse) and can fail in scenarios involving concurrency. It's not possible to update one property of a JSON cookie value. You have to parse it, change one property and overwrite the value. This means another edit could be undone theoretically. Again, when using localStorage this is less of a problem.

The only way I can think of: constant ajax communication with the server to report any user action on the other tabs.

How about to use a cookie to store data in one tab and poll it in another tab?
i dont know yet if a cookie is shared between tabs but just an idea now ...

I just took a look at how Facebook Chat does it and they keep a request to the server open for a little less then a minute. If data comes back to the server, the server then sends back the message to each open request. If no data comes back in a minute, it re-requests and continues to do this (for how long, I am not sure).

Given that these tabs are open with the same site in them, you might consider building an ajax script that reports user actions to server and couple it with another ajax script that reads that reports and reflects them in current window.

You could use AJAX (as everyone else is suggesting) or cookies if the data is small. See http://www.quirksmode.org/js/cookies.html for fun with cookies.

One way to do this is to not let the chat window be dependent on the tabs. Load the tabs as seperate AJAX components that when reloads doesn't affect the chat component.

Depending on the requirements you can also use cookies/sessions. However, this means the data will only be accessible on the first page load of each tab.
If you already have two tabs open, changing something in one will not change the other unless you use some AJAX.

This can be done using BroadcastChannel API in javascript. Let's say you have opened two different pages in a different tab and want to update the first page when the user changes some values in the second page you can do that like below.
First page
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.onmessage = function(e) {
console.log('ticket updated')
};
Second page
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.postMessage();
Now when you can postMessage it will trigger the onmessage on the first page.
Also, you can pass data like the below.
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.postMessage({message:'Updated'});
const ticketUpdateChannel = new BroadcastChannel('ticketUpdate');
ticketUpdateChannel.onmessage = function(e) {
console.log('ticket updated',e.data)
};

Related

Updating LocalStorage with the same data in the same time on multiple tabs causing problems

first of all i'm a beginer front end developer and i'm not a native english speaker so sorry for any mistake i made in my first question :D I'm working on project in Vue that was started by someone else. It uses websocket to display some notifications from server and i spotted a bug associated with this. The notifications are stored in object that pulls data from localStorage using VueUse's useStorage:
const state = reactive({
liveNotifications: useStorage("liveNotifications", []),
notificationsCount: 0,
});
And when data is received from ws it's being added to the beginning of the array like this:
connections.alerts.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data?.status) {
return;
}
state.liveNotifications.unshift(data);
state.notificationsCount += 1;
};
The problem is, when more than 2 tabs are opened and ws send some notifications, the localstorage starts acting weird like its trying to add the same objects over and over and notificationsCount is jumping (for example) from 2 to 3 and vice versa.
Is there any way to e.g. prevent app from updating localstorage multiple times if the data given from ws is the same on all tabs. Or maybe there's another way to make this work properly?
I've tried some solutions from web but to be honest i'm not really sure why is this happening and i didn't know what exactly i was supposed to look for so if someone has better knowledge than me and can help me understand i'm here to learn :)
The problem is: both tabs will read/write to the same "file".
The localStorage read-only property of the window interface allows you to access a Storage object for the Document's origin; the stored data is saved across browser sessions.
MDN - Window.localStorage
Suggestion here is to use sessionStorage instead:
[...] the difference is that while data in localStorage doesn't expire, data in sessionStorage is cleared when the page session ends.
Whenever a document is loaded in a particular tab in the browser, a unique page session gets created and assigned to that particular tab. That page session is valid only for that particular tab.
It sounds like you need a shared worker.
Since you are receiving the same data it is reduntant to keep two connections.
You should handle your websocket connection in a shared worker, then upon receiving the data save it to the localStorage, then post a message to the tabs to update the UI.

Angular scope variable not saved when using browser back button?

I have a AugularJS controller that have something like the following when initialized
$scope.urlChanged = false;
and the url is like /firstpage/test
There is a button in the page and when user clicks the button, the following is executed
$scope.urlChanged = true;
window.location = '/secondpage/test';
The page goes to /secondpage/test as expected. When clicking the browser back button, the page goes back to /firstpage/test. But the $scope.urlChanged is false, not the final value true. Is this expected in Angular? How do I make $scope.urlChanged = true when going back?
Scope variables are not saved when you navigate. In fact not even services will retain their values/state. When you navigate with browser back you actually request a whole new angular app.
This is not angular's fault. That is how the browser is expected to handle a new request. The way to persist data in this case is saving any data in something that will persists between requests.
As i see it you have three(ish) options:
Save state in cookies: Well supported by almost all browsers but take caution to handle them as clientside cookies or you won't be able to save data on a page you did not submit (excatly your problem with navigate back in browser).
Save server-side. This has the same problems as the server side cookies. You need to post data to the server for it to persist - which you could do with ajax calls and 'auto-save' with a timeout function. You also need a way of tracking who you are so you can get the correct data from the server - which is often done with cookies, but can be done with querystring parameters but also with (basic) authentication.
LocalStorage. This is my favorite option and is pretty well supported, if you don't need to support legacy IE browsers. There are good frameworks designed for angular that makes it easy to use - and some even have fallback to cookies if not supported.
Check out this link for LocalStorage support:
https://github.com/grevory/angular-local-storage
On change of view,new controller comes into picture and the previous view's controller's instance gets finished. Also , as every controller has its private scope which gets destroyed once view is changed to avoid confusion.

Using localstorage checks in a different window

I'm trying to make a basic webapp. It's basically a puzzle that appears over time, when you find certain links or URLs.
The puzzle has 8 pieces, and they appear when you visit a certain hash. The hashes are setup using backbone.js, and they each trigger a function that shows the piece that corresponds with it.
The hashes go like this - "index.html#hide/one", "index.html#hide/two", up to "index.html#hide/eight".
Every time a hash is triggered, it shows a piece using a JavaScript function that simply adds a class to the element. Easy enough, right?
The problem is, the hashes open in a different window. The main window is just "index.html#hide". So I need to create a localstorage value for each piece, constantly check it on the main page, and if it's set to "yes", execute a function.
Is this possible? And if so, how could I go about doing it?
Thanks in advance,
-Mitchyl
Edit - Here's the source code if anyone's interested. I'm not quite sure what's relevant and what's not, so here's the code in it's entirety. http://pastebin.com/Q4hpJtQ8
Rather than polling LocalStorage from the main window, it's probably better to use some sort of direct communication between the windows.
If all your windows are the same origin (same protocol, domain and port) and one window opens the others, then you can directly call JS functions from one window to another as long as you save the window handle. For windows that your main window opened, the main window will be in window.opener so you could just call a globally defined function in the main window like:
window.opener.updatePuzzle(data);
You can also use window.postMessage() to exchange data between windows which seems (on the surface) a bit cleaner and isn't as restrictive about same origin (because it requires two cooperating windows), but there are limitations with postMessage() in IE before IE11 which still make it difficult to rely on (thanks IE).
If your main window did not open the other windows in any way (e.g. the user just typed the other URLs), then your windows cannot directly communicate with one another within the browser. In that case, they'd either have to exchange data via a server or poll for data via LocalStorage.
Here's a pretty good tutorial on LocalStorage: http://www.sitepoint.com/an-overview-of-the-web-storage-api/:
From one window:
localStorage.setItem("hash4", true);
From the other window:
setInterval(function() {
var maxHash = 5;
for (var i = 0; i < maxHash; i++) {
var val = localStorage.getItem("hash" + i)
if (val) {
// hash value i was found to be true
}
}
}, 1000);
As it sounds like you're doing this on mobile, you should know that polling continuously is not ideal on mobile for a bunch of reasons. First off, it's a battery drain when it's allowed to run. Second off, the mobile device will probably not let it run continuously (as it tries to save your battery).
You could also be a bit more sophisticated about how you store and rather than use N separate keys, you could store JSON in one key that represented an object that contained all the values in one LocalStorage key.

Disallow login to website in multiple browsers/tabs at the same time

I have an ajax heavy website that breaks (or shows incorrect data) when users have it open in multiple browser windows at the same time. So I would like to enforce only allowing the user to be logged in to the website in one tab at a time, whether it is on the same computer or even multiple computers.
I am looking for ideas on how to do this.
Is there any JavaScript method to tell if a certain page is already open in another tab?
Perhaps there is another solution that could involve the server side..
For instance, the client could message the server every say, 1 minute. If the server gets messages from a certain users at a frequency higher than one message per minute, it knows that it is open in more than one window or tab. It can then let one of the clients know that it needs to shout an error to the user.
The idea of messaging the server every one minute does not sit that well with me though.
Any other ideas out there?
EDIT: some people are wondering why I have this problem in the first place. Here it goes:
This is a time tracking application that is fully ajax. You can browse/create/delete/modify timers, projects and clients with ajax, without ever leaving the page. If the website is open in multiple tabs, things will get inconsistent very quickly. Errors usually even occur. For instance, user creates a project and then starts a timer in tab1, tab2 will not show these changes. And since it is all ajax, it will not simply sync when the user clicks some button in the second tab.
Having read the update in your question, what I would really suggest is using WebSocket where available, falling back to Flash socket, long polling and forever iframe for older browsers (actually I'd use Socket.IO to make it all easy - you can use a similar abstraction for whatever environment you are using). That way you can make all of your windows and tabs consistent in real time - problem solved.
That having been said if you don't want to do it for some reason (though what you are trying to do would be a perfect application for WebSockets so think about it) you might use sessionStorage and localStorage to distinguish sessions between tabs or windows for the same logged in user, but it is not widely available yet - see the compatibility table so it would be probably easier to go real-time with a socket.io-like solution where there are a lot of fallbacks available than to restrict visitors to one tab - not to mention the user experience.
There's no way to get information about other tabs/windows in javascript (and for good reason).
The best way I can think to do it would be to print a unique identifier (a timestamp should work reasonably well) in the javascript code for each page, and then it periodically ping the server with that unique ID, and associate it on the server with the user. This way if you have more than one ID belonging to a single user being pinged within a given interval, you can send back a response to the page to warn the user that having multiple tabs open will result in unexpected behavior.
(Like Caspar said above though, you should really figure out why the unexpected behavior is happening and fix that rather than force the user to act a certain way)
This is pretty lo-fi, but I think the simplicity may make it work: you could try having the login open the session in a named window (or change the name of the current window). Then, on load inside the application, check to see if the browser window name is the one you've allowed them to use; if not, pop up an alert, close the window, focus on the named window, if still there. (If not there--i.e., they've already closed the other window--you could let this one stay open, and change the name to the correct name.)
So you're essentially using window.name and window.opener. Rough idea, but an idea.
I have a similar situation and the solution I use is:
on server: at every login you create an unique ID, save it (ex. database) and return it to client.
on client: on every transaction you send this ID to server as a parameter.
on server: if saved and received ID match then allow the request to execute if not refuse it with an error code.
on client: if transaction failed with specific code then you know that "ID" verification failed and you logout user.
So in this way if the same credentials will be used again in any other tab, browser, PC, country,... the old tab will logout user on next transaction request. Or in other words limiting only one opened page per user on the whole world.
Edit:
As I have stopped using html requests for any data communication and use websockets, I register user on server and if same user wants to login from some other location I close the previously used socket (the page automatically logs out).
In this way I also have a way to trigger full page reloads from server in case admin does something that influences users.
Simply use cookies.
$(window).on('beforeunload onbeforeunload', function(){
document.cookie = 'ic_window_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
});
function validateCallCenterTab() {
var win_id_cookie_duration = 10; // in seconds
if (!window.name) {
window.name = Math.random().toString();
}
if (!getCookie('ic_window_id') || window.name === getCookie('ic_window_id')) {
// This means they are using just one tab. Set/clobber the cookie to prolong the tab's validity.
setCookie('ic_window_id', window.name, win_id_cookie_duration);
} else if (getCookie('ic_window_id') !== window.name) {
// this means another browser tab is open, alert them to close the tabs until there is only one remaining
var message = 'You cannot have this website open in multiple tabs. ' +
'Please close them until there is only one remaining. Thanks!';
$('html').html(message);
clearInterval(callCenterInterval);
throw 'Multiple call center tabs error. Program terminating.';
}
}
callCenterInterval = setInterval(validateCallCenterTab, 3000);
}

How to capture user interaction with a website?

How can I capture user interaction on a website? How many links a user has clicked. From where user has come. I want to create my own logic. I don't want to use any statistics tool. How can I accomplish this?
Thanks in advance
Place where user come from you can get by referer (document.referrer).
And if you have some kind of session or mark user(by cookies), than you can check what links are clicked by capturing onclick event. But do not put onclick on every link, just use event capturing technique. In jQuery this will be:
$('a')
.livequery('click', function(event) {
alert('clicked');
return false;
});
If you want to capture what link was clicked when goes away - you should place onunload event which will send data about clicked link to your server.
There are 2 ways that I know of:
make a service, and call it using a GET method on each event you want to track.
this is something like this:
service.php?event=pageview&time=127862936&userId=70&registered=true
this way your service can work with the data.
second way that I know of, which I myself use, is calling to some dummy Image on my server, chaining GET query to it, and then analyze the request to the image at the server side. each request is anylized and logged, then I build reports.
again, you need to know what events you want to grab, they are pre-defined, and need to catch and send them as they happen. you client can put a 1-script js file, but this script need to add events listeners. lets say you want to know when the use has quit the page. add an event listener to the onbeforeunload event, like this:
window.onbeforeunload = function(){
sendStats({event:'onbeforeunload'});
}
then sendStats function breaks down the JSON and builds a query to be sent to server like this:
function sendStats(statsJSON){
var url = [];
for (var key in statsJSON) {
// make sure that the key is an actual property of an object, and doesn't come from the prototype
if( statsJSON.hasOwnProperty(key) ){
var sign = (!url[0]) ? '?' : '&';
url.push(sign);
url.push(key + '=');
url.push( encodeURI(statsJSON[key]) );
}
}
var time = new Date().getTime();
url.push('&time=');
url.push(time);
var stat = new Image();
stat.src = clientHost + 'stats.gif' + url.join('');
}
Start with web server log files, dig into it's format, try some simple stats. Then you may want to read through the code of statistic tools like awstats to enhance your vision on that.
I am a asp .net developer. But i think this technique will work all the time. If you want to find out from where user has come to your site, you can user some sort of tracking querystring variable www.mysite.com?IMFrom=something. So you when you post your link on some third party website for e.g. say Google. Post link as www.mysite.com?google=traficfromgoogle. You might have trafic comming from different otherwebsite. Have different querystring variable for each. You can also use some kind of unique id for all website which is sending trafic to you. Now create the tracking function which will track this querystring variable. Use this function where it will get called during each request.
And you can now put some customized logic for each request having such querystring.
I don't think you'll need to capture this, as it is most likely already captured in web server logs by the web server itself. You just need to find the software that can analyze the logs and give you some nice metrics. There's lots of packages out there for that.
I know its not creating your own logic but if you decide you don't want to parse your server logs you could try a new service that is trying to one up google analytics: http://mixpanel.com/. It's real time analysis and they have a free limited account so you can try it before you upgrade.
I haven't tried their api to get stuff out yet but I imagine that you could let them collect the data from your site and do some fun stuff with it after you get it back out.
Use Google analytics and hook up site elements using their API.
record and replay the web https://www.rrweb.io/
This can be useful for:
- recording user interaction/events in the browser
- sending recordings to your backend only when an error

Categories

Resources