我的目标是在浏览器选项卡关闭时删除用户 cookie。
是否可以?我可以在没有刷新的情况下处理浏览器选项卡关闭事件吗?
如果我使用beforeunload
或unload
事件,用户刷新页面时触发的函数,我不想要这个,我只想在关闭选项卡时运行。
我怎样才能在 Angular 中做到这一点?
我的目标是在浏览器选项卡关闭时删除用户 cookie。
是否可以?我可以在没有刷新的情况下处理浏览器选项卡关闭事件吗?
如果我使用beforeunload
或unload
事件,用户刷新页面时触发的函数,我不想要这个,我只想在关闭选项卡时运行。
我怎样才能在 Angular 中做到这一点?
可悲的是,这不是一个简单的问题要解决。但这是可以完成的。下面的答案是从许多不同的 SO 答案中合并而来的。
简单部分:
知道窗口正在被破坏。您可以使用onunload
事件句柄来检测这一点。
棘手的部分:
检测它是刷新、链接跟踪还是所需的窗口关闭事件。删除链接和表单提交很容易:
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;
});
穷人的解决方案
检查event.clientY
或event.clientX
确定点击了什么来触发事件。
function doUnload(){
if (window.event.clientX < 0 && window.event.clientY < 0){
alert("Window closed");
}
else{
alert("Window refreshed");
}
}
Y 轴不起作用,因为它对重新加载或选项卡/窗口关闭按钮的点击是负面的,当使用键盘快捷键重新加载(例如 F5、Ctrl-R,...)和窗口关闭(例如 Alt-F4)时,它是积极的)。X 轴没有用,因为不同的浏览器有不同的按钮位置。但是,如果您受到限制,那么通过一系列 if-else 运行事件坐标可能是您最好的选择。请注意,这肯定是不可靠的。
涉及的解决方案
(摘自Julien Kronegg)使用 HTML5 的本地存储和客户端/服务器 AJAX 通信。警告:此方法仅限于支持 HTML5 本地存储的浏览器。
在您的页面上,onunload
向窗口添加以下处理程序
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
}
然后将onloadon
主体添加到以下处理程序
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
}
}
}
在服务器上,收集列表中的断开连接请求并设置一个定时线程,它定期检查列表(我每 20 秒使用一次)。一旦断开连接请求超时(即 5 秒消失),断开用户与服务器的连接。如果同时接收到断开请求取消,则将相应的断开请求从列表中移除,这样用户就不会被断开。
如果您想区分选项卡/窗口关闭事件和跟踪链接或提交的表单,则此方法也适用。您只需要在包含链接和表单的每个页面以及每个链接/表单登录页面上放置两个事件处理程序。
结束评论(双关语):
由于您想在窗口关闭时删除 cookie,我假设这是为了防止它们在以后的会话中被使用。如果这是一个正确的假设,则上述方法将运行良好。您将无效的 cookie 保留在服务器上(一旦客户端断开连接),当客户端创建新会话时,JS 将默认发送 cookie,此时您知道它来自旧会话,将其删除并可选择提供要在客户端上设置新的。
$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
});
关于我们是否可以以万无一失的方式做到这一点,这是颇有争议的。但从技术上讲,这里不能做很多事情,因为你的时间很少,如果客户在你的行动完成之前关闭它,你就有麻烦了,你完全受客户的摆布。不要忘记注入 $window。我建议不要依赖它。
更新:
我正在研究这个问题以及一些建议。除了上述选项之外,处理此类情况的最佳方法是创建没有活动超时可能为 25-30 分钟的会话。将所有需要销毁的 cookie 转换为超时会话。或者,您可以设置 cookie 过期时间,但 cookie 会保留在浏览器中,直到下次登录。少于 25-30 分钟的会话不活动并不完全可靠,因为如果客户端在 10-15 分钟内不使用浏览器,您可能会被注销。我什至尝试从链接 (< a
>) 或 (< submit
>) 或 (keypress
) 基于事件。问题是它可以很好地处理页面刷新。但它并没有消除客户端在您的 cookie 被删除之前关闭浏览器或浏览器选项卡的问题。这是您在用例中必须考虑的事情,并且由于无法控制而强行权衡。与其在稍后面对提到的问题,不如将设计更改为更好的架构。
我刚刚实现了一个(相对)简单的解决方案,上面没有提到。
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 ...
}
}
我发现beforeunload
和unload
事件的逻辑是:
valuedocument.visibilityState
:
卸载前 | 卸下 | |
---|---|---|
标签关闭 | '隐' | '隐' |
选项卡已刷新 | '可见的' | '隐' |
因此,您可以将可见性状态与 beforeunload 事件一起使用来区分用户是刷新还是关闭了选项卡。我使用了一些额外的localStorage
元素来检查注销状态,但对于 5 年后遇到此问题的任何人来说,这可能不需要。
更多document.visibilityState
关于 MDN:https : //developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState
我知道这个问题已经有一段时间了,但我想我也会贡献我的答案。我发现在不丢失 cookie 的情况下重新加载页面并在窗口或选项卡关闭时删除 cookie 的最佳方法是使用 ng-keydown 观看 F5 按键(KeyCode 116)。
HTML
<body ng-controller="exitController" ng-keydown="isRefresh($event)">
控制器
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();
}
};
}]);