By EtienneT


2008-10-15 15:09:22 8 Comments

We are using jQuery thickbox to dynamically display an iframe when someone clicks on a picture. In this iframe, we are using galleria a javascript library to display multiple pictures.

The problem seems to be that $(document).ready in the iframe seems to be fired too soon and the iframe content isn't even loaded yet, so galleria code is not applied properly on the DOM elements. $(document).ready seems to use the iframe parent ready state to decide if the iframe is ready.

If we extract the function called by document ready in a separate function and call it after a timeout of 100 ms. It works, but we can't take the chance in production with a slow computer.

$(document).ready(function() { setTimeout(ApplyGalleria, 100); });

My question: which jQuery event should we bind to to be able to execute our code when the dynamic iframe is ready and not just it's a parent?

10 comments

@Crashalot 2020-05-23 06:59:07

This function from this answer is the best way to handle this as $.ready explicitly fails for iframes. Here's the decision not to support this.

The load event also doesn't fire if the iframe has already loaded. Very frustrating that this remains a problem in 2020!

function onIframeReady($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) {
    errorFn();
}

}

@udondan 2017-08-01 14:19:59

Basically what others have already posted but IMHO a bit cleaner:

$('<iframe/>', {
    src: 'https://example.com/',
    load: function() {
        alert("loaded")
    }
}).appendTo('body');

@Jon Freynik 2016-02-10 14:17:45

This was the exact issue I ran into with our client. I created a little jquery plugin that seems to work for iframe readiness. It uses polling to check the iframe document readyState combined with the inner document url combined with the iframe source to make sure the iframe is in fact "ready".

The issue with "onload" is that you need access to the actual iframe being added to the DOM, if you don't then you need to try to catch the iframe loading which if it is cached then you may not. What I needed was a script that could be called anytime, and determine whether or not the iframe was "ready" or not.

Here's the question:

Holy grail for determining whether or not local iframe has loaded

and here's the jsfiddle I eventually came up with.

https://jsfiddle.net/q0smjkh5/10/

In the jsfiddle above, I am waiting for onload to append an iframe to the dom, then checking iframe's inner document's ready state - which should be cross domain because it's pointed to wikipedia - but Chrome seems to report "complete". The plug-in's iready method then gets called when the iframe is in fact ready. The callback tries to check the inner document's ready state again - this time reporting a cross domain request (which is correct) - anyway it seems to work for what I need and hope it helps others.

<script>
  (function($, document, undefined) {
    $.fn["iready"] = function(callback) {
      var ifr = this.filter("iframe"),
          arg = arguments,
          src = this,
          clc = null, // collection
          lng = 50,   // length of time to wait between intervals
          ivl = -1,   // interval id
          chk = function(ifr) {
            try {
              var cnt = ifr.contents(),
                  doc = cnt[0],
                  src = ifr.attr("src"),
                  url = doc.URL;
              switch (doc.readyState) {
                case "complete":
                  if (!src || src === "about:blank") {
                    // we don't care about empty iframes
                    ifr.data("ready", "true");
                  } else if (!url || url === "about:blank") {
                    // empty document still needs loaded
                    ifr.data("ready", undefined);
                  } else {
                    // not an empty iframe and not an empty src
                    // should be loaded
                    ifr.data("ready", true);
                  }

                  break;
                case "interactive":
                  ifr.data("ready", "true");
                  break;
                case "loading":
                default:
                  // still loading
                  break;   
              }
            } catch (ignore) {
              // as far as we're concerned the iframe is ready
              // since we won't be able to access it cross domain
              ifr.data("ready", "true");
            }

            return ifr.data("ready") === "true";
          };

      if (ifr.length) {
        ifr.each(function() {
          if (!$(this).data("ready")) {
            // add to collection
            clc = (clc) ? clc.add($(this)) : $(this);
          }
        });
        if (clc) {
          ivl = setInterval(function() {
            var rd = true;
            clc.each(function() {
              if (!$(this).data("ready")) {
                if (!chk($(this))) {
                  rd = false;
                }
              }
            });

            if (rd) {
              clearInterval(ivl);
              clc = null;
              callback.apply(src, arg);
            }
          }, lng);
        } else {
          clc = null;
          callback.apply(src, arg);
        }
      } else {
        clc = null;
        callback.apply(this, arguments);
      }
      return this;
    };
  }(jQuery, document));
</script>

@Hassan Tareq 2019-09-09 10:33:56

Worked well for me

@Pier Luigi 2008-10-15 15:41:30

I answered a similar question (see Javascript callback when IFRAME is finished loading?). You can obtain control over the iframe load event with the following code:

function callIframe(url, callback) {
    $(document.body).append('<IFRAME id="myId" ...>');
    $('iframe#myId').attr('src', url);

    $('iframe#myId').load(function() {
        callback(this);
    });
}

In dealing with iframes I found good enough to use load event instead of document ready event.

@Shay Erlichmen 2010-12-25 19:30:46

Shouldn't you set the load event prior to calling the attr('src')?

@Barum Rho 2011-06-24 16:14:32

No, it does not matter. Load event will not fire until the next event loop at minimum.

@Mike Starov 2011-11-03 18:44:01

the load event will not work for iframes that are used for download. like <iframe src="my.pdf"/>

@Pier Luigi 2011-12-12 06:26:11

"Dynamically inserted" because the iframe tag is inserted in the DOM by javascript on the client. So the iframe can exixts or not on the page, based on condition or event that happens on the client.

@Tom 2011-12-14 12:55:31

Problem with load is that it fires when all images and subframes have loaded. A jQuery like ready event would be more useful.

@Woodgnome 2012-01-17 15:06:18

The load event will not fire for iFrames that have already loaded by the time the load handler is bound. As such it is not comparable to jQuery(document).ready() for iFrames. The only solution I can think of is copying how jQuery does it for the frame that jQuery has been loaded in.

@Skunkwaffle 2012-04-30 15:23:38

This wasn't working for some reason, so I tried replacing the first two lines with: myFrame = $('<iframe src="' + src + '" id="myFrame"></iframe>').appendTo('body'); and everything worked fine.

@Alex 2012-05-10 14:09:16

@PierLuigi Could you please explain the callback variable? What should it be? I really don't understand

@Pier Luigi 2012-05-11 09:26:10

callback is any javascript function you want to be called once iframe has loaded. It can be a function local to the caller window or even a function defined in iframe window. You can access iframe window with window['myId'] on the caller.

@Zut 2012-12-18 09:02:23

Note that there are two different .load()-functions. api.jquery.com/load for loading content into an object, and api.jquery.com/load-event to bind the onLoad event.

@NeoWang 2014-03-24 12:18:57

What if I want to call a callback when the DOM of iframe is loaded? $.load is triggered only when everything(a lot of imgs) are loaded. What I need is the DOM ready event of the iframe. Can it be done without calling js across iframes?

@Zain Shaikh 2014-04-14 07:33:12

Any workaround available for catching the load event for pdf files in iframe?

@Ricardo Freitas 2013-04-30 01:49:51

Following DrJokepu's and David Murdoch idea I implemented a more complete version. It requires jQuery on both the parent and iframe and the iframe to be in your control.

iframe code:

var iframe = window.frameElement;

if (iframe){
    iframe.contentDocument = document;//normalization: some browsers don't set the contentDocument, only the contentWindow

    var parent = window.parent;
    $(parent.document).ready(function(){//wait for parent to make sure it has jQuery ready
        var parent$ = parent.jQuery;

        parent$(iframe).trigger("iframeloading");

        $(function(){
            parent$(iframe).trigger("iframeready");
        });

        $(window).load(function(){//kind of unnecessary, but here for completion
            parent$(iframe).trigger("iframeloaded");
        });

        $(window).unload(function(e){//not possible to prevent default
            parent$(iframe).trigger("iframeunloaded");
        });

        $(window).on("beforeunload",function(){
            parent$(iframe).trigger("iframebeforeunload");
        });
    });
}

parent test code:

$(function(){
    $("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
        console.log(e.type);
    });
});

@w-- 2014-03-22 22:27:06

for whatever reason using $(iframe).ready(function...) in the parent would not work for me. It seemed like the callback function was getting executed before the iframe dom was ready. Using this method worked great!

@Pavel Savara 2012-08-16 14:32:45

I'm loading the PDF with jQuery ajax into browser cache. Then I create embedded element with data already in browser cache. I guess it will work with iframe too.


var url = "http://example.com/my.pdf";
// show spinner
$.mobile.showPageLoadingMsg('b', note, false);
$.ajax({
    url: url,
    cache: true,
    mimeType: 'application/pdf',
    success: function () {
        // display cached data
        $(scroller).append('<embed type="application/pdf" src="' + url + '" />');
        // hide spinner
        $.mobile.hidePageLoadingMsg();
    }
});

You have to set your http headers correctly as well.


HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";

@Pavel Savara 2014-09-15 12:52:45

Note, that it doesn't work well on mobile browsers where the cache could be smaller than the size of PDF.

@Danny G 2009-10-12 17:04:03

Try this,

<iframe id="testframe" src="about:blank" onload="if (testframe.location.href != 'about:blank') testframe_loaded()"></iframe>

All you need to do then is create the JavaScript function testframe_loaded().

@Tom 2011-12-14 12:54:57

Problem with load is that it fires when all images and subframes have loaded. A jQuery like ready event would be more useful.

@David Valentiate 2009-04-09 17:37:46

Using jQuery 1.3.2 the following worked for me:

$('iframe').ready(function() {
  $('body', $('iframe').contents()).html('Hello World!');
});

REVISION:! Actually the above code sometimes looks like it works in Firefox, never looks like it works in Opera.

Instead I implemented a polling solution for my purposes. Simplified down it looks like this:

$(function() {
  function manipIframe() {
    el = $('body', $('iframe').contents());
    if (el.length != 1) {
      setTimeout(manipIframe, 100);
      return;
    }
    el.html('Hello World!');
  }
  manipIframe();
});

This doesn't require code in the called iframe pages. All code resides and executes from the parent frame/window.

@cam8001 2009-11-26 03:59:46

What's is the movePreview fucntion referred to in setTimeout()?

@Már Örlygsson 2011-03-28 12:10:47

@cam8001: It was a typo - has now been fixed.

@Julian 2013-06-18 17:02:10

I ended up using a polling solution too. Other solutions seemed to work only with partial success. However, for me, I needed to check for existence of a javascript function, rather than just for the contents of the iframe, before I had success. eg. (typeof iframe.contentWindow.myfunc == 'function')

@eremzeit 2015-12-03 11:32:55

This solution is recursive which takes up memory for each call. If the iframe never loads, then it'll call deeper and deeper forever until memory runs out. I'd recommend setInterval for polling instead.

@EtienneT 2008-10-15 17:00:18

Found the solution to the problem.

When you click on a thickbox link that open a iframe, it insert an iframe with an id of TB_iframeContent.

Instead of relying on the $(document).ready event in the iframe code, I just have to bind to the load event of the iframe in the parent document:

$('#TB_iframeContent', top.document).load(ApplyGalleria);

This code is in the iframe but binds to an event of a control in the parent document. It works in FireFox and IE.

@Sky Sanders 2010-04-04 08:39:04

Found the solution? Looks like Pier had already posted it. Whether you found it on your own or not, etiquette would be to accept his answer, thus rewarding the time he spent answering you.

@Teepeemm 2014-10-04 19:05:09

The difference between Pier's solution and this (and what I was missing in my code) is the context top.document that allows me to have code inside the iframe be able to tell when the iframe has loaded. (Although load has been deprecated since this answer, and should be replaced with on("load").)

@Tamas Czinege 2008-10-15 15:54:38

In IFrames I usually solve this problem by putting a small script to the very end of the block:

<body>
The content of your IFrame
<script type="text/javascript">
//<![CDATA[
   fireOnReadyEvent();
   parent.IFrameLoaded();
//]]>
</script>
</body>

This work most of the time for me. Sometimes the simplest and most naive solution is the most appropriate.

@David Murdoch 2011-01-21 13:50:29

+1 This solution works great for me! One great addition is that you can reach up to the parent to grab a copy of jQuery and use parent.$(document).ready(function(){ parent.IFrameLoaded( ); }); to initialize the iframe.

Related Questions

Sponsored Content

66 Answered Questions

[SOLVED] How do I check whether a checkbox is checked in jQuery?

18 Answered Questions

[SOLVED] How to insert an item into an array at a specific index (JavaScript)?

30 Answered Questions

[SOLVED] jQuery scroll to element

42 Answered Questions

[SOLVED] Is there an &amp;quot;exists&amp;quot; function for jQuery?

  • 2008-08-27 19:49:41
  • Jake McGraw
  • 752750 View
  • 2799 Score
  • 42 Answer
  • Tags:   javascript jquery

38 Answered Questions

[SOLVED] Add table row in jQuery

57 Answered Questions

[SOLVED] How do I check if an element is hidden in jQuery?

18 Answered Questions

[SOLVED] Disable/enable an input with jQuery?

41 Answered Questions

[SOLVED] Setting "checked" for a checkbox with jQuery

36 Answered Questions

[SOLVED] $(document).ready equivalent without jQuery

  • 2009-04-28 21:51:11
  • FlySwat
  • 1054459 View
  • 2047 Score
  • 36 Answer
  • Tags:   javascript jquery

Sponsored Content