Using localstorage checks in a different window - javascript

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.

Related

Can website owner see what client run in console?

I have a refresh script, which clicks automatically in every 5 second on a button.
It looks something like this:
setInterval(function() {
document.getElementsByClassName("buttonclass")[0].click();
}, 5000);
If I put this script into inspect element/console and I run it can the website owner see it?
Can the owner of the site watch what kind of scripts is running in console, which was injected by user/client to the website?
If yes, how can the owner of the website see it?
I probably think the answer here is "it depends on what you do, but usually not". You can talk about these things for hours but put simply: when you "go" to a website you make a HTTP-Request to a web-server which responds with all kinds of data. Your browser uses this data to render the website. The changes you make, or running scripts, etc., happens in your browser. If you change a buttons background-color for example, this change only happens at your machine. But technically speaking, a website could be coded in a way, that every change to the DOM is going to be reported back to the backend (for example via fetch), so it is possible that, for example, a website could track how many times in a second you press a button or something like that. The things you enter in the console though, unless its not interacting with the DOM or any other script, can never be read by the owner, because it has nothing to do with the website, but with your browser (for example, if you type "1+1" in the console, the owner isn't going to see that).
Apart from the fact that it is somewhat technically possible to track some, but not all activity or changes a user makes to a website, by default, if not coded that way, the owner of the website can't see what you are doing with the version of the website that you requested.
JavaScript doesn't provide an API that allows web pages to view everything that is going on in the Console. So to answer the question in your title: no, that is not possible. At least, not directly.
Theoretically there are plenty of ways to detect if specific code is ran. For instance, you can wrap the native setInterval function and sent some AJAX request to the server when someone triggers it.
Here is a small example:
(function() {
const nativeSetInterval = window.setInterval;
window.setInterval = (...args) => {
console.log("Someone ran the setInterval function...");
nativeSetInterval(...args);
}
})();
Open the console, be sure to select the right (stacksnippets.net) frame, and execute <code>setInterval<code>.
<code>setInterval(() => console.log('I run every second.'), 1000);</code>
This is however very unlikely to be the case. What is more likely, is that clicking that button triggers some call to the server of the website, which has some logging. If the logging shows that some action is performed exactly every 5 seconds for an extended period of time, they will easily come to the conclusion that it wasn't done manually.
As far as I know you cant directly see if a user is inputting and using the developer console, that said if you click a button every 5 seconds you could detect that kind of behaviour. So directly you cant see what and if people input thigns in the dev console but indirect you can in some cases

Is it possible to get a tab (or window) identifier that stays the same across page reloads?

Question:
Is there an api or way to determine some kind of an id/uuid of the current tab/window the page is displayed in that is stable when the page is refreshed?
To give you more context why this is important to me:
My use case is to allow my single page app (SPA) to be opened in multiple tabs at the same time.
Right now I can't do it because while the current state is persisted in a "currentModel" property in localStorage, unfortunately not all urls of the SPA are "self-contained" . By "self-contained" I mean that the url itself should be sufficient to reload that state from the backend (or localStorage) because it contains some kind of id/uuid (this flaw of my SPA has grown historically and I know it's not what HTML was meant to be used, however as there's a team of 60+ developers working on that SPA you can imagine that this cannot be refactored in a single day).
So what I have are some urls like this "/search" that display a search form, but also information about the current context (i.e. think business object).
Now if I want to allow the SPA to be opened simultaneously in multiple tabs, I can no longer use a simple "currentModel" property. Instead I need to make it a hashmap with the keys being tab/window ids so that the context of a tab can be restored in case the user hits F5/refresh in one of the tabs. Of course this id whatsoever would need to be stable across page reloads.
Workarounds that I know of
Writing a custom browser extension that has access to tabs
Updating the url to include a "tab"-id created when the SPA is bootstrapped
The window.name property (at least in modern Firefox and Chrome) maintains its value across an F5 page reload. Thus, code can check window.name for navigational hints or whatever else might be useful in order for it to "get its bearings" in the absence of information in the URL itself.
Thus, a newly-loaded tab will be able to detect that it is in fact newly loaded, and can insert and update the name property of the window whenever it wants. Upon a reload, code can sniff the name and request additional stuff from the server (or do whatever) if it sees that sort of information already present in the window name.
I have been able to put any sort of string into that property, in quite limited tests, but it's possible IE is picky about the name having to have some particular form (like a valid identifier or something).
You may still be able to leverage localStorage but instead of keeping one object representing your state, maybe you should keep state on a route-by-route basis?
const state = {
"/": { state: {} },
"/page-2": { state: {} }
}
// persist state
localStorage.setItem('state', JSON.stringify(state))
// unwrap state for route /
const routeState = (JSON.parse(localStorage.getItem('state')))['/']
You can try leveraging the window.onbeforeunload action to save state for a page when a user refreshes the page. You could do something like:
window.onbeforeunload = function(event) {
localStorage.setItem('lastState', JSON.stringify(state))
}
And then when a page reloads, check if state exists in localStorage, load it, and remove it. This method assumes that users won't be reloading pages and opening new ones at the same time. Otherwise, you'd need some other identifier when saving the state which puts you back in your original predicament.

What purpose is of "&rnd=" parameter in http requests?

Why do some web-applications use the http-get parameter rnd? What is the purpose of it? What problems are solved by using this parameter?
This could be to make sure the page/image/whatever isn't taken from the user's cache. If the link is different every time then the browser will get it from the server rather than from the cache, ensuring it's the latest version.
It could also be to track people's progress through the site. Best explained with a little story:
A user visits example.com. All the links are given the same random number (let's say 4).
The user opens a link in a new window/tab, and the link is page2.php?rnd=4. All the links in this page are given the random number 7.
The user can click the link to page3.php from the original tab or the new one, and the analytics software on the server can tell which one by whether it has rnd=4 or rnd=7.
All we can do is suggest possibilities though. There's no one standard reason to put rnd= in a URL, and we can't know the website designer's motives without seeing the server software.
Internet Explorer and other browsers will read an image URL, download the image, and store it in a cache.
If your application is going to be updating the image regular, and so you want your users to not see a cached image, the URL needs to be unique each time.
Therefore, adding a random string ensures this will be unique and downloaded into the cache each time.
It's almost always for cache-busting.
As has been suggested by others. This kind of behaviour is usually used to avoid caching issues when you are calling a page that returns dynamic content data.
For example, say you have a page that gets some current user information such as "mysite.com/CurrentUserData". Now on the first call to this page, the user data will be returned as expected, but depending on the timing and caching settings, the second call may return the same data - even though the expected data may have been updated.
The main reason for caching is of course to optimise the speed of frequent request. But in the instance where this is not wanted, adding a random value as a query string parameter is known to be a widely used solution.
There are however other ways to get around this issue. For example if you were doing an Ajax request with javascript/JQuery. You could set the cache to false in your call...
$.ajax({url: 'page.html', cache: false});
you could also change it for all page calls on document load with...
$.ajaxSetup({cache: false}});
If you were to do an MVC application, you can even disable the caching on the control action methods with an attribute like so...
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult NonCacheableData()
{
return View();
}
(thanks to a quick copy and paste from here)
I dare say there are also settings in IIS you could apply to get the same affect - though I have not been that far with this yet.

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);
}

JavaScript: sharing data between tabs

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)
};

Categories

Resources