JS-监听窗口关闭
JavaScript 监听窗口关闭。初学JavaScript,如有不当请指正。
接手的项目中有这样一个场景:
点击打印按钮,生成需要临时文件存储在项目内(该步骤为水晶报表生成打印文件必须进行的流程);
弹出新窗口,显示即将打印的文件名和下载按钮供用户确认;
用户点击下载按钮,浏览器启动下载;
用户关闭窗口,系统删除生成的临时文件。
原有的处理方式:
1 | function window.onbeforeunload() |
原有的写法在 Visual Studio 2015 中编辑器环境下会提示语法错误,但在
Debug / Run 过程中,在其他浏览器中均不会弹出错误提示,但没有跑进
deletePDF()
函数中,在 IE
的测试中部分测试环境会弹出错误弹窗提示:
JavaScript critical error at ... in ... : Syntax error
点击 Continue 后与其他浏览器类似,并不会影响文件下载,同样未进入
deletePDF()
函数。
onbeforeunload/beforeunload
查询 MDN 的 文档1 ,onbeforeunload
是
beforeunload
的处理程序。语法如下:
1 | window.onbeforeunload = function(event) { ... }; |
1 | window.addEventListener("beforeunload", function(event) { ... }); |
MDN 建议使用第二种语法。 根据 javascript - beforeunload Or onbeforeunload - Stack Overflow2 ,第一种语法取代原有的处理程序,第二种语法新增一个处理程序。
分别使用上述两种语法修改原有的处理函数,原有的函数中的第 3
行存在语法错误,该语句用于判断鼠标的位置,将该语句移除后将会出现多次触发
onbeforeunload
的情况:
页面刚进入时
点击下载按钮时
关闭窗口时
以上情况将导致在文件未下载成功前,已进入 deletePDF
,临时文件已被删除无法下载。
实际上,beforeunload
会在多种情况下被触发,包括但不限于提交表单,点击链接/按钮,关闭窗口或标签,通过地址栏,搜索栏,书签等方式访问新页面。
因此,尽管 addEventListener
实现了关闭窗口的监听,却会在关闭窗口前就执行了相关操作。
尝试使用原有的处理方式中的 event.ClientY
进行下一步限制,但实际上仅仅使用 ClientY
将只能检测鼠标的工作情况,而无法检测通过快捷键关闭的情况,原有的处理方式同时检测了
alt
键,但这将导致所有的 alt
快捷键触发。且测试中 event.ClientY
并未起作用,原因尚未查明。
addEventListener/removeEventListener
与 addEventListener
对应的方法是
removeEventListener
。这两个方法在不支持 IE 9
以下版本。如果要在 IE9 以下版本使用,应该使用 attachEvent
(实际上 MDN 不建议使用 attachEvent
)。
1 | if (window.attachEvent) { |
经过几次尝试,结合 MDN 的文档,addEventListener
与
removeEventListener
必须完全一致,也即,无法添加
window.addEventListner
而移除
button.removeEventListeneer
。
jQuery on/off bind/unbind
在检测关闭窗口并禁止提交触发的实践中,常用的方式还有使用
jQuery
:on/off
bind/unbind
。其中 on/off
在 jQuery 1.7 中取代 bind/unbind
。由于项目较老旧,使用的 jQuery 1.2,故使用 bind/unbind
测试。测试中并未进入 deletePDF
函数,原因尚未查明。
1 | $("[id$='_btnDownload']").click(function () { |
添加标志位检测
考虑添加一个监听事件,当按钮按下时变更标志位,标记此次操作触发方式。
1 | var download = false; |
此代码可正常工作,但未知是否可能存在这样一种情况:
用户1点击下载按钮,
download = true
;用户2不进行下载直接关闭窗口,
download = true
;
在这种情况下,用户2的操作产生的临时文件将不会被删除。在本地使用 Start without debug 调试,通过不同浏览器打开不同窗口,此代码可以正常工作。
onunload/unload
考虑需求,并不需要实现 beforeunload
中的离开弹窗提示,即可以直接使用 unload
事件,监听
unload
将不会在按钮按下时被触发,使用 unload
似乎更简洁。
1 | window.addEventListener("unload", deletePDF); |
Page
Lifecycle API | Web | Google Developers3
指出:使用 unload
, beforeunload
作为检查页面生命周期的方式并不可靠,尤其是 unload
在移动端极不可靠,建议使用 pagehide
,而
beforeunload
适用于存在用户未保存的更改时离开页面的监听,并应该在保存后移除该监听事件,不应使用其作为判断页生命周期的方法。
根据该文章的建议,使用以下代码:
(原文使用的是 const
, 但经过测试,IE
10及以下版本中并不支持 const
)
1 | var terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload'; |
该代码在本地测试除 Firefox
以外可正常使用,而该项目没有移动端需求,目前尚不能看出使用
unload
的劣势。
参考资料
javascript - How to capture the browser window close event? - Stack Overflow
javascript - disable js beforeunload if button with specific id has been clicked - Stack Overflow
javascript - correct usage of addeventlistener attachevent - Stack Overflow
[jQuery] jQuery 實作離開網頁或表單前向使用者確認,以防止使用者誤觸離開按鈕 | 分享你的 Coding 新鮮事 - 點部落
javascript - Narrow Down BeforeUnload To Only Fire If Field Is Changed or Updated - Stack Overflow
javascript - capture close window by beforeunload but exclude asp button - Stack Overflow