It seems that $('#someIframe').load(function(){...})
won't fire if it is attached after the iframe has finished loading. Is that correct?
似乎$('#someIframe')。load(function(){...})如果在iframe完成加载后附加,则不会触发。那是对的吗?
What I'd really like is to have a function that is always called once when or after an iframe has loaded. To make this clearer, here are two cases:
我真正喜欢的是拥有一个在加载iframe时或之后总是被调用一次的函数。为了更清楚,这里有两种情况:
- Iframe hasn't loaded yet: run a callback function once it loads.
- Iframe尚未加载:加载后运行回调函数。
- Iframe has already loaded: run the callback immediately.
- iframe已经加载:立即运行回调。
How can I do this?
我怎样才能做到这一点?
5 个解决方案
#1
25
I've banged my head against a wall until I found out what's happening here.
在我发现这里发生了什么之前,我一直撞到了墙上。
Background information
- Using
.load()
isn't possible if the iframe has already been loaded (event will never fire) - 如果已经加载iframe,则无法使用.load()(事件永远不会触发)
- Using
.ready()
on an iframe element isn't supported (reference) and will call the callback immediately even if the iframe isn't loaded yet - 不支持在iframe元素上使用.ready()(引用),即使iframe尚未加载,也会立即调用回调
- Using
postMessage
or a calling a container function onload
inside the iframe is only possible when having control over it - 使用postMessage或在iframe内部加载时调用容器函数只有在控制它时才有可能
- Using
$(window).load()
on the container would also wait for other assets to load, like images and other iframes. This is not a solution if you want to wait only for a specific iframe - 在容器上使用$(window).load()也会等待加载其他资源,例如图像和其他iframe。如果您只想等待特定的iframe,这不是解决方案
- Checking
readyState
in Chrome for an alredy fired onload event is meaningless, as Chrome initializes every iframe with an "about:blank" empty page. ThereadyState
of this page may becomplete
, but it's not thereadyState
of the page you expect (src
attribute). - 在Chrome中检查readyState以获取alredy已启动的onload事件是没有意义的,因为Chrome会使用“about:blank”空白页初始化每个iframe。此页面的readyState可能已完成,但它不是您期望的页面的readyState(src属性)。
Solution
The following is necessary:
以下是必要的:
- If the iframe is not loaded yet we can observe the
.load()
event - 如果尚未加载iframe,我们可以观察.load()事件
- If the iframe has been loaded already we need to check the
readyState
- 如果已经加载了iframe,我们需要检查readyState
- If the
readyState
iscomplete
, we can normally assume that the iframe has already been loaded. However, because of the above-named behavior of Chrome we furthermore need to check if it's thereadyState
of an empty page - 如果readyState完成,我们通常可以假设已经加载了iframe。但是,由于Chrome的上述行为,我们还需要检查它是否为空页面的readyState
- If so, we need to observe the
readyState
in an interval to check if the actual document (related to the src attribute) iscomplete
- 如果是这样,我们需要在一个间隔中观察readyState以检查实际文档(与src属性相关)是否完整
I've solved this with the following function. It has been (transpiled to ES5) successfully tested in
我用以下功能解决了这个问题。它已经(转化为ES5)成功通过测试
- Chrome 49
- Chrome 49
- Safari 5
- Safari 5
- Firefox 45
- Firefox 45
- IE 8, 9, 10, 11
- IE 8,9,10,11
- Edge 24
- 边缘24
- iOS 8.0 ("Safari Mobile")
- iOS 8.0(“Safari Mobile”)
- Android 4.0 ("Browser")
- Android 4.0(“浏览器”)
Function taken from jquery.mark
函数取自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();
}
};
Working example
Consisting of two files (index.html and iframe.html): index.html:
由两个文件(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:
Iframe.html的:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Child</title>
</head>
<body>
<p>Lorem ipsum</p>
</body>
</html>
You can also change the src
attribute inside index.html
to e.g. "http://example.com/". Just play around with it.
您还可以将index.html中的src属性更改为例如“http://example.com/”。只是玩弄它。
#2
1
I'd use postMessage. The iframe can assign its own onload event and post to the parent. If there are timing issues just make sure to assign the parent's postMessage handler before creating the iframe.
我使用postMessage。 iframe可以分配自己的onload事件并发布到父级。如果存在计时问题,请确保在创建iframe之前分配父级的postMessage处理程序。
For this to work the iframe must know the url of the parent, for instance by passing a GET parameter to the iframe.
为此,iframe必须知道父级的url,例如通过将GET参数传递给iframe。
#3
0
I tried very hard to come to a solution that worked consistently cross browser. IMPORTANT: I was not able to come to such a solution. But here is as far as I got:
我非常努力地找到一个跨浏览器的解决方案。重要提示:我无法找到这样的解决方案。但就我而言:
// 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);
});
}
}
and I was using it like this:
我这样使用它:
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);
}
});
Note that even this does not solve the problem for me. Here are some problems with this solution:
请注意,即使这样也无法解决问题。以下是此解决方案的一些问题:
- In onReady, for iframes that were added dynamically, iframeDocument.readyState seems to be stuck at "uninitialized" and thus the callback never fires
- 在onReady中,对于动态添加的iframe,iframeDocument.readyState似乎停留在“未初始化”,因此回调永远不会触发
- The whole setup still doesn't seem to work in firefox for some reason. It almost seems like the setInterval function is cleared externally.
- 由于某种原因,整个设置似乎仍然没有在Firefox中工作。几乎看起来外部清除了setInterval函数。
- Note that some of these problems only happen when there is a lot of other stuff loading on the page, which makes the timing of these things less deterministic.
- 请注意,其中一些问题只会在页面上加载大量其他内容时发生,这使得这些事情的时间安排不那么具有确定性。
So if anyone can improve upon this, it would be much appreciated.
因此,如果有人可以改进这一点,那将非常感激。
#4
-1
Only when the content inside the iframe is loaded innerDoc is true and fires code inside the if.
只有当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);
});};
example
#5
-1
I think you should try using onreadystatechange
event.
我认为你应该尝试使用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);
});
EDIT: the context is not what I think it is. you can just check the readyState of iframe document and everything should be fine.
编辑:上下文不是我认为的。你可以检查iframe文件的readyState,一切都应该没问题。
OP: This is a packaged up function I made from the concepts described above:
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);
}
}
#1
25
I've banged my head against a wall until I found out what's happening here.
在我发现这里发生了什么之前,我一直撞到了墙上。
Background information
- Using
.load()
isn't possible if the iframe has already been loaded (event will never fire) - 如果已经加载iframe,则无法使用.load()(事件永远不会触发)
- Using
.ready()
on an iframe element isn't supported (reference) and will call the callback immediately even if the iframe isn't loaded yet - 不支持在iframe元素上使用.ready()(引用),即使iframe尚未加载,也会立即调用回调
- Using
postMessage
or a calling a container function onload
inside the iframe is only possible when having control over it - 使用postMessage或在iframe内部加载时调用容器函数只有在控制它时才有可能
- Using
$(window).load()
on the container would also wait for other assets to load, like images and other iframes. This is not a solution if you want to wait only for a specific iframe - 在容器上使用$(window).load()也会等待加载其他资源,例如图像和其他iframe。如果您只想等待特定的iframe,这不是解决方案
- Checking
readyState
in Chrome for an alredy fired onload event is meaningless, as Chrome initializes every iframe with an "about:blank" empty page. ThereadyState
of this page may becomplete
, but it's not thereadyState
of the page you expect (src
attribute). - 在Chrome中检查readyState以获取alredy已启动的onload事件是没有意义的,因为Chrome会使用“about:blank”空白页初始化每个iframe。此页面的readyState可能已完成,但它不是您期望的页面的readyState(src属性)。
Solution
The following is necessary:
以下是必要的:
- If the iframe is not loaded yet we can observe the
.load()
event - 如果尚未加载iframe,我们可以观察.load()事件
- If the iframe has been loaded already we need to check the
readyState
- 如果已经加载了iframe,我们需要检查readyState
- If the
readyState
iscomplete
, we can normally assume that the iframe has already been loaded. However, because of the above-named behavior of Chrome we furthermore need to check if it's thereadyState
of an empty page - 如果readyState完成,我们通常可以假设已经加载了iframe。但是,由于Chrome的上述行为,我们还需要检查它是否为空页面的readyState
- If so, we need to observe the
readyState
in an interval to check if the actual document (related to the src attribute) iscomplete
- 如果是这样,我们需要在一个间隔中观察readyState以检查实际文档(与src属性相关)是否完整
I've solved this with the following function. It has been (transpiled to ES5) successfully tested in
我用以下功能解决了这个问题。它已经(转化为ES5)成功通过测试
- Chrome 49
- Chrome 49
- Safari 5
- Safari 5
- Firefox 45
- Firefox 45
- IE 8, 9, 10, 11
- IE 8,9,10,11
- Edge 24
- 边缘24
- iOS 8.0 ("Safari Mobile")
- iOS 8.0(“Safari Mobile”)
- Android 4.0 ("Browser")
- Android 4.0(“浏览器”)
Function taken from jquery.mark
函数取自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();
}
};
Working example
Consisting of two files (index.html and iframe.html): index.html:
由两个文件(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:
Iframe.html的:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Child</title>
</head>
<body>
<p>Lorem ipsum</p>
</body>
</html>
You can also change the src
attribute inside index.html
to e.g. "http://example.com/". Just play around with it.
您还可以将index.html中的src属性更改为例如“http://example.com/”。只是玩弄它。
#2
1
I'd use postMessage. The iframe can assign its own onload event and post to the parent. If there are timing issues just make sure to assign the parent's postMessage handler before creating the iframe.
我使用postMessage。 iframe可以分配自己的onload事件并发布到父级。如果存在计时问题,请确保在创建iframe之前分配父级的postMessage处理程序。
For this to work the iframe must know the url of the parent, for instance by passing a GET parameter to the iframe.
为此,iframe必须知道父级的url,例如通过将GET参数传递给iframe。
#3
0
I tried very hard to come to a solution that worked consistently cross browser. IMPORTANT: I was not able to come to such a solution. But here is as far as I got:
我非常努力地找到一个跨浏览器的解决方案。重要提示:我无法找到这样的解决方案。但就我而言:
// 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);
});
}
}
and I was using it like this:
我这样使用它:
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);
}
});
Note that even this does not solve the problem for me. Here are some problems with this solution:
请注意,即使这样也无法解决问题。以下是此解决方案的一些问题:
- In onReady, for iframes that were added dynamically, iframeDocument.readyState seems to be stuck at "uninitialized" and thus the callback never fires
- 在onReady中,对于动态添加的iframe,iframeDocument.readyState似乎停留在“未初始化”,因此回调永远不会触发
- The whole setup still doesn't seem to work in firefox for some reason. It almost seems like the setInterval function is cleared externally.
- 由于某种原因,整个设置似乎仍然没有在Firefox中工作。几乎看起来外部清除了setInterval函数。
- Note that some of these problems only happen when there is a lot of other stuff loading on the page, which makes the timing of these things less deterministic.
- 请注意,其中一些问题只会在页面上加载大量其他内容时发生,这使得这些事情的时间安排不那么具有确定性。
So if anyone can improve upon this, it would be much appreciated.
因此,如果有人可以改进这一点,那将非常感激。
#4
-1
Only when the content inside the iframe is loaded innerDoc is true and fires code inside the if.
只有当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);
});};
example
#5
-1
I think you should try using onreadystatechange
event.
我认为你应该尝试使用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);
});
EDIT: the context is not what I think it is. you can just check the readyState of iframe document and everything should be fine.
编辑:上下文不是我认为的。你可以检查iframe文件的readyState,一切都应该没问题。
OP: This is a packaged up function I made from the concepts described above:
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);
}
}