如何检测何时已加载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
,但它不是您期望的页面的readyState
(src
属性)。
解
以下是必要的:
- 如果尚未加载iframe,我们可以观察
.load()
事件 - 如果已经加载了iframe,我们需要检查
readyState
- 如果
readyState
complete
,我们通常可以假设已经加载了iframe。 但是,由于Chrome的上述行为,我们还需要检查它是否为空页面的readyState
- 如果是这样,我们需要在一个间隔中观察
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.html
的src
属性更改为例如“ 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
事件。
$(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); } }