jQuery .ready in a dynamically inserted iframe

355,411

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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);
    });
});

Solution 5

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.

Share:
355,411

Related videos on Youtube

EtienneT
Author by

EtienneT

I am the cofounder of LavaBlast Software. LavaBlast We build software for franchisors: easy to use point of sale software, a franchise management portal, interactive kiosks and e-commerce websites. FranchiseBlast ties these elements together by transferring data to and from a centralized management portal. Moreover, because we understand that collaboration is the key to growing a franchise offering, we’ve added software tools such as a wiki, forum, and blog into our integrated solution. We host and maintain the centralize portal, we take care of in-store software deployment &amp; upgrades, and we handle off-site backups. This leaves you, the franchisor, the hard part: growing your franchise concept.

Updated on May 23, 2020

Comments

  • EtienneT
    EtienneT almost 4 years

    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?

    • Jason Kealey
      Jason Kealey over 15 years
      And you confirm that galleria works when you load it directly instead of through an iframe, correct?
    • EtienneT
      EtienneT over 15 years
      Yes, galleria works perfectly when we use it directly in a normal page.
    • Robert MacLean
      Robert MacLean almost 11 years
  • NONA
    NONA over 14 years
    What's is the movePreview fucntion referred to in setTimeout()?
  • Sky Sanders
    Sky Sanders about 14 years
    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.
  • Shay Erlichmen
    Shay Erlichmen over 13 years
    Shouldn't you set the load event prior to calling the attr('src')?
  • David Murdoch
    David Murdoch over 13 years
    +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.
  • Már Örlygsson
    Már Örlygsson about 13 years
    @cam8001: It was a typo - has now been fixed.
  • Barum Rho
    Barum Rho almost 13 years
    No, it does not matter. Load event will not fire until the next event loop at minimum.
  • Mike Starov
    Mike Starov over 12 years
    the load event will not work for iframes that are used for download. like <iframe src="my.pdf"/>
  • Pier Luigi
    Pier Luigi over 12 years
    "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.
  • Sukhpreet Singh Alang
    Sukhpreet Singh Alang over 12 years
    Problem with load is that it fires when all images and subframes have loaded. A jQuery like ready event would be more useful.
  • Sukhpreet Singh Alang
    Sukhpreet Singh Alang over 12 years
    Problem with load is that it fires when all images and subframes have loaded. A jQuery like ready event would be more useful.
  • Woodgnome
    Woodgnome over 12 years
    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
    Skunkwaffle about 12 years
    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
    Alex almost 12 years
    @PierLuigi Could you please explain the callback variable? What should it be? I really don't understand
  • Pier Luigi
    Pier Luigi almost 12 years
    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
    Zut over 11 years
    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.
  • Julian
    Julian almost 11 years
    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')
  • w--
    w-- about 10 years
    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!
  • NeoWang
    NeoWang about 10 years
    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
    Zain Shaikh about 10 years
    Any workaround available for catching the load event for pdf files in iframe?
  • Pavel Savara
    Pavel Savara over 9 years
    Note, that it doesn't work well on mobile browsers where the cache could be smaller than the size of PDF.
  • Teepeemm
    Teepeemm over 9 years
    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").)
  • eremzeit
    eremzeit over 8 years
    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.
  • Pang
    Pang over 7 years
  • HASSAN MD TAREQ
    HASSAN MD TAREQ over 4 years
    Worked well for me