Javascript callback when IFRAME is finished loading?
Asked Answered
G

11

159

I need to execute a callback when an IFRAME has finished loading. I have no control over the content in the IFRAME, so I can't fire the callback from there.

This IFRAME is programmaticly created, and I need to pass its data as a variable in the callback, as well as destroy the iframe.

Any ideas?

EDIT:

Here is what I have now:

function xssRequest(url, callback)
{
    var iFrameObj = document.createElement('IFRAME');
    iFrameObj.src = url;            
    document.body.appendChild(iFrameObj);   

    $(iFrameObj).load(function() 
    {
        document.body.removeChild(iFrameObj);
        callback(iFrameObj.innerHTML);
    });
}

This callsback before the iFrame has loaded, so the callback has no data returned.

Gluteal answered 2/10, 2008 at 19:21 Comment(6)
I think you don't want to attach the event handler on the iframe itself but it's content window.Clasp
The problem is the cross-domain request. You cannot do it if the iframe is from another domainTeasley
Actually, there is a load event on the iframe object that fires everytime the iframe finishes loading a document, otherwise, you'd need to hook up to the window after every loadMargerymarget
See also #205587Overcome
See my answer here: https://mcmap.net/q/136814/-how-to-detect-when-an-iframe-has-already-been-loadedTetrabrach
check nczonline.net/blog/2009/09/15/iframes-onload-and-documentdomainBrutus
L
49

First up, going by the function name xssRequest it sounds like you're trying cross site request - which if that's right, you're not going to be able to read the contents of the iframe.

On the other hand, if the iframe's URL is on your domain you can access the body, but I've found that if I use a timeout to remove the iframe the callback works fine:

// possibly excessive use of jQuery - but I've got a live working example in production
$('#myUniqueID').load(function () {
  if (typeof callback == 'function') {
    callback($('body', this.contentWindow.document).html());
  }
  setTimeout(function () {$('#frameId').remove();}, 50);
});
Ladew answered 16/10, 2008 at 18:31 Comment(3)
you could replace $('body', this.contentWindow.document).html() with this.contentDocument.body.outerHTMLGrassgreen
Any idea how to do this without jQuery?Jocular
As of jQuery 3.0, load is gone. Please use .on('load', function() { ... }) instead.Hardaway
T
38

I am using jQuery and surprisingly this seems to load as I just tested and loaded a heavy page and I didn't get the alert for a few seconds until I saw the iframe load:

$('#the_iframe').load(function(){
    alert('loaded!');
});

So if you don't want to use jQuery take a look at their source code and see if this function behaves differently with iframe DOM elements, I will look at it myself later as I am interested and post here. Also I only tested in the latest chrome.

Trapezohedron answered 3/5, 2013 at 16:12 Comment(2)
I noticed you created the DOM element with pure javascript and then passed that variable to jQuery as the selector, maybe that is why you code isn't working?Trapezohedron
As of jQuery 3.0, load is gone. Please use .on('load', function() { ... }) instead.Hardaway
T
8

I have had to do this in cases where documents such as word docs and pdfs were being streamed to the iframe and found a solution that works pretty well. The key is handling the onreadystatechanged event on the iframe.

Lets say the name of your frame is "myIframe". First somewhere in your code startup (I do it inline any where after the iframe) add something like this to register the event handler:

document.getElementById('myIframe').onreadystatechange = MyIframeReadyStateChanged;

I was not able to use an onreadystatechage attribute on the iframe, I can't remember why, but the app had to work in IE 7 and Safari 3, so that may of been a factor.

Here is an example of a how to get the complete state:

function MyIframeReadyStateChanged()
{
    if(document.getElementById('myIframe').readyState == 'complete')
    {
        // Do your complete stuff here.
    }
}
Tevis answered 5/12, 2008 at 5:33 Comment(0)
G
8

The innerHTML of your iframe is blank because your iframe tag doesn't surround any content in the parent document. In order to get the content from the page referred to by the iframe's src attribute, you need to access the iframe's contentDocument property. An exception will be thrown if the src is from a different domain though. This is a security feature that prevents you from executing arbitrary JavaScript on someone else's page, which would create a cross-site scripting vulnerability. Here is some example code the illustrates what I'm talking about:

<script src="http://prototypejs.org/assets/2009/8/31/prototype.js" type="text/javascript"></script>

<h1>Parent</h1>

<script type="text/javascript">
function on_load(iframe) {
  try {
    // Displays the first 50 chars in the innerHTML of the
    // body of the page that the iframe is showing.
    // EDIT 2012-04-17: for wider support, fallback to contentWindow.document
    var doc = iframe.contentDocument || iframe.contentWindow.document;
    alert(doc.body.innerHTML.substring(0, 50));
  } catch (e) {
    // This can happen if the src of the iframe is
    // on another domain
    alert('exception: ' + e);
  }
}
</script>
<iframe id="child" src="iframe_content.html" onload="on_load(this)"></iframe>

To further the example, try using this as the content of the iframe:

<h1>Child</h1>

<a href="http://www.google.com/">Google</a>

<p>Use the preceeding link to change the src of the iframe
to see what happens when the src domain is different from
that of the parent page</p>
Germanism answered 12/2, 2010 at 10:32 Comment(1)
contentDocument property is not supported by IE 7 and maybe more IE's too, the way to deal with IE is window['iframeid'].document or what is exactly the same window.frames['iframeid'].document proof link here developer.mozilla.org/en/…Ejaculation
M
7

I wanted to hide the waiting spinner DIV when the i frame content is fully loaded on IE, i tried literally every solution mentioned in Stackoverflow.com, but with nothing worked as i wanted.

Then I had an idea, that when the iframe content is fully loaded, the $(window) load event might be fired. And that is exactly what happened. So, I wrote this small script, which worked like a magic:

 $(window).load(function() {
     //alert("Done window ready ");
     var lblWait = document.getElementById("lblWait");
     if (lblWait != null) {
         lblWait.style.visibility = "false";
         document.getElementById("divWait").style.display = "none";
     }
 });

Hope this helps.

Metabolite answered 10/12, 2014 at 8:16 Comment(2)
this is a simple solution and works fine if you want your javascript to be executed AFTER the entire page is loaded. this means that first you see the fully loaded page, without js effect, and after maybe a second or two (depending on the load) whatever changes are created by the javascript will happen then. I personally tried resizing my iframe, and it works if you don't pay attention to the screen for the first three seconds after clicking on a page but else this may (as was in my case) appear as a patchwork to the code.Extreme
thank you for your comment, this solution worked great form my case, since i wanted the iframe to be loaded after the page is loaded.Metabolite
O
5

This function will run your callback function immediately if the iFrame is already loaded or wait until the iFrame is completely loaded.

This also addresses the following issues:

  1. Chrome initializes every iFrame with an about:blank page which will have readyState == "complete". Later, it will replace `about:blank with the actual iframe src value. So, the initial value of readyState will not represent the readyState of your actual iFrame. Therefore, besides checking for readyState value, this function also addresses the about:blank issue.

  2. DOMContentLoaded event doesn't work with iFrame. So it uses the load event for running the callback function if iFrame isn't already loaded. The load event is equivalent to readyState == "complete" which has been used to check whether iFrame is already loaded. So, in any scenario, the callback function will run after iFrame is fully loaded.

  3. iFrame src can have redirects and therefore load a page different from the original src url. This function will also work in that scenario.

Pass in your callback function that you want to run when the iFrame finishes loading and the <iframe> element to this function:

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);
    }
}
Offcenter answered 24/10, 2021 at 7:49 Comment(1)
I had a need to allow an iFrame to sequence through a series of redirects for some hokey SSO solution and then load a resource after authentication. I used this answer and added a simple check for a keyword in the path: if( iframeWindow.src.includes('desktop')){...}, and when found, appended the path to the needed resource iframeWindow.src += /path/to/mine.html. I'm expecting the load event to fire at least 3-4 times and hoping I don't have to get a new contentWindow each time.Actium
B
2

I had a similar problem as you. What I did is that I use something called jQuery. What you then do in the javascript code is this:

$(function(){ //this is regular jQuery code. It waits for the dom to load fully the first time you open the page.

    $("#myIframeId").load(function(){
       callback($("#myIframeId").html());
       $("#myIframeId").remove();

    });

});

It seems as you delete you iFrame before you grab the html from it. Now, I do see a problem with that :p

Hope this helps :).

Bodine answered 10/9, 2011 at 8:23 Comment(0)
C
1

I have a similar code in my projects that works fine. Adapting my code to your function, a solution could be the following:

function xssRequest(url, callback)
{
    var iFrameObj = document.createElement('IFRAME');
    iFrameObj.id = 'myUniqueID';
    document.body.appendChild(iFrameObj);       
    iFrameObj.src = url;                        

    $(iFrameObj).load(function() 
    {
        callback(window['myUniqueID'].document.body.innerHTML);
        document.body.removeChild(iFrameObj);
    });
}

Maybe you have an empty innerHTML because (one or both causes): 1. you should use it against the body element 2. you have removed the iframe from the your page DOM

Cairn answered 15/10, 2008 at 13:52 Comment(0)
M
1

I think the load event is right. What is not right is the way you use to retreive the content from iframe content dom.

What you need is the html of the page loaded in the iframe not the html of the iframe object.

What you have to do is to access the content document with iFrameObj.contentDocument. This returns the dom of the page loaded inside the iframe, if it is on the same domain of the current page.

I would retreive the content before removing the iframe.

I've tested in firefox and opera.

Then i think you can retreive your data with $(childDom).html() or $(childDom).find('some selector') ...

Myiasis answered 25/8, 2009 at 13:33 Comment(0)
R
0

I've had exactly the same problem in the past and the only way I found to fix it was to add the callback into the iframe page. Of course that only works when you have control over the iframe content.

Reek answered 2/10, 2008 at 19:34 Comment(1)
I didn't vote you down, but I imagine you got voted down because you suggested exactly what he said he can't do -- he did say specifically, "I have no control over the content in the IFRAME, so I can't fire the callback from there."Millard
C
-1

Using onload attrbute will solve your problem.

Here is an example.

function a() {
alert("Your iframe has been loaded");
}
<iframe src="https://stackoverflow.com" onload="a()"></iframe>

Is this what you want?

Click here for more information.

Cramer answered 26/3, 2018 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.