如果在iframe完成加载后附加$('#someIframe').load(function(){...})
,则{{1}}似乎不会触发。那是对的吗?
我真正喜欢的是拥有一个在加载iframe时或之后始终调用一次的函数。为了更清楚,这里有两种情况:
我该怎么做?
答案 0 :(得分:33)
在我发现这里发生的事情之前,我一直撞到了墙上。
.load()
.ready()
(reference),即使iframe尚未加载,也会立即调用回调postMessage
或在iframe内load
调用容器功能$(window).load()
也会等待加载其他资源,例如图片和其他iframe。如果您只想等待特定的iframe readyState
以查找alredy已启动的onload事件是没有意义的,因为Chrome会使用“about:blank”空白页初始化每个iframe。此页面的readyState
可能是complete
,但它不是您期望的页面readyState
(src
属性)。 以下是必要的:
.load()
事件readyState
readyState
为complete
,我们通常可以假设已经加载了iframe。但是,由于Chrome的上述行为,我们还需要检查它是否为空页的readyState
readyState
以检查实际文档(与src属性相关)是否为complete
我用以下功能解决了这个问题。已经(转化为ES5)成功测试了
/**
* 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 强>:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Parent</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-1.12.2.min.js"></script>
<script>
$(function() {
/**
* 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();
}
};
var $iframe = $("iframe");
onIframeReady($iframe, function($contents) {
console.log("Ready to got");
console.log($contents.find("*"));
}, function() {
console.log("Can not access iframe");
});
});
</script>
<iframe src="iframe.html"></iframe>
</body>
</html>
<强> Iframe.html的强>:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Child</title>
</head>
<body>
<p>Lorem ipsum</p>
</body>
</html>
您还可以将src
内的index.html
属性更改为“http://example.com/”。只是玩弄它。
答案 1 :(得分:1)
我会使用postMessage。 iframe可以分配自己的onload事件并发布到父级。如果存在计时问题,请确保在创建iframe之前分配父级的postMessage处理程序。
为此,iframe必须知道父级的url,例如将GET参数传递给iframe。
答案 2 :(得分:1)
我遇到了同样的问题,就我而言,我只是检查了onload函数是否被触发。
var iframe = document.getElementById("someIframe");
var loadingStatus = true;
iframe.onload = function () {
loadingStatus = false;
//do whatever you want [in my case I wants to trigger postMessage]
};
if (loadingStatus)
//do whatever you want [in my case I wants to trigger postMessage]
答案 3 :(得分:0)
我非常努力地找到一个跨浏览器的解决方案。重要提示:我无法找到这样的解决方案。但就我而言:
// 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);
}
});
请注意,即使是也无法解决问题。以下是此解决方案的一些问题:
因此,如果有人能够改进这一点,我们将非常感激。
答案 4 :(得分:-1)
只有当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);
});};
答案 5 :(得分:-1)
我认为你应该尝试使用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:这是我根据上述概念制作的打包功能:
// 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);
}
}