【问题标题】:The Safari Back Button ProblemSafari 后退按钮问题
【发布时间】:2010-09-06 15:30:42
【问题描述】:

我为当地的一所社区大学做一些小的编程和网络工作。工作包括维护一个非常庞大且破坏灵魂的网站,该网站由 VBScript、javascript、Dreamweaver 生成的杂乱无章的大杂烩和各种骗子多年来说服他们购买的附加组件的集合组成。

几天前,我接到一个电话“该网站正在为使用 Safari 的人锁定!”好的,第一步下载Safari(v3.1.2),第二步上网。一切似乎都运行良好。

长话短说,我终于找出了这个问题,它与 Safari 的后退按钮有关。该网站使用了一个花哨的 javascript 菜单,该菜单适用于我尝试过的所有浏览器,包括 Safari,这是第一次。但在 Safari 中,如果您点击页面外的链接,然后点击后退按钮,则菜单不再起作用。

我做了一个精简的网页来说明原理。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><title>Safari Back Button Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body onload="alert('Hello');">
<a href="http://www.codinghorror.com">Coding Horror</a>
</body>
</html>

加载页面,您会看到警告框。然后点击页面外的链接并点击后退按钮。在 IE 和 Firefox 中,您会再次看到警告框,而在 Safari 中则不会。

经过一番激烈的谷歌搜索后,我发现其他人也有类似的问题,但没有真正令人满意的答案。所以我的问题是,当用户点击后退按钮后,如何让我的页面在 Safari 中像在其他浏览器中一样工作?

如果这是一个愚蠢的问题,请轻描淡写,javascript 对我来说有点新。

【问题讨论】:

  • 嘿,汽车,你知道了吗?我已经在 iPad 和 iPod 上尝试了以下所有解决方案,当客户端按下后退按钮时,我无法让 Safari 运行 JavaScript。 (当我在桌面上使用 Safari 时,我可以得到它,但我无法让它与移动版本的 Safari 一起使用。这是你使用过的东西吗?)
  • 查看问题Mobile Safari back button的答案
  • onload() 事件在 Safari 中未触发后退按钮被点击
  • Sal 我最终还是明白了这一点。我用 jquery 的“document.ready”而不是“onload”来调用我的函数。

标签: javascript safari


【解决方案1】:

Stefan 的 iframe 解决方案有效,但如果这还不够优雅,我发现以下 JavaScript 也可以解决它:

window.onunload = function(){};

也就是说,如果你的菜单是 JavaScript,那么你可能更喜欢用 JavaScript 来解决这个问题。

unload 事件处理程序定义的想法来自这篇 Firefox 1.5 文章:https://developer.mozilla.org/en/Using_Firefox_1.5_caching

【讨论】:

  • 即使我使用 jQuery 的 $(document).ready 事件,Safari 中的后退按钮也有问题。我添加了上面的代码,它解决了这个问题。
  • 似乎这个问答仍然相关。当考虑浏览器的怪癖时,人们倾向于只考虑 IE,但“其他”(Webkit、Firefox)也有它们,尽管更小并且更难以捉摸......
  • @Mika Tuupola:Safari 不仅在 iOS 上可用。我看不出操作系统对 Safari 的行为有何影响,您也必须拥有更新版本的 Safari。我没有任何 iOS 设备,所以耸耸肩
  • @LeeKowalkowski 操作系统确实对它有影响 - iOS 版 Safari 工作,而 OS X 版本确实工作!跨度>
  • @AralRoca 是的。查看bfcache(例如通过this blog post)并查看here 如何挂钩一些代码以整理之前的页面状态。
【解决方案2】:

把这个放在body标签里 &lt;body onunload=""&gt;

这将在您每次点击返回按钮时强制 safari、chrome、FF 重新加载

【讨论】:

  • 它通过 Safari 和 Firefox 测试解决了我在 Angular 项目上的问题,谢谢?
【解决方案3】:

请不要遵循任何告诉您忽略缓存的建议。页面被缓存是有原因的——改善用户体验。您使用的方法会使用户体验变差,所以除非您讨厌您的用户,否则不要这样做。

适用于 Safari(桌面和 iOS)的正确解决方案是使用 pageshow 事件而不是 onload 事件(请参阅 https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching 了解这些事件的含义)。

pageshow 事件将在您期望onload 事件触发的同时触发,但当通过缓存提供页面时它也将起作用。无论如何,这似乎是您想要的。

【讨论】:

  • 真的吗?如果 js 以某种方式修改了页面,例如在指示进程的按钮上指示微调器,然后加载新页面,则在返回导航时,您将在按钮上看到此微调器。这不是应该的方式
  • @SergeyP.akaazure 这就是您使用pagehide 事件来停止微调器的原因。
【解决方案4】:

这是一个很好的 Mobile Safari 解决方案:

/*! Reloads page on every visit */
function Reload() {
    try {
        var headElement = document.getElementsByTagName("head")[0];
        if (headElement && headElement.innerHTML)
            headElement.innerHTML += " ";
        } catch (e) {}
    }

    /*! Reloads on every visit in mobile safari */
    if ((/iphone|ipod|ipad.*os 5/gi).test(navigator.appVersion)) {
        window.onpageshow = function(evt) {
            if (evt.persisted) {
                document.body.style.display = "none";
                location.reload();
            }
        };
    }

(source)

我根据自己的需要对其进行了修改,但没关系。 (如果你不修改它,刷新时会出现烦人的白屏)。

【讨论】:

  • 单独的链接是considered a poor answer,因为它本身没有意义,并且不能保证目标资源将来仍然存在。请尝试至少包含您链接到的信息摘要。
【解决方案5】:

iframe 解决了这个问题:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><title>Safari Back Button Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body onload="alert('Hello');">
<a href="http://www.codinghorror.com">Coding Horror</a>
<iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
this prevents back forward cache
</iframe>
</body>
</html>

更多details

【讨论】:

【解决方案6】:
$(window).bind("pageshow", function(event) {
  if (event.originalEvent.persisted) {
    window.location.reload() 
  }
});

here 最近由mshah 回答。

【讨论】:

    【解决方案7】:

    我不知道是什么导致了问题,但我知道谁能帮助你。 Safari 是基于 Webkit 构建的,缺少 Apple(他们不那么具有社区意识)Webkit team 可能知道问题出在哪里。

    这根本不是一个愚蠢的问题。

    【讨论】:

      【解决方案8】:

      我注意到一些非常相似的东西。我认为这是因为 Firefox 和 IE 在返回时再次从服务器检索页面,而 Safari 则没有。您是否尝试过添加页面过期/无缓存标头?当我发现这种行为但还没有时间时,我正打算调查它。

      【讨论】:

        【解决方案9】:

        我知道这个帖子已经有一年了,但我只是使用 ProjectSeven 的 Safari 后退按钮修复了一个相同的 Safari 专用问题。像魅力一样工作。

        http://www.projectseven.com/extensions/info/safaribbfix/index.htm

        【讨论】:

        • 这很有趣,因为 ProjectSeven 是我所说的 javascript 菜单。最终放弃了它,转而使用自制的 jquery 。从 body 标记中的 onload 移动到 jquery 的 document.ready 为我解决了这个问题。
        【解决方案10】:

        在 iPad 上遇到同样的问题。

        不是那么漂亮,但它有效:)。它是如何工作的。

        我意识到在 iPad Safari 上,按下后退按钮时页面没有重新加载。我在页面上每秒放置一个计数器并保存当前时间戳。

        当页面加载时,计数器和时间是同步的。在后退按钮上,计数器从它停止的地方继续,并且时间戳和计数器之间存在间隙。如果间隔大于 500 毫秒,则强制重新加载页面。

        在文件action.js中

        var showLoadingBoxSetIntervalVar;
        var showLoadingBoxCount = 0;
        var showLoadingBoxLoadedTimestamp = 0
        
        function showLoadingBox(text) {
        
            var showLoadingBoxSetIntervalVar=self.setInterval(function(){showLoadingBoxIpadRelaod()},1000);
            showLoadingBoxCount = 0
            showLoadingBoxLoadedTimestamp = new Date().getTime();
        
            //Here load the spinner
        
        }
        
        function showLoadingBoxIpadRelaod()
        {
            //Calculate difference between now and page loaded time minus threshold 500ms
            var diffTime = ( (new Date().getTime()) - showLoadingBoxLoadedTimestamp - 500)/1000;
        
            showLoadingBoxCount = showLoadingBoxCount + 1;
            var isiPad = navigator.userAgent.match(/iPad/i) != null;
        
            if(diffTime > showLoadingBoxCount && isiPad){
                location.reload();
            }
        }
        

        【讨论】:

          【解决方案11】:

          试试这个
          if(performance.navigation.type == 2) {
          警报(“你好”);
          }

          【讨论】:

          • 虽然这可能是解决问题的宝贵提示,但一个好的答案也可以证明解决方案。请EDIT 提供示例代码来说明您的意思。或者,考虑将其写为评论
          【解决方案12】:

          在我的 Angular 应用程序中,我使用此条件来解决问题。首先我在下面的index.html lilke 中添加了<strong>onunload=""</strong> 事件句柄

          <body class="mat-typography" onunload="">
              <app-root></app-root>
          </body>
          

          然后在我的组件 <strong>ngOnInit</strong> 方法中,我添加了如下代码

            public ngOnInit(): void {
              window.onpageshow = (event) => {
                  if (event.persisted) {
                      document.body.style.display = "none";
                      location.reload();
                  }
              };
              ...
            }
          

          【讨论】: