打印

BX2047: 各浏览器对 onbeforeunload 事件的支持与触发条件实现有差异

作者:钱宝坤

标准参考

无。

问题描述

一般情况下,onbeforeunload 事件处理函数内会写入一些提示性语句,当用户的浏览器跳转到其他页面时,用来提醒用户当前页面将要跳转,请用户决定是否观看新页面。
或者在 onbeforeunload 事件内处理一些业务逻辑,在浏览器跳转到新页面之前 ,执行一些业务逻辑,如保存用户浏览信息等。

简单的说这个事件应仅在页面 URL 发生变化时触发,但是在 IE 中 使用 JavaScript 伪协议执行脚本程序时,也会触发 onbeforeunload 事件。

造成的影响

此问题不会造成大问题,但会导致不友好的提示出现,稍微影响用户操作体验。

受影响的浏览器

所有浏览器  

问题分析

onbeforeunload 事件是非 W3C DOM-Event 标准事件,它属于 BOM (Browser Object Model) 范畴。到现在为止 BOM 还没有被标准化,它由各个浏览器厂商制定,因此会有实现差异。

时至今日,HTML5 规范草案中已经开始标准化 BOM,遗憾的是 onbeforeunload 事件的触发条件还没有在草案中作出详细说明。

更多内容可参考:6.1.6.2 Event handlers on elements, Document objects, and Window objects

最初的 onbeforeunload 事件支持是由 IE4.0 版本提供,存在于 BODY、FRAMESET 的 DOM 对象及 window 对象之中,随后被其他浏览器复制,但具体事件触发方式并没有统一。

根据 MSDN 中描述,IE 的 onbeforeunload 事件可由以下这些条件触发:

  • 关闭当前浏览器窗口。
  • 导航到另一个进入一个新的地址或选择一个喜欢的位置。
  • 单击后退,前进,刷新,或主页按钮。
  • 点击一个链接到新页面。
  • 调用 超链接的 click 方法。
  • 调用 document.write 方法。
  • 调用 document.open 方法。
  • 调用 document.close 方法。
  • 调用 window.close 方法。
  • 调用 window.open 方法,窗口名称设置值为 _self。
  • 调用 window.navigate 或 NavigateAndFind 方法。
  • 调用 location.replace 方法。
  • 调用 location.reload 方法。
  • 指定一个 location.href 属性的新值。
  • 使用 submit 按键提交表单,或调用 form.submit 方法。

更详细的说明可以查考 MSDN 原文:onbeforeunload Event

在这些触发条件中绝大多数都使页面产生了跳转,但还缺少一些常见情况说明,即页面 URL 可能发生了变化但没有产生跳转。比如 "javascipt:" "mailto:" 等常见的浏览器内置伪协议,以及由第三方或用户自定义的为协议时,页面并不跳转,而是根据伪协议执行指定的行为。这个情况应加入到触发条件中。

根据以上所有这些触发条件,我们构建如下代码来检测各浏览器对 onbeforeunload 事件的支持程度与触发条件:

<script>
window.onbeforeunload=function(){
  return "请点击取消留在此页";
}
</script>
请手工关闭当前浏览器窗口。<br/>
请手工单击后退,前进,刷新,或主页按钮。<br/>
请手工在地址栏输入其他页面地址或从收藏夹、历史记录中将页面导航其他站点。<br/>
<a href="http://www.google.com" id="A">点击一个链接到新页面</a><br />
<button onclick="document.getElementById('A').click()">调用 anchor.click 方法</button><br />
<button onclick="document.write('A')">调用 document.write 方法</button><br />
<button onclick="document.open()">调用 document.open 方法</button><br />
<button onclick="document.close()">调用 document.close 方法。</button><br />
<button onclick="window.open('http://www.google.com','_self')">调用 window.open方法,窗口名称设置值为 _self。</button><br />
<button onclick="try{window.navigate('http://www.google.com')}catch(e){alert('不支持此方法')}">调用 window.navigate 方法</button><br />
<button onclick="try{window.external.NavigateAndFind('http://www.google.com','','')}catch(e){alert('不支持此方法')}">调用 NavigateAndFind 方法</button><br />
<button onclick="location.replace('http://www.google.com')">调用 location.replace 方法</button><br />
<button onclick="location.reload()">调用 location.reload 方法</button><br />
<button onclick="location.href='http://www.google.com'">指定一个 location.href 属性的新值</button><br />
<form action="http://www.google.com" id="B">
<input type="submit" value="提交具有action属性的一个表单">
</form>
<button onclick="document.getElementById('B').submit()">调用 form.submit 方法</button>
<a href="javascript:">调用 javascipt: 伪协议</a><br />
<a href="mailto:">调用 mailto: 伪协议</a><br />
<a href="custom:">调用自定义伪协议</a>

执行结果汇总入表:

  IE Firefox Chrome Safari Opera
关闭当前浏览器窗口 事件被触发 事件被触发 事件被触发 不支持该事件
导航到另一个进入一个新的地址或选择一个喜欢的位置 事件被触发 事件被触发 事件被触发 不支持该事件
单击后退,前进,刷新,或主页按钮 事件被触发 事件被触发 事件被触发 不支持该事件
点击一个链接到新页面 事件被触发 事件被触发 事件被触发 不支持该事件
调用 anchor.click方法 事件被触发 不支持此方法1 不支持此方法1 不支持该事件
调用 document.write方法 事件被触发 事件被触发 事件未触发 不支持该事件
调用 document.open方法 事件被触发 事件被触发 事件未触发 不支持该事件
调用 document.close方法 事件未触发 事件未触发 事件未触发 不支持该事件
调用 window.open方法,窗口名称设置值为 _self 事件被触发 事件被触发 事件被触发 不支持该事件
调用 window.navigate 事件被触发 不支持此方法2 不支持此方法2 不支持该事件
调用 NavigateAndFind方法 事件被触发 不支持此方法3 不支持此方法3 不支持此方法3
调用 location.replace 方法 事件被触发 事件被触发 事件被触发 不支持该事件
调用 location.reload 方法 事件被触发 事件被触发 事件被触发 不支持该事件
指定一个 location.href 属性的新值 事件被触发 事件被触发 事件被触发 不支持该事件
使用 submit 按键提交表单 事件被触发 事件被触发 事件被触发 不支持该事件
调用 form.submit 方法 事件被触发 事件被触发 事件被触发 不支持该事件
调用 javascipt: 伪协议 事件被触发 事件未触发 事件未触发 不支持该事件
调用 mailto: 伪协议 事件未触发 事件未触发 事件被触发 不支持该事件
调用自定义伪协议 事件被触发 事件被触发 事件被触发 不支持该事件

注 1: 直接调用链接元素的 click 方法模拟鼠标点击事件,只有 IE 和 Opera 支持,BX9052: IE Opera 支持使用 window.navigate 方法控制页面跳转SD9025: IE6 IE7 IE8 Opera 支持除 INPUT 和 BUTTON 元素以外的其他元素的 'click' 方法
注 2: 使用 window.navigate 方法导航网页仅被 IE Opera 支持,可参考 MSDN 原文:navigate Method
注 3: NavigateAndFind 方法处于 window.external 对象中,external 对象也仅 IE 支持,可参考 MSDN 原文:NavigateAndFind Method 和本站文章 BT9012: IE 的 external 对象提供的方法是 IE 特有的

结合汇总表可以看出:

  • Opera 并不支持 onbeforeunload 事件。
  • Chrome Safari 在调用 document.write、document.open、document.close 方法以及 "javascipt:" 伪协议时,不会触发 onbeforeunload 事件。
  • Firefox 在调用 document.close 方法和 "javascipt:"、"mailto:" 伪协议时,不会触发 onbeforeunload 事件。
  • IE 浏览器在调用 document.close 方法和 "mailto:" 伪协议时,不会触发 onbeforeunload 事件。

解决方案

onbeforeunload 事件还未标准化,各浏览器的支持以及事件触发条件差异较多,需谨慎使用。

如必须在非 Opera 浏览器内使用该事件,应尽量避免在页面中调用常见的 "javascrpt:" 以及其他伪协议,以此回避不同浏览器中 onbeforeunload 事件被频繁触发。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.10
Chrome 7.0.544.0 dev
Safari 5.0.2
Opera 10.62
测试页面: onbeforeunload_event_differences.html
本文更新时间: 2010-10-13

关键字

javascipt URL Protocol onbeforeunload