34

My goal is remove user cookies when browser tab closed.

Is it possible? Can I handle browser tab close event without refresh case?

If I use beforeunload or unload events, function triggered when user refresh page, I don't want this, I want just run when closed tab.

How can I do this in Angular?

Furkan Başaran
  • 1,927
  • 2
  • 19
  • 28
  • Try this once: angular.element($window).bind("beforeunload", methodToCall) – Ved Dec 07 '15 at 08:51
  • I tried that in .run() but not seem works. – Furkan Başaran Dec 07 '15 at 09:07
  • what is the problem you faced with this code.? – Ved Dec 07 '15 at 09:16
  • Not triggered when I close tab – Furkan Başaran Dec 07 '15 at 09:18
  • 1
    You can only detect when the page is unloaded, not when the window is closed. Also, the onbeforeunload is non-standard, so it's not supported by all browsers. – Ved Dec 07 '15 at 09:30
  • 1
    http://stackoverflow.com/questions/1783302/clear-cookies-on-browser-close says cookies are cleared when the browser is closed anyhow. Just don't set an expiration on them. – flcoder Dec 17 '15 at 03:03
  • @flcoder, Right I believe it is what I would do. Thanks for pointing out. I believe this is the easiest solution for cookies. The use case for cookies are right however for something like localstorage-cache does not work with this. Anything here that you might want to add? Dont want to add a new question for related issue. – Gary Dec 17 '15 at 15:41
  • @Gary, unless you're (for some reason) concerned about malicious access to the data in localstorage, here's what I would do... at the beginning of your session, i.e., the first time the user goes to the page since they opened the browser, check cookie session_started, if it doesn't exist, you got a new session, clear whatever old data you were saving in localstorage and start fresh, also set session_started=true. – flcoder Dec 18 '15 at 08:34

7 Answers7

20

This is, tragically, not a simple problem to solve. But it can be done. The answer below is amalgamated from many different SO answers.

Simple Part: Knowning that the window is being destroyed. You can use the onunload event handle to detect this.

Tricky Part:

Detecting if it's a refresh, link follow or the desired window close event. Removing link follows and form submissions is easy enough:

var inFormOrLink;
$('a').live('click', function() { inFormOrLink = true; });
$('form').bind('submit', function() { inFormOrLink = true; });

$(window).bind('beforeunload', function(eventObject) {
    var returnValue = undefined;
    if (! inFormOrLink) {
        returnValue = "Do you really want to close?";
    }
    eventObject.returnValue = returnValue;
    return returnValue;
}); 

Poor-man's Solution

Checking event.clientY or event.clientX to determine what was clicked to fire the event.

function doUnload(){
 if (window.event.clientX < 0 && window.event.clientY < 0){
   alert("Window closed");
 }
 else{
   alert("Window refreshed");
 }
}

Y-Axis doesn't work cause it's negative for clicks on reload or tab/window close buttons, and positive when keyboard shortcuts are used to reload (e.g. F5, Ctrl-R, ...) and window closing (e.g. Alt-F4). X-Axis is not useful since different browsers have differing button placements. However, if you're limited, then running the event coordinates thru a series of if-elses might be your best bet. Beware that this is certainly not reliable.

Involved Solution

(Taken from Julien Kronegg) Using HTML5's local storage and client/server AJAX communication. Caveat: This approach is limited to the browsers which support HTML5 local storage.

On your page, add an onunload to the window to the following handler

function myUnload(event) {
    if (window.localStorage) {
        // flag the page as being unloading
        window.localStorage['myUnloadEventFlag']=new Date().getTime();
    }

    // notify the server that we want to disconnect the user in a few seconds (I used 5 seconds)
    askServerToDisconnectUserInAFewSeconds(); // synchronous AJAX call
}

Then add a onloadon the body to the following handler

function myLoad(event) {
    if (window.localStorage) {
        var t0 = Number(window.localStorage['myUnloadEventFlag']);
        if (isNaN(t0)) t0=0;
        var t1=new Date().getTime();
        var duration=t1-t0;
        if (duration<10*1000) {
            // less than 10 seconds since the previous Unload event => it's a browser reload (so cancel the disconnection request)
            askServerToCancelDisconnectionRequest(); // asynchronous AJAX call
        } else {
            // last unload event was for a tab/window close => do whatever
        }
    }
} 

On the server, collect the disconnection requests in a list and set a timer thread which inspects the list at regular intervals (I used every 20 seconds). Once a disconnection request timeout (i.e. the 5 seconds are gone), disconnect the user from the server. If a disconnection request cancelation is received in the meantime, the corresponding disconnection request is removed from the list, so that the user will not be disconnected.

This approach is also applicable if you want to differentiate between tab/window close event and followed links or submitted form. You just need to put the two event handlers on every page which contains links and forms and on every link/form landing page.

Closing Comments (pun intended):

Since you want to remove cookies when the window is closed, I'm assuming it's to prevent them from being used in a future session. If that's a correct assumption, the approach described above will work well. You keep the invalidated cookie on the server (once client is disconnected), when the client creates a new session, the JS will send the cookie over by default, at which point you know it's from an older session, delete it and optionally provide a new one to be set on the client.

Community
  • 1
  • 1
vvohra87
  • 5,594
  • 4
  • 22
  • 34
7
$window.addEventListener("beforeunload", function (e) {
  var confirmationMessage = "\o/";
  console.log("closing the tab so do your small interval actions here like cookie removal etc but you cannot stop customer from closing");
  (e || window.event).returnValue = confirmationMessage; //Gecko + IE
  return confirmationMessage;                            //Webkit, Safari, Chrome
});

This is quite debated on whether we can do it or not in a fool proof manner. But technically not a lot of things can be done here since the time you have is quite less and if customer closes it before your actions complete you are in trouble, and you are at mercy of the client completely. Dont forget to inject $window. I advice not to depend on it.

javascript detect browser close tab/close browser

Update: I was researching this issue along with some recommendations. And apart from this above option, the best way you can handle such cases is by creating sessions with no activity timeout of may be 25-30 mins. Convert all your cookies that need to be destroyed into sessions with timeout. Alternately, you can set cookie expiry but then the cookie stays in the browser until next login. Less than 25-30 mins of session inactivity is not completely dependable since you can get logged out if the client is not using the browser for 10-15 mins. I even tried the capturing events from link (<a>) or (<submit>) or (keypress) based events. The problem is it handles page refresh well. But it does not remove the problem of client closing the browser or browser tab before your cookies are deleted. It is something you have to consider in your use case and forcefully trade off due to no control on it. Better to change the design on its dependence to a better architecture than to face mentioned problems later.

Community
  • 1
  • 1
Gary
  • 2,293
  • 2
  • 25
  • 47
3

I've just implemented a (relatively) simple solution to this that's not covered above.

HTML:

<div
  (window:beforeunload)="doBeforeUnload()"
  (window:unload)="doUnload()"
>
  ... your code here ...
</div>

TS:

  doBeforeUnload() {
    if (document.visibilityState === 'hidden') {
      this.tabWasClosed = true;
    }

    return false;
  }

  doUnload() {
    if (this.tabWasClosed) {
      ... perform logout action ...
    }
  }

The logic I found to the beforeunload and unload events was:

Value for document.visibilityState:

beforeunload unload
tab closed 'hidden' 'hidden'
tab refreshed 'visible' 'hidden'

So you can use the visibilityState with the beforeunload event to differentiate whether the user refreshed or closed the tab. I used some additional localStorage elements to check logout state but that's maybe not needed for anyone coming to this issue after 5 years.

More on document.visibilityState on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState

Kev
  • 31
  • 2
1

If your goal is to remove cookies when the browser is closed, don't set expiration. Cookies without expiration are removed when the browser closes.

You really should never need to know if a user closes their browser or not. I'm confident there are alternative ways to accomplish whatever it is you're wanting to do.

The Bottom Line:

If a visitor is not on your page anymore, it really is none of your business what the user is doing with their browser or their computer. So detecting browser close will likely never be implemented without some kind of opting-in from the user, e.g., browser extensions/plugins.

flcoder
  • 713
  • 4
  • 14
1

I know it's been a while since this question was asked, but I thought I'd contribute my answer as well. The best way I found to reload the page without losing cookies, and remove cookies on window or tab close was to use ng-keydown to watch the F5 Keypress (KeyCode 116).

HTML

<body ng-controller="exitController" ng-keydown="isRefresh($event)">

CONTROLLER

yourApp.controller('exitController', ['$rootScope', '$scope', '$window', '$cookies', function($rootScope, $scope, $window, $cookies) {
//set refresh to false to start out, it will become true only when we hit the refresh page
$scope.refresh = false;

//This is where we'll actually clear our cookies in the event of a window close
$scope.clearCookies = function() {
    $cookies.remove('sessionData');
    $cookies.remove('ss-id');
    $cookies.remove('ss-pid');
};

//When the user types any key, it will be assessed.
$scope.isRefresh = function(e) {
    var keyCode = e.keyCode; //find the key that was just pressed
    if(keyCode === 116) { //if the key that was pressed is F5, then we know it was a refresh
        $scope.refresh = true;
    }
}

//event that handles all refresh requests
window.onbeforeunload = function (e) {
    if (!$scope.refresh) { //as long as the trigger for the refresh wasnt initiated by F5, we remove the cookies
        $scope.clearCookies();
    }
};

}]);

0

Ref: https://www.techiediaries.com/angular-leave-browser-beforeunload-event/

The best way by uisng a HostListener for window:beforeunload in your app component or the component you are targeting for tab close event

@HostListener('window:beforeunload')
  onBeforeUnload() {
    return false;
}
Amit Singh Rawat
  • 559
  • 1
  • 9
  • 27