jQuery .ready in a dynamically inserted iframe

jQuery .ready in a dynamically inserted iframe

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?

Solutions/Answers:

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.

Related:  Google.com and clients1.google.com/generate_204

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.

Related:  Access to ES6 array element index inside for-of loop

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.

Solution 6:

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().

Related:  Sort JavaScript object by key