一个项目的需求是这样的:前台的留言表单根据用户的操作,有两种不同的提交方式。一种是在当前页面提交,另外一种是弹出一个新的页面,通过 JavaScript 控制表单的 target 属性来实现。为了保证比较好的用户体验:
- 当前页面提交成功后,返回上一页;
- 弹出页面提交成功后,在显示结果之后关闭当前窗口。
于是我想到了一个比较简单的办法,在目标页面加入:
<script>
window.history.go(-1) ||window.close()
</script>
经过简单测试,发现效果不错,功能上基本可以满足要求。但是,项目上线之后,有用户反映,留言提交完成以后,有时候浏览器窗口会出现非正常关闭的情况。
主要表现为:
当前页面提交后,浏览器返回到提交页面,然后马上又把页面关闭了。
于是,我仔细研究了一下:
window.history.go(-1)
发现这个函数本身并不会返回一个能够参与逻辑判断的结果,也就是说,后面的:
window.close()
实际上还是会继续执行。
于是换一种方式处理:
if(window.history.length <=1){
window.close();
}else{
window.history.go(-1);
}这里利用 history.length 来判断当前页面的历史记录情况。这样修改之后,上面的问题得到了解决。但是问题虽然解决了,我还是觉得有必要继续分析一下:
为什么使用 window.history.go(-1) || window.close() 的时候,会出现这些问题?经过测试,我总结出了下面几个比较有意思的情况。
问题一:为什么有的浏览器会关闭,有的不会?
既然:
window.history.go(-1)
不会返回一个有效的结果,那么:
window.close()
理论上就会执行。
那么,在当前页面提交的时候,为什么有的浏览器窗口没有关闭,而有的浏览器窗口却关闭了?经过测试发现:
Chrome 下页面不会关闭,而 IE 等其他浏览器下页面可能会关闭。
为什么 window.close() 明明执行了,页面却没有关闭?在连续几次测试中,我观察到了 Chrome 控制台中的提示:
Scripts may close only the windows that were opened by it.
我当时的理解是,这应该和浏览器的窗口关闭权限有关。虽然:
window.close()
已经执行了,但是前一个页面没有权限关闭最初打开的那个浏览器窗口。这样,第一个问题基本就解释通了。
问题二:页面已经返回了,为什么又会被关闭?
那么第二个问题又来了。如果:
window.history.go(-1)
执行成功,页面实际上已经返回到了上一页。这个时候后面的:
window.close()
又继续执行。
为什么有些浏览器最终会把页面关闭呢?这是不是 JavaScript 的跨域执行?
当时我觉得这个理解应该不太准确。个人推测,可能有两个原因。
第一,因为两个页面实际上是在同一个窗口中跳转的,加上 JavaScript 执行速度非常快,所以可能出现浏览器在页面跳转过程中继续执行后面的 window.close() 的情况。
我做了一个简单的测试:
window.history.go(-1);
setTimeout(function(){
window.close();
},1000);
这样加上延时之后,就不会再出现之前的情况。这也进一步让我怀疑,问题可能与页面跳转和 JavaScript 执行之间的时间关系有关。当然,这里只是我当时根据测试结果做出的推测。
问题三:为什么新窗口打开时表现又不一样?
还有第三种情况。页面上有一个链接:
<a href="..."target="_blank">留言</a>
用户点击这个链接之后,会打开一个新的窗口进入留言页面。如果在这个页面中提交留言:
- 如果使用弹出方式提交,结果页面可以正常关闭;
- 如果直接在当前页面提交,那么返回原来的页面之后,所有浏览器都会把当前页面关闭。
这和问题一中的表现又有所不同。为什么这一次所有浏览器都关闭了?
我的猜测是:
因为这里本身就是新产生的一个窗口,所以浏览器给了这个窗口被 JavaScript 关闭的权限。
这样也就可以解释为什么前面直接在原页面操作的时候,不同浏览器会产生不同的结果,而通过 target="_blank" 新打开的页面,则表现得更加一致。
总结
这次的问题其实就是由下面这句代码引起的:
window.history.go(-1) ||window.close()
表面上看起来非常简单,但是当 history.go() 和 window.close() 连在一起使用的时候,就涉及到页面跳转、窗口权限以及不同浏览器处理方式等多个问题。
最后,我还是改成了:
if(window.history.length <=1){
window.close();
}else{
window.history.go(-1);
}这样至少可以避免原来那种“返回上一页之后又继续执行 window.close()”的问题。当然,上面这些结论全部来自我当时的实际测试和个人推测,并没有找到官方文档作为依据。如果你发现有相关的文档资料,或者有不同的理解,欢迎交流。
评论2
欢迎分享你的看法,也欢迎补充不同的实践经验。