点击后退按钮时是否存在跨浏览器的onload事件?

200

对于所有主要浏览器(除IE外),当页面由于返回按钮操作而加载时,JavaScript的onload事件不会触发 - 它只在页面第一次加载时触发。

有人可以给我指出一些可解决这个问题的跨浏览器示例代码(Firefox,Opera,Safari,IE等)吗?我熟悉Firefox的pageshow事件,但不幸的是,Opera和Safari都没有实现此功能。


6
这不是问题——这样可以使用户按返回按钮时网页加载得更快。详情请见下文的回答。本文中提出的解决方法会让用户感到更加烦恼,因为前进和后退操作会变得更慢。 - Nickolay
2
@romkyns:您的评论与此问题无关。当浏览器不恢复JS/DOM状态时,它们确实会触发加载事件。 - Nickolay
iOS 5+ 历史返回按钮问题解决方法在这里:https://dev59.com/RGct5IYBdhLWcg3wmOZN#12652160 - Mladen Janjetovic
16个回答

126

大家好,我发现JQuery只有一个作用:在按下后退按钮时重新加载页面。这与"ready"无关。

它是如何工作的呢?JQuery 添加了一个onunload事件监听器。

// http://code.jquery.com/jquery-latest.js
jQuery(window).bind("unload", function() { // ...

默认情况下,它没有任何作用。但不知为何,在Safari、Opera和Mozilla中,似乎会触发重新加载。无论事件处理程序包含什么。

[编辑(Nickolay):它为什么能这样工作的原因在这里:webkit.orgdeveloper.mozilla.org。请阅读这些文章(或者我在另一个答案中的摘要),并考虑您是否真的需要这样做,并使您的页面加载速度变慢。

难以置信吗?试试这个:

<body onunload=""><!-- This does the trick -->
<script type="text/javascript">
    alert('first load / reload');
    window.onload = function(){alert('onload')};
</script>
<a href="http://stackoverflow.com">click me, then press the back button</a>
</body>

使用JQuery时,您将看到类似的结果。

您可能想与没有onunload的情况进行比较。

<body><!-- Will not reload on back button -->
<script type="text/javascript">
    alert('first load / reload');
    window.onload = function(){alert('onload')};
</script>
<a href="http://stackoverflow.com">click me, then press the back button</a>
</body>

3
这也可以起作用(不要忘记加上 onunload="" 部分,否则在 Firefox 3 上无法正常工作):<body onload="alert('onload');" onunload=""> - Wadih M.
3
这是因为浏览器假设如果一个页面有 onunload 处理程序(页面已经销毁了所有内容,为什么要缓存它?),它就无法被缓存。 - Casey Chu
1
显然使用onload会破坏'bfcache':http://friendfeed.com/paul/2b7ddce5/jquery-1-3-breaks-bfcache-because-it-binds - waterlooalex
14
似乎答案在现代浏览器中无法使用——已在最新版本的Chrome和Opera中测试过。 - Andrew
10
不适用于iPad Safari浏览器...请取消将此标记为答案。 - xus
显示剩余10条评论

85
一些现代浏览器(Firefox、Safari和Opera,但不包括Chrome)支持特殊的“后退/前进”缓存(我称之为bfcache,这是Mozilla发明的术语),当用户导航到后退时使用。与常规的(HTTP)缓存不同,它捕获页面的完整状态(包括JS、DOM的状态)。这使得它可以更快地重新加载页面,并且可以确切地还原用户离开时的状态。
在从bfcache中加载页面时,load事件不应该被触发。例如,如果您在“load”处理程序中创建了UI,并且“load”事件在初始加载时触发了一次,在页面从bfcache中重新加载时第二次触发,页面将最终具有重复的UI元素。
这也是为什么添加“unload”处理程序会阻止页面被存储在bfcache中(从而使其后退速度变慢)——unload处理程序可能执行清理任务,这可能使页面处于不可工作的状态。
对于需要知道何时被导航到或导航回的页面,Firefox 1.5+和带有 Bug 28758 修复版本的 Safari 支持名为“pageshow”和“pagehide”的特殊事件。
参考资料:

1
@Benson:Firefox无法将页面保存在bfcache中的可能原因已列在我链接到的dev.m.o页面上。我认为不可能将页面保存到bfcache中,但某些DOM状态可能未被保存。 - Nickolay
@Nickolay,如果网站使用pushstatereplacestate,那么bfcache非常容易出现错误。我们有一些真正有效的解决方案吗? - Pacerier
@Pacerier:我不知道,但如果你链接到导致你得出这个结论的问题/错误报告,你可能会得到更好的答案。 - Nickolay
Chrome浏览器有bfcache吗?我已经能够在Safari和FF中触发bfcache,但在Chrome中却无法。其他线程/博客表明Chrome没有实现bfcache。例如,请参见此Chrome问题:https://code.google.com/p/chromium/issues/detail?id=2879 - mla
Mla,感谢提供链接!我在Chrome基于Webkit时编写了这个程序,并错误地假设它在Chrome中自动启用(毕竟它是快速浏览器!)已更新帖子。 - Nickolay
显示剩余6条评论

31

我遇到了一个问题,当用户点击后退或前进时,我的JS代码没有执行。我首先尝试停止浏览器缓存,但这似乎不是问题的原因。我的JavaScript设置为在所有库等加载完成后执行。我使用readyStateChange事件检查了它们。

经过一些测试,我发现页面中元素的readyState在点击“后退”后不是“已加载”,而是“已完成”。将||element.readyState == 'complete'添加到我的条件语句中解决了我的问题。

只是想分享我的发现,希望能帮助其他人。

编辑完整内容

我的代码如下:

script.onreadystatechange(function(){ 
   if(script.readyState == 'loaded' || script.readyState == 'complete') {
      // call code to execute here.
   } 
});
在上述代码示例中,script变量是一个新创建的脚本元素,已被添加到DOM中。

9
你把这个添加在哪里了?用户点击“返回”后,你是如何触发任何操作的?请展示一些代码! - Keerigan
当你说“我的条件语句”时,你的意思是什么? - Phillip Senn
1
@Phillip 条件语句就是 if 语句。 - Brian Heese

22

好的,这里是一个最终解决方案,基于ckramer的初始解决方案和palehorse的示例,在所有浏览器中都有效,包括Opera。如果将history.navigationMode设置为“compatible”,那么jQuery的ready函数将在Opera中的后退按钮操作以及其他主要浏览器中启动。

此页面有更多信息

示例:

history.navigationMode = 'compatible';
$(document).ready(function(){
  alert('test');
});

我在Opera 9.5、IE7、FF3和Safari中测试过,它们都可以正常工作。


2
显然已经有一段时间了(大约5.5年),但值得注意的是你的链接已经失效。 - Jeff
2
这个解决方案对我不起作用。我在最新的Firefox(45.0.1)中遇到了这个问题。 - Armin
在Chrome 100中无法工作。 - mhenry1384

6

我无法使上述示例正常工作。我只是想在通过后退按钮返回页面时触发对某些修改的div区域进行刷新。我使用的技巧是,当div区域从原始状态更改时,将隐藏输入字段(称为“脏位”)设置为1。当我点击后退时,隐藏输入字段实际上会保留其值,因此在onload中,我可以检查这个位是否被设置。如果设置了,我就刷新页面(或只刷新div区域)。然而,在最初加载时,该位未设置,因此我不会浪费时间加载页面两次。

<input type='hidden' id='dirty'>

<script>
$(document).ready(function() {
  if ($('#dirty').val()) {
    // ... reload the page or specific divs only
  }
  // when something modifies a div that needs to be refreshed, set dirty=1
  $('#dirty').val('1');
});
</script>

每当我点击返回按钮时,它都能正确触发。


3
虽然这种方法在Firefox中不起作用,但我认为它是处理IE/Chrome中隐藏字段持久性非常有用的情况的一个狡猾方式。我已经使用了您的技巧以及“pageshow”事件,因此我覆盖了所有主要浏览器。 - Magnus Smith
这个浏览器可以使用pageshow,而其他浏览器不能? - Justin

5
我可以确认ckramer,jQuery的ready事件在IE和FireFox中都有效。下面是一个示例:
<html>
<head>
    <title>Test Page</title>
    <script src="http://code.jquery.com/jquery-latest.js" type="text/javascript"></script>
    <script type="text/javascript">
            $(document).ready(function () {
               var d = new Date();
               $('#test').html( "Hi at " + d.toString() );
            });
    </script>
</head>
<body>
    <div id="test"></div>
    <div>
        <a href="http://www.google.com">Go!</a>
    </div>
</body>
</html>

4

我以为这是针对“onunload”,而不是页面加载,因为我们讨论的是在点击“返回”时触发事件。$document.ready()用于希望在页面加载时触发的事件,无论您如何到达该页面(即重定向、直接在浏览器中打开URL等),而不是在单击“返回”时,除非您正在谈论当上一页再次加载时要触发什么。而且我不确定页面是否没有被缓存,因为我发现即使在其中包含了$document.ready(),JavaScript仍然会被缓存。每当我们编辑具有此事件的脚本并希望在页面中测试结果时,我们不得不按Ctrl+F5。

$(window).unload(function(){ alert('do unload stuff here'); }); 

当用户点击“返回”并卸载当前页面时,以及关闭浏览器窗口时,您希望的是onunload事件。即使我在$document.ready()响应方面处于少数派,这似乎更符合要求。基本上,区别在于一个事件在当前页面关闭时触发,另一个事件在单击“返回”并加载时触发。在IE 7中测试过,其他浏览器无法确定。但这可能是另一种选择。


4

如果我没记错的话,添加unload()事件意味着页面不能被缓存(在前进/后退缓存中)-因为它的状态会随着用户导航而改变。因此,在通过历史对象导航返回页面时恢复最后一秒钟的状态是不安全的。


3

2
仅提供一个链接被认为是一个差劲的回答(参见[faq#deletion]),因为它本身是没有意义的,而且目标资源未来可能无法访问最好的做法是在这里包含答案的关键部分,并提供链接作为参考。 - j0k

3

jQueryready 事件就是为了解决这种问题而创建的。你可能需要深入了解其实现原理。


6
很久以后,在使用Firefox的“返回”按钮时,“ready”似乎没有被触发。参见Nickolay的回答 - Arjan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接