如何检测何时已加载iframe

似乎$('#someIframe').load(function(){...})如果在iframe完成加载后附加,则不会触发。 那是对的吗?

我真正喜欢的是拥有一个在加载iframe时或之后总是被调用一次的函数。 为了更清楚,这里有两种情况:

  • Iframe尚未加载加载后运行回调函数。
  • iframe已经加载 :立即运行回调。

我怎样才能做到这一点?

在我发现这里发生了什么之前,我一直撞到了墙上。

背景资料

  • 如果已经加载iframe,则无法使用.load() (事件永远不会触发)
  • 不支持在iframe元素上使用.ready() ( 引用 ),即使iframe尚未加载,也会立即调用回调
  • 使用postMessage或在iframe内部load调用容器函数只有在控制它时才有可能
  • 在容器上使用$(window).load()也会等待加载其他资源,例如图像和其他iframe。 如果您只想等待特定的iframe,这不是解决方案
  • 在Chrome中检查readyState以获取alredy已启动的onload事件是没有意义的,因为Chrome会使用“about:blank”空白页初始化每个iframe。 此页面的readyState可能已complete ,但它不是您期望的页面的readyStatesrc属性)。

以下是必要的:

  1. 如果尚未加载iframe,我们可以观察.load()事件
  2. 如果已经加载了iframe,我们需要检查readyState
  3. 如果readyState complete ,我们通常可以假设已经加载了iframe。 但是,由于Chrome的上述行为,我们还需要检查它是否为空页面的readyState
  4. 如果是这样,我们需要在一个间隔中观察readyState以检查实际文档(与src属性相关)是否complete

我用以下function解决了这个问题。 它已经(转化为ES5)成功通过测试

  • Chrome 49
  • Safari 5
  • Firefox 45
  • IE 8,9,10,11
  • 边缘24
  • iOS 8.0(“Safari Mobile”)
  • Android 4.0(“浏览器”)

函数取自jquery.mark

 /** * Will wait for an iframe to be ready * for DOM manipulation. Just listening for * the load event will only work if the iframe * is not already loaded. If so, it is necessary * to observe the readyState. The issue here is * that Chrome will initialize iframes with * "about:blank" and set its readyState to complete. * So it is furthermore necessary to check if it's * the readyState of the target document property. * Errors that may occur when trying to access the iframe * (Same-Origin-Policy) will be catched and the error * function will be called. * @param {jquery} $i - The jQuery iframe element * @param {function} successFn - The callback on success. Will * receive the jQuery contents of the iframe as a parameter * @param {function} errorFn - The callback on error */ var onIframeReady = function($i, successFn, errorFn) { try { const iCon = $i.first()[0].contentWindow, bl = "about:blank", compl = "complete"; const callCallback = () => { try { const $con = $i.contents(); if($con.length === 0) { // https://git.io/vV8yU throw new Error("iframe inaccessible"); } successFn($con); } catch(e) { // accessing contents failed errorFn(); } }; const observeOnload = () => { $i.on("load.jqueryMark", () => { try { const src = $i.attr("src").trim(), href = iCon.location.href; if(href !== bl || src === bl || src === "") { $i.off("load.jqueryMark"); callCallback(); } } catch(e) { errorFn(); } }); }; if(iCon.document.readyState === compl) { const src = $i.attr("src").trim(), href = iCon.location.href; if(href === bl && src !== bl && src !== "") { observeOnload(); } else { callCallback(); } } else { observeOnload(); } } catch(e) { // accessing contentWindow failed errorFn(); } }; 

工作实例

由两个文件(index.html和iframe.html)组成: index.html

     Parent        

iframe.html

     Child   

Lorem ipsum

您还可以将index.htmlsrc属性更改为例如“ http://example.com/ ”。 只是玩弄它。

我使用postMessage 。 iframe可以分配自己的onload事件并发布到父级。 如果存在计时问题,请确保在创建iframe之前分配父级的postMessage处理程序。

为此,iframe必须知道父级的url,例如通过将GET参数传递给iframe。

我非常努力地找到一个跨浏览器的解决方案。 重要提示:我无法找到这样的解决方案。 但就我而言:

 // runs a function after an iframe node's content has loaded // note, this almost certainly won't work for frames loaded from a different domain // secondary note - this doesn't seem to work for chrome : ( // another note - doesn't seem to work for nodes created dynamically for some reason function onReady(iframeNode, f) { var windowDocument = iframeNode[0].contentWindow.document; var iframeDocument = windowDocument?windowDocument : iframeNode[0].contentWindow.document; if(iframeDocument.readyState === 'complete') { f(); } else { iframeNode.load(function() { var i = setInterval(function() { if(iframeDocument.readyState === 'complete') { f(); clearInterval(i); } }, 10); }); } } 

我这样使用它:

 onReady($("#theIframe"), function() { try { var context = modal[0].contentWindow; var i = setInterval(function() { if(context.Utils !== undefined && context.$) { // this mess is to attempt to get it to work in firefox context.$(function() { var modalHeight = context.someInnerJavascript(); clearInterval(i); }); } }, 10); } catch(e) { // ignore console.log(e); } }); 

请注意, 即使这样也无法解决问题。 以下是此解决方案的一些问题:

  • 在onReady中,对于动态添加的iframe,iframeDocument.readyState似乎停留在“未初始化”,因此回调永远不会触发
  • 由于某种原因,整个设置似乎仍然没有在Firefox中工作。 几乎看起来外部清除了setInterval函数。
  • 请注意,其中一些问题只会在页面上加载大量其他内容时发生,这使得这些事情的时间安排不那么具有确定性。

因此,如果有人可以改进这一点,那将非常感激。

只有当iframe中的内容被加载时,innerDoc才为真,并在if内部触发代码。

  window.onload = function(){ function manipulateIframe(iframeId, callback) { var iframe = document.getElementById(iframeId).contentWindow.document; callback(iframe); }; manipulateIframe('IFwinEdit_forms_dr4r3_forms_1371601293572', function (iframe) { console.log(iframe.body); });}; 

我认为你应该尝试使用onreadystatechange事件。

http://jsfiddle.net/fk8fc/3/

 $(function () { var innerDoc = ($("#if")[0].contentDocument) ? $("#if")[0].contentDocument : $("#if")[0].contentWindow.document; console.debug(innerDoc); $("#if").load( function () { alert("load"); alert(innerDoc.readyState) }); innerDoc.onreadystatechange = function () { alert(innerDoc.readyState) }; setTimeout(innerDoc.onreadystatechange, 5000); }); 

编辑:上下文不是我认为的。 你可以检查iframe文件的readyState,一切都应该没问题。

OP:这是我用上述概念制作的打包function:

 // runs a function after an iframe node's content has loaded // note, this almost certainly won't work for frames loaded from a different domain onReady: function(iframeNode, f) { var windowDocument = iframeNode[0].contentWindow.document; var iframeDocument = windowDocument?windowDocument : iframeNode[0].contentWindow.document if(iframeDocument.readyState === 'complete') { f(); } else { iframeNode.load(f); } }