It sounds simple and I think it should be simple, but somehow I don't get it to work...
I want to set a Cookie using Javascript and this Cookie should be removed when the user quits the browser. Setting the cookie and getting the value is not the problem. But when I quit the browser and reopen it, the cookie is still there (Firefox).
I use jQuery and the Cookie-Plugin.
Here is my test code:
$(document).ready(function(){
$('#output').append( '<li>initialize...</li>' );
var $cookieVal = $.cookie('testCookie');
$('#output').append( '<li>check cookie...</li>' );
if(!$cookieVal) {
$('#output').append( '<li>set cookie...</li>' );
$.cookie('testCookie', 'eat cookies', { path: '/' });
//console.log( $.cookie() );
} else {
$('#output').append( '<li>cookie is already set...</li>' );
$('#output').append( '<li>cookie value: '+$.cookie('testCookie')+'</li>' );
}
});
Please find the working example at jsFiddle.
I am beginning to wonder if your testing method might be the problem here. So, I am going to write this in a specific way.
Actual Answer: Browser Setting
In Firefox, Options>General>When Firefox starts>"Show my windows and tabs from last time" is going to preserve your previous session. Change this setting to see that this is indeed working as it is supposed to. Firefox is prolonging your session. For further information, see this "bug": http://bugzilla.mozilla.org/show_bug.cgi?id=530594
There are similar settings in most browsers that probably behave the same way.
Original Answer:
I created a fiddle, http://jsfiddle.net/8Ahg2/ that uses document.cookie rather than jquery cookie plugin. Here is how you test this. (source below)
copy the following URL to your clipboard: http://fiddle.jshell.net/8Ahg2/show/
Completely close your browser of choice - this should be browser independent.
Open your browser, paste the url. The first time it should say: check cookie...
set cookie...
Refresh the page, notice that it should now say the value of the cookie ("test")
Close your browser completely again.
Navigate to the URL that should still be in your clipboard. *Do not refresh the page on the first view, it should again say 'check cookie...
set cookie...'
js
$(document).ready(function () {
$('#output').append('<li>initialize...</li>');
//this regex gets the "name" cookie out of the string of cookies that look like this: "name=test;var2=hello;var3=world"
var cookieVal = document.cookie.replace(/(?:(?:^|.*;\s*)name\s*\=\s*([^;]*).*$)|^.*$/, "$1");
$('#output').append('<li>check cookie...</li>');
if (!cookieVal) {
$('#output').append('<li>set cookie...</li>');
document.cookie = "name=test";
} else {
$('#output').append('<li>cookie is already set...</li>');
$('#output').append('<li>cookie value: ' + cookieVal + '</li>');
}
});
There is some code that worked for me. It should expire when you close the browser because of the date to expire being before now:
var vEnd = new Date();
vEnd.setDate(vEnd.getDate() - 1);
var endOfCookieText = "; expires=" + vEnd.toGMTString() + "; path=/";
document.cookie = escape('testCookie') + "=" + escape("eat cookies") + endOfCookieText;
FIDDLE MODIFIED
Note that the fiddle gives a bunch of load errors on the console for me.
Related
We are trying to set a cookie in order to use user auto login.
We are using an SPA with Reactjs + Redux + JavaScript (ES6),
To set the cookie we have created a component called CookieHandler which contains the set cookie function
setCookie(token = '', expirationDate = '1970-01-01T00:00:00') {
const expDay = new Date(expirationDate);
document.cookie = 'userToken=' + token + '; expires=' + expDay.toUTCString() + '; path=/;';
}
We also made sure that it's called once, just when is needed.
This works for all browsers expect in Internet explorer.
The problem is that IE sets the token in the current session but once we close the window and re-open it the cookie is gone, I also have tried to use toGMTString (which is deprecated) instead of toUTCString but still not working
Extra
We get the userToken and expirationDate from the back-end which its format is the same as the default value in the setCookie function
In all the other browsers works as expected even though we close the window.
Here I found some info that IE doesn't like "=" sign.
Maybe it's the problem?
Don't use "=" signs in your cookie names.
If you need them, use the single quotes to tell IE not to interpret it, but to accept it as a literal.
For example i have a <P>tag contain a class as below
<p class="badge">new</p>
and i do like to add some CSS for the element, when the user .onclick(), so i created a function like below
$(".badge").click(function(){
$(".badge").css("display","none");
});
And the question is how may i use cookies to remember that the user had already clicked before, so the css will be added automatically?
You're better off using window.localStorage
$(".badge").click(function(){
$(".badge").css("display","none");
localStorage.setItem('btnClicked', true);
});
And on document load you should check if the user has clicked the button before and act accordingly:
$(document).ready(function (){
var clicked = localStorage.getItem("btnClicked");
if(clicked){
$(".badge").css("display","none");
}
});
You could use the jQuery cookie library:-
Create expiring cookie, 7 days from then:
$.cookie('name', 'value', { expires: 7 });
Read cookie:
$.cookie('name'); // => "value"
So your code could work like:-
$(".badge").click(function(){
$(".badge").css("display","none");
$.cookie('hide-badge', true, { expires: 7 });
});
$(function(){
if($.cookie('hide-badge'))
$(".badge").css("display","none");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<p class="badge">new</p>
If you want to use cookies, try this sample:
$(function() {
function getCookie(name) {
var matches = document.cookie.match(new RegExp(
"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
));
return matches ? decodeURIComponent(matches[1]) : undefined;
}
$('.badge').click(function(){
var
cookieValue = +getCookie('counter'); // get cookie
$('.badge').css('display' ,'none');
document.cookie = 'counter=' + (cookieValue + 1); // setting a new value
});
});
But, as noticed #guradio, you can use localStorage to do that:
$(function() {
$('.badge').click(function(){
var
counter = +localStorage['counter']; // get counter's value from localStorage
$('.badge').css('display' ,'none');
localStorage['counter'] = (counter + 1) // setting a new value
});
});
localStorage makes your code more compact an elegant :)
The Web Storage API provides mechanisms by which browsers can store
key/value pairs, in a much more intuitive fashion than using cookies.
The two mechanisms within Web Storage are as follows:
sessionStorage maintains a separate storage area for each given origin that's available for the duration of the page session (as long
as the browser is open, including page reloads and restores)
localStorage does the same thing, but persists even when the browser is closed and reopened.
sessionStorage is similar to localStorage, the only difference is while data stored in localStorage has no expiration set, data stored in sessionStorage gets cleared when the page session ends.
This is a follow up to my original question here. The answers proposed work in Firefox, but there are problems with at least Chrome and Safari (on iOS).
The initial issue is this: on an unrelated site (say Facebook), users can create links where the href is in the form http//www.siteA.com/?http://www.siteB.com. The intention is that siteA parses the querystring and re-directs the browser to siteB. That all works fine.
Then, when a user, having been re-directed to siteB, clicks the back button on their browser, the goal is that they should return to siteA and not be re-directed again.
The answer to my previous question proposed that at the time of the re-direction from siteA, the code on siteA checks for a cookie - if it is not there, it sets it and re-directs. If it is there, then no redirection. In order to allow the user to return to the original referring page and click the same link again (and be re-directed to siteB), it was also proposed that if the cookie is found on siteA, as well as no re-direction, the cookie is deleted.
On Firefox that all works. The 2 problems now are:
on Chrome (and maybe others), the cookie deletion either doesn't work, or works only after the user navigates to another site. The deletion code is just simple javascript, setting the same cookie with an expiry date in the past. This may in practice be a relatively minor issue, but it would be nice to find a solution.
on Safari on iOS, siteA is not in the browser history. It seems iOS (and maybe Safari generally), tries to avoid the looping problem) of returning to a page that re-directed to a second site), by omitting the re-directing page from the history stack. As a result, the pressing the back button on siteB goes to the page prior to the re-directing page. This is a major issue.
It seems there are 3 possibilities - what I want to do is not possible because it's a security risk; there's no crosss-browser/platform solution; or I've approached the goal by completely the wrong method.
The fixed points are:
the form of the URL (with a querystring containing the second URL);
no access to the server (limited to javascript/jquery).
no control over siteB (only siteA).
I'd be grateful for any suggestions and/or advice.
Thanks
This appears to be a solution to issue 2:
$(document).ready(function() {
var s = location.search;
if(s != '') {
var split = s.split('?');
var loc = split[1].replace('?', '');
if (document.cookie.indexOf('redirected=' + loc + '') == -1) {
document.cookie = 'redirected=' + loc + '';
var link = document.createElement('a');
link.href = loc;
document.body.appendChild(link);
link.click();
} else {
document.cookie = 'redirected=' + loc + '; expires=Thu, 01 Jan 1970 00:00:00 GMT';
var url = location.href.replace('' + s + '', '');
location.href = '' + url + '';
}
} else
{
//do something on the re-direction page
}
});
It's a bit old school, but instead of re-directing, you create a link on the intermediate page and click it programmatically. That works like a re-direction, but leaves the re-directing page in the history stack, even on iOS.
Thanks to this answer on SO for the hint.
Still looking for a way to remove the cookie more effectively though.
I'd be interested and grateful to read any other comments on these issues. Thanks.
I want users to browse my site from only one tab in their browser. How can this be done? Would I use javascript and cookies?
For example, I have a website: www.example.com - and I want my clients to only be able to visit the site from one single tab in one browser. If they open another tab and load the site (or a subpage of the site) - I want an alert "Can't open multiple instances", and then redirect them to an error page.
Once thing to note - if the user changes the address from www.example.com/action/door/mine.aspx to www.example.com - that should work fine, because the user is in the same (original) tab.
Any help will be appreciated. Thanks in advance.
I've created a simple solution for this. The master page layout creates a tab GUID and stores it in sessionStorage area of the tab. The using an event listener on the storage area I write the tab GUID to the sites localStorage area. The listener then compares the tabs GUID to the one written to site storage and if they differ then it knows more than one tab is open.
So if I have three tabs A,B,C then click something in tab C, tab A and B detect another tab is open and warn user of this. I haven't yet got to fixing it so the last tab used get's notification, work in progress.
Here's the JS I have in master page, plus in the login page I have a localStorage.Clear to clear last tab from previous session.
// multi tab detection
function register_tab_GUID() {
// detect local storage available
if (typeof (Storage) !== "undefined") {
// get (set if not) tab GUID and store in tab session
if (sessionStorage["tabGUID"] == null) sessionStorage["tabGUID"] = tab_GUID();
var guid = sessionStorage["tabGUID"];
// add eventlistener to local storage
window.addEventListener("storage", storage_Handler, false);
// set tab GUID in local storage
localStorage["tabGUID"] = guid;
}
}
function storage_Handler(e) {
// if tabGUID does not match then more than one tab and GUID
if (e.key == 'tabGUID') {
if (e.oldValue != e.newValue) tab_Warning();
}
}
function tab_GUID() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
function tab_Warning() {
alert("Another tab is open!");
}
Note: It's IE9+
Hope this helps.
UPDATE - 2020
Client side implementation:
We can make use of Broadcast Channel API which allows communication across browsing contexts (windows, tabs, frames or iframes) provided both contexts are from same origin.
A simple implementation to detect 2nd tab loading the website from the 1st tab:
//in entry point of your app (index.js)
const channel = new BroadcastChannel('tab');
channel.postMessage('another-tab');
// note that listener is added after posting the message
channel.addEventListener('message', (msg) => {
if (msg.data === 'another-tab') {
// message received from 2nd tab
alert('Cannot open multiple instances');
}
});
This doesn't use localStorage or cookies and it even works if 1st tab is offline and 2nd tab is being loaded.
Note: This is not supported in Safari & IE11 yet :(
UPDATE - 2022
From March 2022, it is now officially supported on Safari 🥳
Take a note on its browser compatibility.
However, there's a polyfill available that does the job.
EDIT2:
It's the exact thing which is mentioned at this answer, You need 2 IDs:
One random one
One consistent one (this will be our SSID actually, since you limit tabs of a single browser, it's better to get generated form browser's unique parameters)
You can generate consistent one from browser's user-agent or get it from server-side. store both of them server-side.
Store the random one in window.name property which is tab-specific.
Send a heartbeat every 1~2 seconds to your server containing both consistent ID and random one. if server fails to receive the heartbeat, it cleans up database and de-register dead clients.
on every browser's request, check window.name for the value. if it were missing, check with the server-side whether if the previous tab is closed or not (cleaned from database).
If yes, generate a new pair for client if no, reject them.
Two suggestions on top of my mind:
Server-side (better): provide all your clients, a user name and password. request them on their first visit of your site to enter with their credentials. then on every other request, check for whether user with said credentials is already logged in or not.
Client *
|
|
Server ---> Check whether
Already logged
or not?
______________
| |
yes no
| |
permit reject
them them
Client-side: If you really need a strong check of this, use evercookie to store an already-logged-in cookie on client's machine.
Side-note: Do know that every attempt in client side is not secure at all! client-side should help server-side, it shouldn't be used as the one and only source of security. even evercookies can be deleted so, give my first suggestion a go.
**EDIT:**
Evercookie is really doing a good job at storing most secure zombie cookies ever but since the library itself is a little bit heavy for browsers (storing a cookie takes more than 100ms each time) it's not really recommended for using in real-world web app.
use these instead if you went with server-side solution:
Way around ASP.NET session being shared across multiple tab windows
Kiranvj's answer
Extending rehman_00001's answer to handle the case where you want the alert on the new tabs instead.
const channel = new BroadcastChannel('tab');
let isOriginal = true;
channel.postMessage('another-tab');
// note that listener is added after posting the message
channel.addEventListener('message', (msg) => {
if (msg.data === 'another-tab' && isOriginal) {
// message received from 2nd tab
// reply to all new tabs that the website is already open
channel.postMessage('already-open');
}
if (msg.data === 'already-open') {
isOriginal = false;
// message received from original tab
// replace this with whatever logic you need
alert('Cannot open multiple instances');
}
});
I know this post is pretty old, but in case it helps anybody, I recently looked into basically doing the same thing using localStorage and sessionStorage.
Similar Anthony's answer, it sets an interval to make sure the originating tab keeps the entry fresh, so that if the browser crashes or somehow closes without calling the unload event (included in the comments but not part of the code for testing purposes), then there would just be a short delay before the application would run properly in a new browser window.
Obviously, you would change the "tab is good", "tab is bad" conditions to do whatever logic you want.
Oh, and also, the createGUID method is just a utility to make the session identifier unique... it is from this answer to a previous question (wanted to make sure I wasn't taking credit for that).
https://jsfiddle.net/yex8k2ts/30/
let localStorageTimeout = 15 * 1000; // 15,000 milliseconds = 15 seconds.
let localStorageResetInterval = 10 * 1000; // 10,000 milliseconds = 10 seconds.
let localStorageTabKey = 'test-application-browser-tab';
let sessionStorageGuidKey = 'browser-tab-guid';
function createGUID() {
let guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
/*eslint-disable*/
let r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
/*eslint-enable*/
return v.toString(16);
});
return guid;
}
/**
* Compare our tab identifier associated with this session (particular tab)
* with that of one that is in localStorage (the active one for this browser).
* This browser tab is good if any of the following are true:
* 1. There is no localStorage Guid yet (first browser tab).
* 2. The localStorage Guid matches the session Guid. Same tab, refreshed.
* 3. The localStorage timeout period has ended.
*
* If our current session is the correct active one, an interval will continue
* to re-insert the localStorage value with an updated timestamp.
*
* Another thing, that should be done (so you can open a tab within 15 seconds of closing it) would be to do the following (or hook onto an existing onunload method):
* window.onunload = () => {
localStorage.removeItem(localStorageTabKey);
};
*/
function testTab() {
let sessionGuid = sessionStorage.getItem(sessionStorageGuidKey) || createGUID();
let tabObj = JSON.parse(localStorage.getItem(localStorageTabKey)) || null;
sessionStorage.setItem(sessionStorageGuidKey, sessionGuid);
// If no or stale tab object, our session is the winner. If the guid matches, ours is still the winner
if (tabObj === null || (tabObj.timestamp < new Date().getTime() - localStorageTimeout) || tabObj.guid === sessionGuid) {
function setTabObj() {
let newTabObj = {
guid: sessionGuid,
timestamp: new Date().getTime()
};
localStorage.setItem(localStorageTabKey, JSON.stringify(newTabObj));
}
setTabObj();
setInterval(setTabObj, localStorageResetInterval);
return true;
} else {
// An active tab is already open that does not match our session guid.
return false;
}
}
if (testTab()) {
document.getElementById('result').innerHTML = 'tab is good';
} else {
document.getElementById('result').innerHTML = 'tab is bad';
}
window.addEventListener('load', function () {
if (localStorage.getItem('web_browser') == null) {
// new tab
localStorage.setItem('web_browser', 'true');
window.addEventListener('unload', function() {
localStorage.removeItem('web_browser');
})
} else {
// duplicate tab
return;
}
})
Put this script at the beginning of html pages, where you don't want users to duplicate current page or tab.
The same problem (and solution) : https://sites.google.com/site/sarittechworld/track-client-windows
Similar :
http://www.codeproject.com/Articles/35859/Detect-and-prevent-multiple-windows-or-tab-usage-i
The best way to solve this is to have one-time session IDs.
Eg, each page contain a session ID, that is valid for one visit, is unique, and random.
When clicking any one link, it will use & invalidate the session ID, and the new page will have a new session ID.
This will force the user to always browse in the newest window or tab, and also prevents session stealing over the wire.
Any attempt to reuse a old session ID should immediately kill also the active session IDs for that user.
Its also important to store, in the session management system, which pages is accessible from page X. So if page X (with session ID abc) contains links to page 1, 2 and 3, any attempt to visit page 4 with session ID abc, will fail and also kill the session.
This will force the user to always have one single session track, and always follow the logic on the site. Any attempt to go forward, back, using history or log entires, or opening multiple windows or tabs, will fail and logout the user in all windows, tabs and devices.
All this can be completely implemented on server-side, without any client-side logic.
Why do you want to do this?
Could try to do some ugly hacking, but the result would be: There is no way you could completely suppress this behaviour.
This could not be solved by JavaScript, because there is always the possibility that the user has disabled JavaScript in his browser, or allows only a certain subset.
The user could open a new browser, use a different computer, etc. to visit multiple pages at once.
But more important:
Also, your site would be the only site that has this behaviour and for this reason this will confuse everybody which uses your site, because it doesn't work like a web site should work. Everybody who tries to open a second tab will think: "This is odd. This website sucks because it different then websites should be. I will not come again!" ;-)
I wrote this to stop a call center page from being accessed in multiple tabs. It works well and is purely client-side. Just update the else if part to do what you want if it detects a new tab.
// helper function to set cookies
function setCookie(cname, cvalue, seconds) {
var d = new Date();
d.setTime(d.getTime() + (seconds * 1000));
var expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
// helper function to get a cookie
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
// Do not allow multiple call center tabs
if (~window.location.hash.indexOf('#admin/callcenter')) {
$(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);
}
I'm using the following code for setting/getting deleting cookies:
function get_cookie(cookie_name)
{
var results = document.cookie.match('(^|;) ?' + cookie_name + '=([^;]*)(;|$)');
if (results)
return ( decodeURI(results[2]) );
else
return null;
}
function set_cookie(name, value, exp_y, exp_m, exp_d, path, domain, secure)
{
var cookie_string = name + "=" + encodeURI(value);
if (exp_y)
{
var expires = new Date(exp_y, exp_m, exp_d);
cookie_string += "; expires=" + expires.toGMTString();
}
if (path)
cookie_string += "; path=" + encodeURI(path);
if (domain)
cookie_string += "; domain=" + encodeURI(domain);
if (secure)
cookie_string += "; secure";
document.cookie = cookie_string;
}
function delete_cookie(cookie_name)
{
var cookie_date = new Date(); // current date & time
cookie_date.setTime(cookie_date.getTime() - 1);
document.cookie = cookie_name += "=; expires=" + cookie_date.toGMTString();
}
but i am getting inconsistent results.
for example, a cookie set on the startpage (www.example.com/start) , will not always show up on a subsequent page (www.example.com/foo/thing.jsp).
i am setting a cookie "onUnload" of the page using
set_cookie("beginrequest", (new Date()).getTime(), null, null, null, "/");
and retrieving + deleting it "onLoad" via
loadDur = (new Date()).getTime() - get_cookie("beginrequest");
delete_cookie("beginrequest");
to measure the total amount of time the page took to load.
when using firebug, i often see "leftover" beginrequest-cookies and multiple instances of beginrequest with past timestamps.
how can i achieve to see just one beginrequest-cookie on every page?
If you're getting old cookies that might be because your page contains a lot of content and onload isn't called before onunload (because the page doesn't finish loading). So delete the cookie by calling something like this from both onload and onunload:
var deleted_cookie = false;
function delete_timestamp() {
if(!deleted_cookie) delete_cookie("beginrequest");
deleted_cookie = true;
}
You might also have a race condition if you're loading the next page quick enough that the 'delete_cookie' cookie hasn't expired properly, and your get_cookie implementation is picking that up. So try changing the regular expression in get_cookie to only pick up cookies with a value:
var results = document.cookie.match('(^|;) ?' + cookie_name + '=([^;]+)(;|$)');
Also, if you're viewing the site in more than one window (or tab), their cookies can get mixed up, so don't do that. But try using a global regular expression to pick up all the values, and only using the latest one.
Echoing the other's suggestion to do some of the work on the server side - I've done this in the past:
1) Capture the time of request on the server side, and include a script tag with the time variable in the page:
<script type="text/javascript"> var start = 1224068624230;</script>
2) At the end of the page, in JavaScript, get a new time and calculate the total time.
Your code for set_cookie, get_cookie and delete_cookie seems to be correct. And your usage as well.
I think you should move this into your Java code - for me it seems an easier option than to hack this via cookies.
i agree with amix on both counts: your code looks ok at first glance, and this would probably be better handled by something on the server side.
regardless, at a guess, i'd say the issue you're running into at the moment is likely that the events aren't firing the way that you think they are. two ways you can clear up what's happening, are by making it visually obvious that the event is firing , and by increasing the verbosity of your output. any number of things could be interfering with the events actually firing: plugins, addons, keypresses, etc. if you open a new window (or an alert, or whatever) when the onload and onunload events fire, you'll be able to tell for certain that the functions are being called. likewise, if you store more data in the cookies, like originating URL, etc, you'll be able to better figure out which pages are generating the extra cookies.
i'd guess that one of your Firefox extensions is intermittently interfering with the onunload event.