这是多么普遍而又正常的需求啊,然而在多浏览器时代,这又是多么难做啊~~(我不是FE,我是Java工程师)
目前这个代码能够兼容以下浏览器(我亲测过的):
IE8,Chrome12,Firefox5,Safari4
应该也能支持以下浏览器:
IE7,Chrome8以后的版本,Firefox3.6,4
由于我们的系统不支持IE6,因此就不考虑这个问题了,估计应该没啥问题。
由于Opera不支持beforeunload事件,因此不会弹出提示让用户决定是否退出,同时对于刷新回退等操作也不会调用unlaod,因为它认为那是reload,会直接从cache中获得。我唯一成功响应Opera的unload事件是将Opera关闭了,然后再打开由于它会保留上次的标签,因此会重新加载,就在这个时候它响应了unload。因此放弃对Opera的支持,毕竟我们的服务对象没什么人用。
本来的逻辑是当用户关闭或者刷新或者后退时会弹出一个提示框询问用户是否真的关闭,这里面有以下几个技术陷阱:
1 这个事情只能通过beforeunload完成,但是一旦注册了这个事件,浏览器就会弹出个确认框,不需要你自己写什么confirm,我起初不知道,因为使用了一个开源的产品,找遍了它所有的代码想解除绑定或者屏蔽那个确认框,后来才发现原来是浏览器内置的!
其用法如下:
- window.onbeforeunload=function(){
- return 'Are you really going to leave?';
注意这个地方IE,Firefox,Chrome,各不相同,Safari跟IE相同:
IE和Safari会显示一段内置的话,然后中间显示你这句话;
Firefox只会显示它内置的提示;
Chrome只会显示你写的这段话。
后面让人崩溃的浏览器差异还有很多。
2 当刷新或后退时,执行的流程是beforeunload事件,unload事件和load事件。
但是IE会弹出两次那个提示框。
3 当unload事件触发时,退出登录,有两种方法:
1)发送Ajax请求退出
2)location.href
但是,Chrome和Safari(这俩浏览器是一个内核)在unload方法里执行location.href无效。
按照网上说的各种方法,比如window.location,self.location,navigate.go(0)等等,都无效。
然后使用window.close替代,也无效,后来发现可以这样:
window.open('', '_self', ''); //bug fix
window.close();
这样搞完又会弹两次框。这很正常,因为又打开了一个窗口嘛。
4 Chrome执行完unload事件后,因为没有执行location.href因此继续执行onload方法,悲剧发生了,虽然注销登录成功了(通过Ajax的方式),但是本页面并没有被filter拦截跳转到登录页面,但是其中引入的js,iframe却被filter拦住了,通过Fiddler2观察确实发送了很多次Login那个页面的请求,然后页面由于该加载的JS没过来,就在那儿不动了。这时只有点击刷新,才会真正的跳转到Login页面。
这是使用window.close的一个理由,否则就算location.href不成功,能在onload时自动跳转也没事,但可惜不行。
5 IE和Chrome弹两次这个问题,应该是通过将绑定事件放到onload,而不是直接写在<script>标签中。可能是这两个浏览器的某种机制使得他们会执行两次。
全部代码如下:
<script language='javascript'>
- function getOs(){
- if(navigator.userAgent.indexOf("MSIE")>0)return 1;//IE
- if(isFirefox=navigator.userAgent.indexOf("Firefox")>0)return 2;//Firefox
- if(isSafari=navigator.userAgent.indexOf("Chrome")>0)return 3;//Chrome
- if(isSafari=navigator.userAgent.indexOf("Safari")>0)return 4;//Safari
- if(isCamino=navigator.userAgent.indexOf("Camino")>0)return 5;//Camino
- if(isMozilla=navigator.userAgent.indexOf("Gecko/")>0)return 6;//Gecko
- if(isOpera=navigator.userAgent.indexOf('Opera') >= 0) return 7;//Opera
- //other...
- return 0;
- }
-
- var quite=false;
-
-
- function bindOnbeforeunload(){
- quite=false;
- window.onbeforeunload=checkLeave;
- }
-
- function unbindOnbeforeunload(){
- quite=true;
- window.onbeforeunload=null;
- }
-
-
-
- function checkLeave(){
- return '您正在离开...';
- }
-
-
- window.onunload=function(){
- try{
- unbindOnbeforeunload();
- }catch(e){
-
- }
- window.location.href='/Logout';
- if(getOs()==3||getOs()==4){
- var xmlHttp = false;
- try {
- xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
- } catch (e) {
- try {
- xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
- } catch (e2) {
- xmlHttp = false;
- }
- }
- if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
- xmlHttp = new XMLHttpRequest();
- }
- var url = "/Logout";
- xmlHttp.open("POST", url, false);
- // Send the request
- xmlHttp.send(null);
- window.open('', '_self', ''); //bug fix
- window.close();
- }
-
-
-
- }
-
-
-
- </script>
- </head>
在HTML body中注册:
<body oncontextmenu="return false;" onload="javascript:return bindOnbeforeunload();" >