Capture iframe load complete event
<iframe>
elements have a load
event for that.
How you listen to that event is up to you, but generally the best way is to:
1) create your iframe programatically
It makes sure your load
listener is always called by attaching it before the iframe starts loading.
<script>
var iframe = document.createElement('iframe');
iframe.onload = function() { alert('myframe is loaded'); }; // before setting 'src'
iframe.src = '...';
document.body.appendChild(iframe); // add it to wherever you need it in the document
</script>
2) inline javascript, is another way that you can use inside your HTML markup.
<script>
function onMyFrameLoad() {
alert('myframe is loaded');
};
</script>
<iframe id="myframe" src="..." onload="onMyFrameLoad(this)"></iframe>
3) You may also attach the event listener after the element, inside a <script>
tag, but keep in mind that in this case, there is a slight chance that the iframe is already loaded by the time you get to adding your listener. Therefore it's possible that it will not be called (e.g. if the iframe is very very fast, or coming from cache).
<iframe id="myframe" src="..."></iframe>
<script>
document.getElementById('myframe').onload = function() {
alert('myframe is loaded');
};
</script>
Also see my other answer about which elements can also fire this type of load
event
How to call a javascript function when iframe finished loading?
try this
<iframe id ='myframe' src='http://www.mysite.com/doit.php' onload="onLoadHandler();"></iframe>
<script type="text/javascript">
function onLoadHandler() {
alert('loaded');
}
</script>
How do I fire an event when a iframe has finished loading in jQuery?
I'm pretty certain that it cannot be done.
Pretty much anything else than PDF works, even Flash. (Tested on Safari, Firefox 3, IE 7)
Too bad.
Checking if iframe contents are loaded?
To be notified when the iFrame is loaded, you can still use the onload event today.
Create the iFrame and set an ID for JavaScript:
<iframe id="test" src="SRC_URL"></iframe>
Now access the iFrame with JavaScript and set an EventListener for the load event:
const iframe = document.getElementById("test");
iframe.addEventListener("load", function() {
console.log("Finish");
});
When the iFrame has finished loading, "Finish" is logged in the console. I have tested it with Google Chrome and it works fine.
Within the EventHandler you can then perform actions. For example, send a message:
iframe.contentWindow.postMessage({ title: "Hi", message: "Seems to work" }, targetOrigin);
Please also make sure that you have permission to embed the web page (X-frame options).
How to detect when an iframe has already been loaded
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) - Using
.ready()
on an iframe element isn't supported (reference) and will call the callback immediately even if the iframe isn't loaded yet - Using
postMessage
or a calling a container function onload
inside the iframe is only possible when having control over it - 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 - 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).
Solution
The following is necessary:
- If the iframe is not loaded yet we can observe the
.load()
event - If the iframe has been loaded already we need to check the
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 - If so, we need to observe the
readyState
in an interval to check if the actual document (related to the src attribute) iscomplete
I've solved this with the following function. It has been (transpiled to ES5) successfully tested in
- Chrome 49
- Safari 5
- Firefox 45
- IE 8, 9, 10, 11
- Edge 24
- iOS 8.0 ("Safari Mobile")
- Android 4.0 ("Browser")
Function taken from 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:
<!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>
You can also change the src
attribute inside index.html
to e.g. "http://example.com/". Just play around with it.
Detecting when Iframe content has loaded (Cross browser)
to detect when the iframe has loaded and its document is ready?
It's ideal if you can get the iframe to tell you itself from a script inside the frame. For example it could call a parent function directly to tell it it's ready. Care is always required with cross-frame code execution as things can happen in an order you don't expect. Another alternative is to set ‘var isready= true;’ in its own scope, and have the parent script sniff for ‘contentWindow.isready’ (and add the onload handler if not).
If for some reason it's not practical to have the iframe document co-operate, you've got the traditional load-race problem, namely that even if the elements are right next to each other:
<img id="x" ... />
<script type="text/javascript">
document.getElementById('x').onload= function() {
...
};
</script>
there is no guarantee that the item won't already have loaded by the time the script executes.
The ways out of load-races are:
on IE, you can use the ‘readyState’ property to see if something's already loaded;
if having the item available only with JavaScript enabled is acceptable, you can create it dynamically, setting the ‘onload’ event function before setting source and appending to the page. In this case it cannot be loaded before the callback is set;
the old-school way of including it in the markup:
<img onload="callback(this)" ... />
Inline ‘onsomething’ handlers in HTML are almost always the wrong thing and to be avoided, but in this case sometimes it's the least bad option.
Related Topics
JavaScript Es6 Computational/Time Complexity of Collections
Why Does JavaScript Replace Only First Instance When Using Replace
Google Chrome Console.Log() Inconsistency with Objects and Arrays
Detecting the Onload Event of a Window Opened with Window.Open
How to Access a JavaScript Variable Using a String That Contains the Name of the Variable
How to View Events Fired on an Element in Chrome Devtools
How to Initialize an Array's Length in JavaScript
Mongoose - What Does the Exec Function Do
How to Detect Browser's Protocol Handlers
How to Get First and Last Day of the Current Week in JavaScript
Pure JavaScript: a Function Like Jquery's Isnumeric()
JavaScript Callback When Iframe Is Finished Loading
Why am I Seeing an "Origin Is Not Allowed by Access-Control-Allow-Origin" Error Here
How to Determine the Number of Days in a Month with JavaScript
Add Property to an Array of Objects
When a 'Blur' Event Occurs, How to Find Out Which Element Focus Went *To*