是否有任何跨浏览器的JavaScript/jQuery代码来检测浏览器或浏览器标签是否正在关闭,但不是由于链接被单击?


当前回答

如果我没说错,你想知道什么时候一个标签/窗口是有效关闭的。好吧,在JavaScript中检测这个的唯一方法是使用onunload或onbeforeunload事件。

不幸的是(或幸运的是?),当您通过链接或浏览器的后退按钮离开网站时,这些事件也会被触发。这是我能给出的最好答案,我不认为你能在JavaScript中原生检测纯close。如果我说错了,请指正。

其他回答

对于类似的任务,可以使用sessionStorage在本地存储数据,直到关闭浏览器选项卡。

sessionStorage对象仅存储一个会话的数据(当浏览器选项卡关闭时,该数据将被删除)。

这是我的钢笔。

<div id="Notice">
    <span title="remove this until browser tab is closed"><u>dismiss</u>.</span>
</div>
<script>
    $("#Notice").click(function() {
     //set sessionStorage on click
        sessionStorage.setItem("dismissNotice", "Hello");
        $("#Notice").remove();
    });
    if (sessionStorage.getItem("dismissNotice"))
    //When sessionStorage is set Do stuff...
        $("#Notice").remove();
</script>

由于没有人提到它(8年多以后):WebSocket可以是检测关闭选项卡的另一种有效方法。只要选项卡打开并指向主机,客户端就能够保持到主机的活动WebSocket连接。

警告:请注意,这个解决方案只适用于WebSocket不需要任何额外的重大开销的项目。

在合理的超时时间内(例如2分钟),服务器端可以在WebSocket断开连接后确定客户端已经离开,并执行所需的任何操作,例如删除上传的临时文件。(在我非常专业的用例中,我的目标是在WebSocket连接断开和所有CGI/FastCGI活动终止三秒钟后终止本地主机应用服务器-任何其他保持活动的连接都不会影响我。)

我有问题让onunload事件处理程序正确地与信标一起工作(正如这个答案所推荐的)。关闭选项卡似乎不会触发信标,而打开选项卡则会以可能导致问题的方式触发信标。WebSocket更干净地解决了我遇到的问题,因为连接关闭的时间与选项卡关闭的时间大致相同,在应用程序中切换页面只是在延迟窗口内打开一个新的WebSocket连接。

<body onbeforeunload="ConfirmClose()" onunload="HandleOnClose()">

var myclose = false;

function ConfirmClose()
{
    if (event.clientY < 0)
    {
        event.returnValue = 'You have closed the browser. Do you want to logout from your application?';
        setTimeout('myclose=false',10);
        myclose=true;
    }
}

function HandleOnClose()
{
    if (myclose==true) 
    {
        //the url of your logout page which invalidate session on logout 
        location.replace('/contextpath/j_spring_security_logout') ;
    }   
}

//如果你正在关闭标签或只有一个标签的浏览器,这在IE7中是有效的

浏览器已经进行了更新,以便在用户离开应用程序时更好地调整用户。事件“visibilitychange”让你在页面从另一个选项卡隐藏或关闭时进行调整。您可以跟踪文档可见性状态。财产文件。visbilitystate将返回当前状态。您将需要跟踪标志的进出,但它更接近目标。

更新的浏览器支持这一点,但safari(正如我们所知)从来不符合标准。你可以使用' pagshow '和'pagehide'在safari中工作。

您甚至可以使用像sendBeacon这样的新API在标签关闭时向服务器发送单向请求,并且不应该期待响应。

我构建了一个类的快速端口,用于跟踪此情况。我不得不删除框架中的一些调用,所以它可能是bug,但这应该让你开始。

export class UserLoginStatus
{
    /**
     * This will add the events and sign the user in.
     */
    constructor()
    {
        this.addEvents();
        this.signIn();
    }

    /**
     * This will check if the browser is safari. 
     * 
     * @returns {bool}
     */
    isSafari()
    {
        if(navigator && /Safari/.test(navigator.userAgent) && /Chrome/.test(navigator.userAgent))
        {
            return (/Google Inc/.test(navigator.vendor) === false);
        }
        return false;
    }

    /**
     * This will setup the events array by browser.
     * 
     * @returns {array}
     */
    setupEvents()
    {
        let events = [
            ['visibilitychange', document, () =>
            {
                if (document.visibilityState === 'visible')
                {
                    this.signIn();
                    return;
                }

                this.signOut();
            }]
        ];

        // we need to setup events for safari
        if(this.isSafari())
        {
            events.push(['pageshow', window, (e) =>
            {
                if(e.persisted === false)
                {
                    this.signIn();
                }
            }]);

            events.push(['pagehide', window, (e) =>
            {
                if(e.persisted === false)
                {
                    this.signOut();
                }
            }]);
        }

        return events;
    }

    /**
     * This will add the events.
     */
    addEvents()
    {
        let events = this.setupEvents();
        if(!events || events.length < 1)
        {
            return;
        }

        for(var i = 0, length = events.length; i < length; i++)
        {
            var event = events[i];
            if(!event)
            {
                continue;
            }

            event[1].addEventListener(event[0], event[3]);
        }
    }

    /**
     * 
     * @param {string} url 
     * @param {string} params 
     */
    async fetch(url, params)
    {
        await fetch(url, 
        {
            method: 'POST',
            body: JSON.stringify(params)
        });
    }

    /**
     * This will sign in the user.
     */
    signIn()
    {
        // user is the app
        const url = '/auth/login';
        let params = 'userId=' + data.userId;

        this.fetch(url, params);
    }

    /**
     * This will sign out the user.
     */
    signOut()
    {
        // user is leaving the app

        const url = '/auth/logout';
        let params = 'userId=' + data.userId;

        if(!('sendBeacon' in window.navigator))
        {
            // normal ajax request here
            this.fetch(url, params);
            return;
        }

        // use a beacon for a more modern request the does not return a response
        navigator.sendBeacon(url, new URLSearchParams(params));
    }
}

我的方法是:

使用onpopstate监听url中的变化,并将sessionStorage变量设置为1 监听页面加载并将sessionStorage变量设置为0 在beforeunload上,检查变量是否为0。如果是这样,这意味着用户正在关闭而不是改变url。

这仍然是一条迂回的路,但对我来说是有意义的