如果在 iframe 完成加载后附加它似乎$('#someIframe').load(function(){...})
不会触发。那是对的吗?
我真正想要的是拥有一个在 iframe 加载时或加载后总是调用一次的函数。为了更清楚地说明这一点,这里有两种情况:
- iframe 尚未加载:加载后运行回调函数。
- iframe 已经加载:立即运行回调。
我怎样才能做到这一点?
如果在 iframe 完成加载后附加它似乎$('#someIframe').load(function(){...})
不会触发。那是对的吗?
我真正想要的是拥有一个在 iframe 加载时或加载后总是调用一次的函数。为了更清楚地说明这一点,这里有两种情况:
我怎样才能做到这一点?
我一直用头撞墙,直到我发现这里发生了什么。
.load()
如果 iframe 已经加载,则无法使用(事件永远不会触发).ready()
不支持在 iframe 元素上使用( reference ),即使 iframe 尚未加载,也会立即调用回调postMessage
或调用容器函数load
$(window).load()
也会等待其他资源加载,例如图像和其他 iframe。如果您只想等待特定的 iframe,这不是解决方案readyState
已经触发的 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/ ”。只是玩弄它。
我会使用postMessage。iframe 可以分配自己的 onload 事件并发布到父级。如果存在时间问题,请确保在创建 iframe 之前分配父级的 postMessage 处理程序。
为此,iframe 必须知道父级的 url,例如通过将 GET 参数传递给 iframe。
我有同样的问题。就我而言,我只是检查了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]
如果 iFrame 已加载,此函数将立即运行您的回调函数,或者等到 iFrame 完全加载后再运行您的回调函数。只需将您想要在 iFrame 完成加载时运行的回调函数和元素传递给此函数:
function iframeReady(callback, iframeElement) {
const iframeWindow = iframeElement.contentWindow;
if ((iframeElement.src == "about:blank" || (iframeElement.src != "about:blank" && iframeWindow.location.href != "about:blank")) && iframeWindow.document.readyState == "complete") {
callback();
} else {
iframeWindow.addEventListener("load", callback);
}
}
这将解决最常见的问题,例如 chrome 使用 about:blank 初始化 iframe 和 iFrame 不支持 DOMContentLoaded 事件。请参阅此https://stackoverflow.com/a/69694808/15757382答案以获得解释。
我非常努力地找到了一个跨浏览器始终有效的解决方案。重要提示:我无法得出这样的解决方案。但据我所知,这里是:
// 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);
}
});
请注意,即使这样也不能解决我的问题。以下是此解决方案的一些问题:
因此,如果有人可以对此进行改进,将不胜感激。
这是我在没有 jQuery 依赖的 Typescript 中的解决方案
export function whenIframeLoaded(iframe: HTMLIFrameElement): Promise<void> {
return new Promise((resolve, reject) => {
try {
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
if (iframeDocument.readyState === 'complete') {
resolve();
} else {
iframe.addEventListener(
'load', () => resolve(),
{ once: true }
);
}
} catch {
reject();
}
});
}
我怎么称呼它
const iframe = document.getElementById(iframeName) as HTMLIFrameElement;
await whenIframeLoaded(iframe);
仅当 iframe 中的内容被加载时,innerDoc 为 true 并在 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
event.
$(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);
}
}