跨域iframe内容加载检测

18

我有一个相当有趣的问题。我有一个父页面,它将创建一个包含在对话框中的iframe jquery模态窗口。该iframe将填充来自第三方域名的内容。我的问题是,我需要创建一些对话框级别的JavaScript,以便检测iframe的内容是否成功加载。如果在5秒的时间范围内没有加载成功,则关闭对话框并返回用户到父页面。

我已经研究了许多解决方案,只有两个是真正有价值的。

  1. 让远程站点包含一行JavaScript代码:document.domain = 'our-domain.com'。
  2. 使用URL片段技巧,但我需要请求远程站点能够通过在URL末尾添加“#some_value”来修改URL,我的对话框窗口必须轮询URL,直到看到它或超时为止。

这些是否真的是我要处理的唯一选项?难道没有更简单的方法来检测这个吗?

我一直在研究是否有一种方法来轮询http响应错误,但这仍然受到相同限制的约束。

任何帮助将不胜感激。

谢谢


一个解决方法是使用 setTimeout - Shayan
6个回答

10

最简单的方法(如果你可以将代码添加到外部网站中)是让它们添加一个指向你域名上特殊HTML文件的不可见iframe。然后,可以使用 parent.parent.foo() 来通知原始窗口有关加载事件的信息。

仅监听“load”事件只会告诉你窗口是否已加载,但不会告诉你加载了什么或文档是否准备好进行交互。


这听起来像是一个明确的选择。我会建议开发人员考虑这个选项。谢谢! - rik_davis
2
+1 针对我的特定问题,您提出的解决方案是我认为唯一的解决方案!谢谢。 - Town
这个非常出色,尽管有些令人惊讶,直到你开始思考它。 - Kaganar
2
我不相信这在更新的Chrome版本中还能起作用(我使用62.0.3202.45进行了测试)。控制台显示错误:“未捕获的DOMException:阻止了一个具有来源...的框架访问跨源框架。” - HypeXR

5
尼古拉斯·扎卡斯(Nicholas Zakas)撰写了一篇关于检测iframe是否加载的文章:http://www.nczonline.net/blog/2009/09/15/iframes-onload-and-documentdomain/。基本上,您需要以下代码片段:
var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";

if (iframe.attachEvent){
    iframe.attachEvent("onload", function(){
        alert("Local iframe is now loaded.");
    });
} else {
    iframe.onload = function(){
        alert("Local iframe is now loaded.");
    };
}

document.body.appendChild(iframe);

我没有测试过,但我相当确定jQuery可以通过像这样做来处理它:$("#iframe").load(function () { alert("本地iframe现在已加载。"); });


谢谢你提供的代码片段。我会尝试一下,看看它是如何工作的。 - rik_davis
1
请您更新问题,使用addEventListener代替attatchEvent,因为后者已经过时了。addEventListener的事件名应该是load而不是onload - Shayan

4
你可以尝试使用 postMessage 在框架之间进行通信。
这将需要远程站点包含一些特定的JavaScript代码,在加载完成后向父文档发送消息。

我可以,但那只在IE8中有效。抱歉,我应该提到根据我们客户的规格说明,这个功能必须在IE 6、7和8中都能正常工作。 - rik_davis
[不要介意的广告] 我制作了一个微型框架,以便在帮助您不在网站中打开安全漏洞的同时简化操作:https://github.com/Okrajs/Okrajs - Omar Al-Ithawi

2
可以通过在 iframe 上使用 onload 处理程序来实现此操作。不幸的是(惊喜!),IE 使它变得困难。我唯一能让它工作的方法是为 iframe 组合 HTML,然后使用 innerHTML 将其附加到文档中。然后我必须轮询以查看 iframe 何时出现在 DOM 中,这取决于页面是否正在加载。以下是源代码链接:http://svn.openlaszlo.org/openlaszlo/trunk/lps/includes/source/iframemanager.js
请参阅 create()、__finishCreate() 和 gotload()。欢迎复制并自行使用!
祝好, Max Carlson OpenLaszlo.org

1
谢谢你提供的可能解决方案,Max。我和我的同事决定尝试使用postMessage方法。虽然这在IE 6和7上不起作用,但我们发现如果客户同意使用postMessage,它将满足我们的即时需求。我很乐意检查你的建议,因为一个人永远不能拥有太多的技巧。 :) - rik_davis

1
这是我检测跨域iframe加载的方法:
  1. 为iframe设置唯一id(您可以使用任何类型的标识符,都没有关系)
请注意,保留原文中的html标签。

<iframe id="crossDomainIframe" src=""> </iframe>

设置窗口事件监听器:
document.getElementById("crossDomainIframe").addEventListener('load',

 function actionToPerform(){
   //Do your onLoad actions here
 }

)

0

无论如何,您都需要从其他域的服务器得到某种形式的合作,因为您试图滥用同源策略(SOP)

第一个解决方案document.domain = ...如果域不同,则不起作用。它仅适用于子域和端口,如上面的链接中所述。

唯一允许不轮询跨域通信的选项是JSONP或使用JS函数回调进行脚本注入。此方法在所有Google API中都可用,并且效果很好。

我们在我们的博客上解释了将这些调用沙箱化以确保安全性的方法。虽然postMessage现在更好,但window.name hack具有在旧浏览器上工作的优势。

具有讽刺意味的是,SOP并不能阻止您将任何内容POST到另一个域。但您将无法读取响应。


2
好的,我并不想“滥用”SOP政策,但用户活动中对此的需求已经到了关键阶段。是的,我确实需要它在上述浏览器上运行。奇怪的是,目前没有其他浏览器在他们的规格中。我会去查看那些链接,并感谢您提供的额外信息。 - rik_davis

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