Fork me on GitHub

ajax打开新窗口实现

注:转载自[举重若轻]ajax打开新窗口实现 

最近开发中碰到一个问题,有一场景需要在ajax验证通过后在新窗口打开一个url。一开始通过在异步回调函数中使用window.open()来实现,但是发现这种实现方式会被浏览器拦截,需要用户进行浏览器设置才能打开,但是这显然不适合在互联网应用中对用户做这个限制。因此就想有没有什么办法来解决?

        首先的一个想法是js打开一个新窗口不行,有可能是浏览器对于js直接打开新窗口有安全限制,那么我form表单提交或者伪造一个url去触发click函数,总不应该会进行拦截了吧。试了之后遗憾地的发现:不行。查了一些资料,浏览器只有在认为click和submit在打开新窗口时(如果是_self则不会有此限制),这些操作是由用户主动触发时才是安全可以被执行,而ajax回调函数中去执行click和submit被浏览器认为不是由用户主动触发的,因此不能被安全执行,所以被拦截。

        然后就想不在ajax异步回调中去要开新窗口,而是在ajax执行的函数中去延时去执行打开新窗口,如下:

 

[javascript] view plaincopyprint?
 
  1. $.ajax({  
  2.     url:url,  
  3.     data:form.serialize(),  
  4.     dataType: 'json'  
  5. }).done(function(o){  
  6.     if(o.success){  
  7.         if(o.data){  
  8.             $('#jump-url-div').val('http://www.XXX.com');  
  9.         }else{  
  10.                         _onFailure();  
  11.                 }}else{_onFailure();}  
  12. }).fail(_onFailure);  
  13. setTimeout(function(){window.open($('#jump-url-div').val());},5000);  

        试了之后,无奈的发现,不仅ajax回调中会被安全拦截,像timeout、interval这种也同样会被浏览器认为不是由用户主动触发的操作,而被拦截,无奈失败了!

 

        想到ajax是异步调用的,那么我在当前函数里通过while(true)来判断url是否获取完毕,如果取到url就去执行新窗口打开逻辑,这样总该可以了吧,于是就写了以下的代码:

 

[javascript] view plaincopyprint?
 
  1. $.ajax({  
  2.     url:url,  
  3.     data:form.serialize(),  
  4.     dataType: 'json'  
  5. }).done(function(o){  
  6.     if(o.success){  
  7.         if(o.data){  
  8.             $('#jump-url-div').val('http://www.XXX.com');  
  9.         }else{  
  10.             _onFailure();  
  11.         }  
  12.     }else{  
  13.         _onFailure();  
  14.     }  
  15. }).fail(_onFailure);  
  16. while(true){  
  17.     if($('#jump-url-div').val()!=''){  
  18.         window.open($('#jump-url-div').val());  
  19.     break;  
  20.     }  
  21. }  

          最后一执行,发现浏览器被挂住,且一直不打开新窗口。后来一查才知道,浏览器的javascript执行引擎是单线程的,所谓的ajax异步执行,其实是在成功回调后,将当前的任务放到浏览器的javascript的执行队列之中,等待执行。而这里由于while(true)一直在执行,因此不可能去执行ajax的回调函数,因此url一直取不到,导致死循环,仍然失败!
 

        后来一想,之所以浏览器认为打开新窗口不是由用户主动发起的,是因为ajax的异步请求导致,而ajax不仅可以进行异步请求,也可以进行同步请求,然后就把这段代码的ajax请求改为同步,调试通过,如下:

 

[javascript] view plaincopyprint?
 
  1. $.ajax({  
  2.     url:url,  
  3.     data:form.serialize(),  
  4.     dataType: 'json',  
  5.     async:false  
  6. }).done(function(o){  
  7.     if(o.success){  
  8.         if(o.data){  
  9.             window.open('http://www.XXX.com');  
  10.         }else{  
  11.             newwin.close();  
  12.             $(document).trigger('simpleDialog',['#dialog-has-order']);  
  13.         }  
  14.     }else{  
  15.         _onFailure();  
  16.     }  
  17. }).fail(_onFailure);  

        但是这种方法虽然解决了问题,但是也同样失去了我们使用ajax的实衷,而且这种方法在jquery的使用手册里也明确指出,其受到调用的接口的性能影响,如果长时间不响应,势必会锁住浏览器,失去异步响应的优势。
        后来通过进一步查找,据说淘宝有一种作法,就是在ajax执行之前先打开一个新窗口,这时候是同步的,所以不会有问题,然后在异步调用成功后使用location.href将其指向新的url,如果失败则将该窗口关闭,如下:

 

[javascript] view plaincopyprint?
 
  1. var newwindow=window.open("about:blank");  
  2. window.focus();  
  3. $.ajax({  
  4.     url:form.attr('action'),  
  5.     data:form.serialize(),  
  6.     dataType: 'json',  
  7.     async:false  
  8. }).done(function(o){  
  9.     if(o.success){  
  10.         if(o.data){  
  11.             newwindow.location.href="http://www.XXX.com";  
  12.                         newwindow.focus();  
  13.         }else{  
  14.             newwindow.close();  
  15.         }  
  16.     }else{  
  17.         newwindow.close();  
  18.     }  
  19. }).fail(function(){newwindow.close();});  

        但是这种方法带来一个问题,就是当失败后,会先打开一个窗口,然后再关闭,虽然一闪而过,但是对用户总是一个困扰,相对来说还不如将ajax改成同步,毕竟现在的互联网的访问速度,不至于会将浏览器锁住太长时间。

posted @ 2015-12-14 17:26  donglegend  阅读(628)  评论(0编辑  收藏  举报