如何在iframe中解决两次或根本没有触发的IE11 localStorage事件?

Han*_*ans 40 javascript iframe events local-storage internet-explorer-11

我猜这是一个错误,但我还没有找到任何关于此的讨论.

众所周知,IE10将(针对规范)本地存储事件(即,在触发事件的同一全局执行上下文中),但IE11似乎更偏离规范(http://www.w3.org)/TR/webstorage /)当涉及到同域iframe时:

  • 如果iframe嵌入在触发存储事件的页面中,则该事件将在该iframe中触发TWICE.
  • 如果iframe嵌入在与触发存储事件的页面不同的页面中,则该事件将不会在该iframe中触发.
  • 如果事件是从iframe触发,则会在本地触发TWICE,在同一页面中嵌入的任何其他iframe触发TWICE,但在其他页面的iframe中则不会.

您可以通过在两个单独的选项卡中打开以下链接来测试此问题:http://hansifer.com/main.html

任何人都对这个怪癖有任何见解?

最新测试版本: IE v11.0.9600.16476 (更新2016-08-13:显然它是相关的"更新版本",而不是IE的"关于"对话框中报告的"版本")

链接到错误报告: https ://connect.microsoft.com/IE/feedback/details/811546/ie11-localstorage-events-fire-twice-or-not-at-all-in-iframes

更新2015-10-26

我只是注意到这似乎在v11.0.9600.18059中修复了,虽然我无法分辨修复程序何时删除,因为它似乎没有在任何最近的KB中被引用.

不幸的是,IE11 localStorage事件在其他方面仍然存在异常(尽管这些与本文中提出的问题不同):

  • IE在窗口上下文中引发localStorage事件,从中调用localStorage设置或删除触发事件的事件.localStorage事件只应在同一来源的外部窗口上下文中引发.(更新:在EdgeHTML 13.10586中工作)

  • IE使用空字符串,而不是null用于e.oldValue/e.newValue在添加存储物品/移除.(更新:仍然是EdgeHTML 13.10586中的问题)

  • IE不确定地调用localStorage事件处理程序,或者在设置/删除之前或之后生效,而不是一致地执行AFTER.

更新2015-12-24

看来这个bug被转移到了Edge(测试过的EdgeHTML 13.10586)

更新2016-02-02

韦尔普,没关系.在IE v11.63.10586.0中再次观察到此错误(更新2016-08-13:显然它是相关的"更新版本",而不是IE的"关于"对话框中报告的"版本")

更新2016-08-13

现在似乎已在IE(更新版本11.0.34)中修复了此问题,尽管存储事件仍然在原始窗口中针对规范(如上所述的已知长期问题)触发.

我发现这个KB包含在IE 2016年6月14日的安全更新中,尽管根据其描述它只涉及上面的第二个项目符号.

至于Edge(经测试的EdgeHTML 14.14393),似乎现在已经修复了这个问题,但是存在一个新问题:存储事件不会在同一页面的同源帧中触发.

在这里分别向MS报告.

Sch*_*ien 3

如果您想要限制某个事件被多次调用,无论调用原因如何,请使用标志来阻止后续事件。有几种策略:

.1. 基于时间的节流。假设你有一个函数“func”,并且你希望它在 200 毫秒内仅被调用一次:

function func(){
  if (document.func_lock) return;
  document.func_lock=true; // block future calls
  setTimeout(function(){document.func_lock=null;},300);
}
Run Code Online (Sandbox Code Playgroud)

当同时触发多个事件时,您可以预期所有事件都会在 300 毫秒的窗口内到达。上面的代码可以通过利用定时器句柄来简化:

function func(){
  if (document.func_lock) return;
  document.func_lock=setTimeout(function(){return;},300);
}
Run Code Online (Sandbox Code Playgroud)

当定时器到期时,锁会自动解除。

.2. 使用回调删除该标志。这个过程很简单,所以我不会在这里发布示例代码。

在标志放置方面,您可以使用任何唯一的 DOM 对象。由于我不知道您的应用程序的上下文,因此我在这里仅使用“文档”。您还可以使用特定于您的应用程序的哈希键,因为您已经在处理本地存储。概念是一样的。