为什么加载 iframe 时 Safari 中不触发 popstate?

发布于 2024-11-08 12:26:32 字数 2081 浏览 0 评论 0 原文

在 Safari 中,如果存在 iframe 加载,并且用户通过后退或前进更改历史记录状态,则不会触发 popstate 事件,从而导致应用程序状态和窗口位置不同步。

我认为活跃的 XHR 请求会导致相同的行为,但我还无法确认这一点。

这是一个 jsfiddle,可以让您轻松重现该问题: http://jsfiddle.net/neonsilk/muHk8/

您只需单击中的链接/按钮顺序,从 1 到 6。

在 Safari 5.0.5 中,输出为:(

按时间倒序,重要部分是顶部的状态比较)

[1305665493096] /
[1305665493096] vs
[1305665493096] /node
---------------
[1305665489955] iframe loaded
---------------
[1305665489806] (did popstate or $.address change trigger?)
[1305665489805] called history.back()
[1305665489805] appended iframe
---------------
[1305665488428] popstate: /node
[1305665488427] $.address change: /node
---------------
[1305665487821] popstate: /
[1305665487821] $.address change: /
---------------
[1305665487179] $.address change: /node
---------------
[1305665486606] $.address change: /
---------------
[1305665485732] iframe loaded
---------------
[1305665485569] $.address change: /neonsilk/muHk8/show/
[1305665485568] $.address init

然而在 Chrome (11) 或 FireFox 中(4.0),输出如下所示:(

请注意,状态是同步的)

[1305665609499] /
[1305665609499] vs
[1305665609499] /
---------------
[1305665608360] iframe loaded
---------------
[1305665607770] popstate: /
[1305665607770] $.address change: /
[1305665607758] (did popstate or $.address change trigger?)
[1305665607758] called history.back()
[1305665607758] appended iframe
---------------
[1305665606870] popstate: /node
[1305665606869] $.address change: /node
---------------
[1305665606150] popstate: /
[1305665606149] $.address change: /
---------------
[1305665605551] $.address change: /node
---------------
[1305665604808] $.address change: /
---------------
[1305665603354] iframe loaded
---------------
[1305665602688] $.address change: /neonsilk/muHk8/show/
[1305665602682] $.address init
[1305665602676] popstate: /neonsilk/muHk8/show/

这是 Safari 中的错误吗?如果是的话,有人发现了解决方法吗?

(同样有趣的是,FireFox 和 Chrome 都会在页面加载时触发 popstate 事件。)

In Safari, if there's an iframe loading, and the user changes the history state by going back or forward, the popstate event isn't triggered, causing the application state and the window location to be out of sync.

I think that an active XHR request will cause the same behavior, but I haven't been able to confirm that yet.

Here's a jsfiddle that will allow you to easily reproduce the issue:
http://jsfiddle.net/neonsilk/muHk8/

You just have to click the links/buttons in order, from 1 to 6.

In Safari 5.0.5, the output is:

(reverse chronological, the important part is the state comparison at the top)

[1305665493096] /
[1305665493096] vs
[1305665493096] /node
---------------
[1305665489955] iframe loaded
---------------
[1305665489806] (did popstate or $.address change trigger?)
[1305665489805] called history.back()
[1305665489805] appended iframe
---------------
[1305665488428] popstate: /node
[1305665488427] $.address change: /node
---------------
[1305665487821] popstate: /
[1305665487821] $.address change: /
---------------
[1305665487179] $.address change: /node
---------------
[1305665486606] $.address change: /
---------------
[1305665485732] iframe loaded
---------------
[1305665485569] $.address change: /neonsilk/muHk8/show/
[1305665485568] $.address init

Yet in Chrome (11) or FireFox (4.0), the output looks like:

(note that the states are in sync)

[1305665609499] /
[1305665609499] vs
[1305665609499] /
---------------
[1305665608360] iframe loaded
---------------
[1305665607770] popstate: /
[1305665607770] $.address change: /
[1305665607758] (did popstate or $.address change trigger?)
[1305665607758] called history.back()
[1305665607758] appended iframe
---------------
[1305665606870] popstate: /node
[1305665606869] $.address change: /node
---------------
[1305665606150] popstate: /
[1305665606149] $.address change: /
---------------
[1305665605551] $.address change: /node
---------------
[1305665604808] $.address change: /
---------------
[1305665603354] iframe loaded
---------------
[1305665602688] $.address change: /neonsilk/muHk8/show/
[1305665602682] $.address init
[1305665602676] popstate: /neonsilk/muHk8/show/

Is this a bug in Safari? And if it is, has anyone discovered a workaround?

(It's also interesting that both FireFox and Chrome fire a popstate event on page load.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ぇ气 2024-11-15 12:26:32

更新

据我所知,这个错误是首先由 Ben Cherry 报告的< /a>.

这是相应的错误报告变更集

以及来自 Chromium 的问题

我一直无法弄清楚 Chrome 中的错误何时被修复。如果其他人有,我将非常感激这些信息。

我确实知道该错误存在于 WebKit 533.21.1(Safari 5.0.5 使用的)中,并且已被 534.36(Safari/WebKit nightly)修复——但我不知道,也无法解决找出哪个中间版本引入了修复程序。

有一个一个有用的图表,将 Chrome 版本映射到其 WebKit 版本。

最重要,当存在任何活动网络流量(图像加载、Ajax 请求等)而不仅仅是 iframe 时,此错误显然会发生。如果您正在尝试实现 History API 支持,或者您正在使用最新版本的 Asual 的 jQuery 地址插件(默认情况下启用历史记录支持),此错误可能会严重影响您的应用程序。


第二次更新

我想我找到了引入修复程序的确切 WebKit 版本 (534.10)。所以,如果您正在使用 jQuery 和 Asual 的 Address 插件,这里是实用的解决方案:

if (!($.browser.webkit === true && parseFloat($.browser.version) < 534.10)) {
    // only enable state support if WebKit version >= 534.10
    $.address.state("/base/path");
}

我希望这对某人有帮助!


我刚刚在最新的 WebKit nightly (r86671) 中测试了 fiddle,并且状态保持同步,所以它一定是一个错误。有趣的是,花费大量时间写下问题并立即提交会激发答案。

尽管如此,如果有人有解决方法,我们将不胜感激。

WebKit nightly (r86671):

[1305666598481] /
[1305666598481] vs
[1305666598480] /
---------------
[1305666597469] iframe loaded
---------------
[1305666597300] popstate: /
[1305666597298] $.address change: /
[1305666597270] (did popstate or $.address change trigger?)
[1305666597269] called history.back()
[1305666597269] appended iframe
---------------
[1305666596605] popstate: /node
[1305666596605] $.address change: /node
---------------
[1305666596008] popstate: /
[1305666596008] $.address change: /
---------------
[1305666595555] $.address change: /node
---------------
[1305666595142] $.address change: /
---------------
[1305666578600] iframe loaded
---------------
[1305666578400] $.address change: /_display/
[1305666578400] $.address init
---------------
[1305666577964] popstate: /_display/

同样有趣的是,Safari 现在与 Chrome 和 FireFox 的行为相匹配,在页面加载时触发 popstate。

Update

This bug was, as far as I can tell, first reported by Ben Cherry.

Here is the corresponding bug report and changeset from WebKit.

And the issue from Chromium.

I haven't been able to figure out when the bug was fixed in Chrome. If anyone else has, I would greatly appreciate the info.

I do know that the bug exists in WebKit 533.21.1 (which is what Safari 5.0.5 uses), and has been fixed by 534.36 (Safari/WebKit nightly) -- but I don't know, and haven't been able to figure out, which intermediary build introduced the fix.

There is a helpful chart that maps the Chrome version to its WebKit version.

Most importantly, this bug apprently occurs when there is any active network traffic (images loading, Ajax requests, etc.), and not just iframes. If you are trying to implement History API support, or you are using the latest version of Asual's jQuery Address plugin (which has history support enabled by default), this bug could seriously impact your application.


2nd Update

I think I tracked down the exact WebKit version where the fix was introduced (534.10). So, if you are using jQuery and Asual's Address plugin, here's the practical solution:

if (!($.browser.webkit === true && parseFloat($.browser.version) < 534.10)) {
    // only enable state support if WebKit version >= 534.10
    $.address.state("/base/path");
}

I hope this helps someone!


I just tested the fiddle in the latest WebKit nightly (r86671), and the state stays in sync, so it must be a bug. Funny how spending a lot of time writing up a question and submitting it immediately inspires the answer.

Still, if anyone has a workaround, it would be greatly appreciated.

WebKit nightly (r86671):

[1305666598481] /
[1305666598481] vs
[1305666598480] /
---------------
[1305666597469] iframe loaded
---------------
[1305666597300] popstate: /
[1305666597298] $.address change: /
[1305666597270] (did popstate or $.address change trigger?)
[1305666597269] called history.back()
[1305666597269] appended iframe
---------------
[1305666596605] popstate: /node
[1305666596605] $.address change: /node
---------------
[1305666596008] popstate: /
[1305666596008] $.address change: /
---------------
[1305666595555] $.address change: /node
---------------
[1305666595142] $.address change: /
---------------
[1305666578600] iframe loaded
---------------
[1305666578400] $.address change: /_display/
[1305666578400] $.address init
---------------
[1305666577964] popstate: /_display/

Also interesting that Safari now matches the behavior of Chrome and FireFox, firing popstate on page load.

凉墨 2024-11-15 12:26:32
  1. Img/script/iframe 加载会破坏 popstate,但 XHR 不受此错误影响!
  2. 文档加载之前的后退/前进将会延迟。加载时我们将得到一个 popstate。 (即使有多个后退/前进,也只会弹出最后一个状态。)

(在 Android 2.2 和 iOS 4.3.3 上的 Safari 下测试)

因此解决方法是:

使用 XHR 而不是 img 来跟踪/预加载加载事件后的任何内容。

如果用户在加载事件之前执行后退/前进两次或更多次,我们可能仍然会遇到问题,但是如果加载时间很短,用户很少会执行多个操作,并且因为最终状态是正确的,所以我认为这是可以的。

另一个解决方法是:

使用计时器监视 url,如果在没有 popstate 的情况下更改了 url,请自行完成脏工作。 (正如这个bug的评论,Facebook曾经使用过这个解决方案。)

但是这个方法太复杂了,因为你应该将状态序列化/反序列化为url或localstorage/sessionstorage,就像你没有History API一样。所以我认为这不是一个可以接受的解决方法。

  1. Img/script/iframe loading will break popstate, but XHR is NOT affected by this bug!
  2. back/forward before document load will be delayed. We will get ONE popstate when load. (only the last state will be pop even there are multiple back/forward.)

(Tested under Android 2.2 and Safari on iOS 4.3.3)

So the workaround is:

Use XHR instead of img for tracking/preload whatever after load event.

We may still meet problem if user do back/forward twice or more before load event, but user will rarely do multiple actions if load time is short, and because the final state is correct, so I think it's ok.

And another workaround is:

Watching the url using timer, if it's changed without popstate, do dirty work by yourself. (As the comments of this bug, Facebook used to use this solution.)

But this method is too complicated because you should serialize/deserialize the state to url or localstorage/sessionstorage, just like you don't have History API. So I don't think it's a acceptable workaround.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文