120

I have an iframe with id = "myIframe" and here my code to load it's content :

$('#myIframe').attr("src", "my_url");

The problem is sometimes it take too long for loading and sometimes it loaded very quickly. So I must to use "setTimeout" function :

setTimeout(function(){
   if (//something shows iframe is loaded or has content)
   {
       //my code
   }
   else
   {
       $('#myIframe').attr("src",""); //stop loading content
   }
},5000);

All I want to know is how to find out if an iFrame is loaded or it has content. Using iframe.contents().find() will not work. I can't use iframe.load(function(){}).

Jon Surrell
  • 9,444
  • 8
  • 48
  • 54
newbie29
  • 1,227
  • 2
  • 9
  • 6
  • So you don't want the iframe to load anything if it takes it more than 5 seconds to load? – skimberk1 Feb 12 '12 at 14:50
  • Thanks for your help :) . Yes, I don't want to the iframe load anything if it takes more than 5 seconds to load. Using ".ready()" as you show bellow not work. – newbie29 Feb 12 '12 at 15:05
  • See my answer here http://stackoverflow.com/questions/17158932/how-to-detect-when-an-iframe-has-already-been-loaded/36155560#36155560 – dude Apr 01 '16 at 10:30
  • Check this answer - https://forums.tumult.com/t/detect-iframe-loaded/7588 – Varshaan Sep 30 '19 at 10:28

13 Answers13

115

Try this.

<script>
function checkIframeLoaded() {
    // Get a handle to the iframe element
    var iframe = document.getElementById('i_frame');
    var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

    // Check if loading is complete
    if (  iframeDoc.readyState  == 'complete' ) {
        //iframe.contentWindow.alert("Hello");
        iframe.contentWindow.onload = function(){
            alert("I am loaded");
        };
        // The loading is complete, call the function we want executed once the iframe is loaded
        afterLoading();
        return;
    } 

    // If we are here, it is not loaded. Set things up so we check   the status again in 100 milliseconds
    window.setTimeout(checkIframeLoaded, 100);
}

function afterLoading(){
    alert("I am here");
}
</script>

<body onload="checkIframeLoaded();"> 
Biranchi
  • 16,120
  • 23
  • 124
  • 161
  • 24
    Demerits for passing a string to setTimeout(). It is cleaner to pass a function reference: `setTimeout(checkIframeLoaded, 100);` – Jesse Hallett Feb 07 '13 at 00:45
  • 4
    this one lead to "Unsafe JavaScript attempt to access frame with URL url1 from frame with url2 Domains, protocols and ports must match." – mohamed-ibrahim Dec 05 '13 at 11:28
  • This was the best solution for what I was trying to accomplish. I am using AngularJS inside the iFrame, and using ng-include, which delays the loading of the included files. I was trying to print the contents of the Iframe upon the onload event, but it was firing too soon. Checking for `readyState === "complete"` did the trick, since it doesn't change until everything is loaded. – Jeffrey A. Gochin Sep 24 '16 at 17:42
  • 3
    I have to say this was the only solution that worked for me as well. You just can't trust the load event on an iframe, as it fires immediately in most cases on IE11, Chrome, and Edge with the `iframe.contentDocument.location.href` showing as `about:blank`. This seems to be true even if the `src` for the `iframe` is specified directly in the HTML. – rmalayter Oct 11 '16 at 14:11
  • 49
    This won't work if the iframe comes from another domain: `VM29320:1 Uncaught DOMException: Blocked a frame with origin "http://localhost:9002" from accessing a cross-origin frame.` – Augustin Riedinger Nov 23 '16 at 12:54
  • One suggestion would be to move the var iframe = document.getElementById('i_frame'); (And now that it's 2017, change it to const iframe) outside the function so it doesn't perform a DOM search each time it's called. – Jose A Nov 29 '17 at 17:46
51

kindly use:

$('#myIframe').on('load', function(){
    //your code (will be called once iframe is done loading)
});

Updated my answer as the standards changed.

pratikabu
  • 1,672
  • 14
  • 17
  • 15
    This approach doesn't work at all as the event fires immediately because `iframe`s generally start with `src="about:blank"`, even if the `src` is specified directly in the HTML or before an `iframe` node is added to the DOM. I tested this by polling for the `iframe`'s `contentDocument.location.href` in a tight loop on IE11, Chrome, and Edge. It also doesn't work if the content being framed does redirects via meta tags or javascript. – rmalayter Oct 11 '16 at 14:07
27

I had the same issue and added to this, i needed to check if iframe is loaded irrespective of cross-domain policy. I was developing a chrome extension which injects certain script on a webpage and displays some content from the parent page in an iframe. I tried following approach and this worked perfect for me.
P.S.: In my case, i do have control over content in iframe but not on the parent site. (Iframe is hosted on my own server)

First:
Create an iframe with a data- attribute in it like (this part was in injected script in my case)
<iframe id="myiframe" src="http://anyurl.com" data-isloaded="0"></iframe>

Now in the iframe code, use :

var sourceURL = document.referrer;
window.parent.postMessage('1',sourceURL);



Now back to the injected script as per my case:

setTimeout(function(){
  var myIframe = document.getElementById('myiframe');
  var isLoaded = myIframe.prop('data-isloaded');
  if(isLoaded != '1')
  {
    console.log('iframe failed to load');
  } else {
    console.log('iframe loaded');
  }
},3000);


and,

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
    if(event.origin !== 'https://someWebsite.com') //check origin of message for security reasons
    {
        console.log('URL issues');
        return;
    }
    else {
        var myMsg = event.data;
        if(myMsg == '1'){
            //8-12-18 changed from 'data-isload' to 'data-isloaded
            $("#myiframe").prop('data-isloaded', '1');
        }
    }           
}



It may not exactly answer the question but it indeed is a possible case of this question which i solved by this method.

user2677034
  • 624
  • 10
  • 20
Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41
16

Easiest option:

<script type="text/javascript">
  function frameload(){
   alert("iframe loaded")
  }
</script>

<iframe onload="frameload()" src=...>
Orane
  • 2,223
  • 1
  • 20
  • 33
9

You can use the iframe's load event to respond when the iframe loads.

document.querySelector('iframe').onload = function(){
    console.log('iframe loaded');
};

This won't tell you whether the correct content loaded: To check that, you can inspect the contentDocument.

document.querySelector('iframe').onload = function(){
    var iframeBody = this.contentDocument.body;
    console.log('iframe loaded, body is: ', body);
};

Checking the contentDocument won't work if the iframe src points to a different domain from where your code is running.

Max Heiber
  • 14,346
  • 12
  • 59
  • 97
  • 2
    Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem. – SuperBiasedMan Oct 23 '15 at 09:59
9

in my case it was a cross-origin frame and wasn't loading sometimes. the solution that worked for me is: if it's loaded successfully then if you try this code:

var iframe = document.getElementsByTagName('iframe')[0];
console.log(iframe.contentDocument);

it won't allow you to access contentDocument and throw a cross-origin error however if frame is not loaded successfully then contentDocument will return a #document object

Raza Ahmed
  • 2,661
  • 2
  • 35
  • 46
5

I'm not sure if you can detect whether it's loaded or not, but you can fire an event once it's done loading:

$(function(){
    $('#myIframe').ready(function(){
        //your code (will be called once iframe is done loading)
    });
});

EDIT: As pointed out by Jesse Hallett, this will always fire when the iframe has loaded, even if it already has. So essentially, if the iframe has already loaded, the callback will execute immediately.

interesting-name-here
  • 1,851
  • 1
  • 20
  • 33
skimberk1
  • 2,064
  • 3
  • 21
  • 27
  • 3
    The nice thing about this method is that the jQuery 'ready' event will fire even if you bind it after the iframe is completely loaded. So it is a good way to check if the iframe already has content or to notify you when it gets content. – Jesse Hallett Feb 07 '13 at 00:42
  • 1
    This appears to only fire once for the iframe. However if your iframe has links in it that may result in the content in the iframe being reloaded then this event will not fire again. If this is your case then use the answer by @pratikabu - `$('#myIframe').load(function(){ })` – ragamufin Oct 11 '13 at 01:28
  • 1
    @skimberk1 Hi, according this [answer](http://stackoverflow.com/questions/5870104/is-jquery-ready-valid-when-used-on-iframe-contentdocument) your solution wont work, because jquery holds a variable to remember if the DOM has been loaded and onley checks the current DOM of the current frame not the DOM of the iframe. – Marco Medrano Jan 21 '14 at 00:31
  • yes, you can fire events, show a video, play music once it's ready, very informative.... – Giridhar Karnik Nov 18 '15 at 13:26
  • According to this ticket: https://dev.jquery.com/ticket/5511 using `ready` on an iframe will always immediately execute the function, as it is not supported to other documents then the frame in which jQuery is running. – dude Mar 21 '16 at 07:40
3

When an iFrame loads, it initially contains the #document, so checking the load state might best work by checking what's there now..

if ($('iframe').contents().find('body').children().length > 0) {
    // is loaded
} else {
    // is not loaded
}
obimod
  • 797
  • 10
  • 26
1

If you need to know when the iframe is ready to manipulate, use an interval. In this case I "ping" the content every 250 ms and if there's any content inside target iframe, stop the "ping" and do something.

var checkIframeLoadedInterval = setInterval( checkIframeLoaded, 250 );

function checkIframeLoaded() {
    var iframe_content = $('iframe').contents();

    if (iframe_content.length > 0) {
        clearInterval(checkIframeLoadedInterval);

        //Apply styles to the button
        setTimeout(function () {
            //Do something inside the iframe 
            iframe_content.find("body .whatever").css("background-color", "red");
        }, 100); //100 ms of grace time
    }
}
Jimmy Adaro
  • 1,325
  • 15
  • 26
1

I got a trick working as follows: [have not tested cross-browser!]

Define iframe's onload event handler defined as

$('#myIframe').on('load', function() {
    setTimeout(function() {
        try {
            console.log($('#myIframe')[0].contentWindow.document);
        } catch (e) {
            console.log(e);
            if (e.message.indexOf('Blocked a frame with origin') > -1 || e.message.indexOf('from accessing a cross-origin frame.') > -1) {
                alert('Same origin Iframe error found!!!');
                //Do fallback handling if you want here
            }
        }
    }, 1000);

});

Disclaimer: It works only for SAME ORIGIN IFRAME documents.

Pratap Singh
  • 401
  • 1
  • 4
  • 14
0

What I Had To Do To Access iFrame DOM Elements AFTER MANY FAILURES

I was working on a simple proof of concept for making sure I could manipulate DOM elements inside an iframe using JavaScript loaded for the main page.

I am not concerned with accessing cross domain pages in the iframe in my work.

First, my simple main page ... NOTE I am using class="test-outer" as the class my JavaScript will query.

<!DOCTYPE html>
<html>
  <head>
    <title>My Outer Page</title>
  </head>
  <body>
    <h1>This is the <span class="test-outer">outer</span> page</h1>
    <hr />

    <iframe
      id="the_iframe"
      src="./Test_1_inner.html"
      height="500px"
      width="1000px"
      title="My Inner Page"
      style="border: none"
    >
    </iframe>
  </body>
  <script src="script.js" defer></script>
</html>

Next is my inner page that will go in the iframe ... NOTE I am using class="test-inner" as the class my JavaScript will query.

<!DOCTYPE html>
<html>
  <head>
    <title>My Inner Page</title>
  </head>
  <body>
    <h1>
      This is the
      <span class="test-inner">INNER</span>
      page
    </h1>
  </body>
</html>

Now for the important part - the JavaScript. NOTE my use of a setTimeout, which was the MAIN thing that got my prototype here to a working state.

"use strict";

const field = document.querySelector(".test-outer");

field.addEventListener("click", function (e) {
  console.log("Field");
});

let seconds = 0.0;
let iframe_timer = setTimeout(function () {
  let the_iframe = document
    .querySelector("iframe")
    .contentDocument.querySelector(".test-inner");
  if (the_iframe === null) {
    console.log("iframe failed to load");
    seconds += 0.25;
    if (seconds > 3.0) {
      my_timer_stopper();
    }
  } else {
    console.log("iframe loaded");
    my_timer_stopper();
    the_iframe = document
      .querySelector("iframe")
      .contentDocument.querySelector(".test-inner");
    the_iframe.addEventListener("click", function (e) {
      console.log("Token");
    });
  }
}, 250);

function my_timer_stopper() {
  clearTimeout(iframe_timer);
}

Finally, the console log minus things you wouldn't want to see from that.

iframe loaded
script.js:27 Token  // from clicking on INNER in the iframe
script.js:6 Field  // from clicking on outer in the main page
script.js:27 Token  // from clicking on INNER in the iframe

I hope this saves some readers a lot of time that I had to waste to figure this out :-)

Thom Ives
  • 3,642
  • 3
  • 30
  • 29
-2

If you're hosting the page and the iframe on the same domain you can listen for the iframe's Window.DOMContentLoaded event. You have to wait for the original page to fire DOMContentLoaded first, then attach a DOMContentLoaded event listener on the iframe's Window.

Given you have an iframe as follows,

<iframe id="iframe-id" name="iframe-name" src="..."></iframe>

the next snippet will allow you to hook into the iframe's DOMContentLoaded event:

document.addEventListener('DOMContentLoaded', function () {
    var iframeWindow = frames['iframe-name'];
    // var iframeWindow = document.querySelector('#iframe-id').contentWindow
    // var iframeWindow = document.getElementById('iframe-id').contentWindow

    iframeWindow.addEventListener('DOMContentLoaded', function () {
        console.log('iframe DOM is loaded!');
    });
});
Daniel
  • 8,655
  • 5
  • 60
  • 87
-2

This will work only if it's not a cross-origin iframe.

You can do it like this:

const $iframe = document.querySelector(`iframe`);

$iframe.addEventListener("load", function () {
    console.log("loaded");
});
Diego Fortes
  • 8,830
  • 3
  • 32
  • 42