Having one iframe watch for other iframes to load.

This is an issue that I thought was going to be straight forward using the jQuery ready function, but i could not get it to work.

The problem is simple: I want to watch a couple of iframes load and then do something once they are all done.

The “when they are all done” part is simple with jQuery Deferred objects.  A really good, quick tutorial on those is here.  Basically, I can pass however many Deferred objects I want to the jQuery.when( ... )  command.  When all of them “resolve” (i.e., when the are done doing whatever they need to do), the when command will continue with some “success” function or functions.  There are also options for when one or more Deferred objects fail, etc.  Read the tutorial… it’s good.  I created a javascript object that encapsulates the Deferred object and my method of resolving the object.  So I just needed to create one per frame I wanted to watch and feed them into the jQuery.when() function.

So for the “watch a couple of iframes” part I want to resolve my Deferred objects, instantiated in iframe A, based on iframes B, C, and D finishing loading.  I assumed this was a simple something like:

jQuery(top.B.document).ready( function() { ... resolve my deferred object ... } );

however, this did not work!  It did not matter that I put top.B.document (this is a path from top down to the frame, and then referencing the frames document), the call seemed to work on my local iframe’s document (iframe A in this example). I couldn’t find any information on this online, so if anyone knows more about it i’d love to hear it.

The solution:

I could have gone into each iframe and placed a document ready function that set a flag, and then read that flag from the iframe that is watching, but I wanted to keep this functionality clean and encapsulated in my object that was also handling the Deferred object.  I wanted to write it once — not X number of times.  Also, I know someone will mention “why are you using iframes at all?  use Ajax and blah blah blah”  I know, but you have to work with what you’re given sometimes… so i’m using iframes.

The solution I came up with is this (most of the Deferred object stuff is left out):

// Constructor
function MyIframeWatcherObject( contextPath ) {
...
// Get the path to the iframe we're watching.
this.contextPathToUse = contextPath || null;
...
{

MyIframeWatcherObject.prototype.resolveOnDOMloaded = function( ) {
	// Re-eval contextDocument each time because it is lost when page reloads.
	var contextDocument = document; //default
	try {
            if ( this.contextPathToUse )
		contextDocument = eval(this.contextPathToUse + '.document');
	} catch ( err ) {
		// Do nothing, use default as contextDocument as fallback for now..
                // Add error handling...
	}
	if ( contextDocument.readyState === 'complete' ) {
            deferredObject.resolve();
        } else {
            var self = this;
            setTimeout( MyIframeWatcherObject.prototype.resolveOnDOMloaded.call( self ), 100 );
        }

}

First, I resolve the contextPath that was passed in to get its document.  I do this each time because if I resolve it in the constructor and store it, I lose it if the page begins to reload again (it becomes undefined).  Then I check the readyState on the document and determine if I should resolve the Deferred object now, or set up another timer.  The setTimeout function is best here so that I am only setting up one more trigger each time and don’t have to worry about tracking it like I would with setInterval.  John Resig has a great tutorial on timers here — definitely a good read if you are not aware of how they function.

I’m not completely thrilled with this solution, but it works.  Please offer another way of doing it if you know one!

UPDATE: just an additional note. The document.readyState property looked like it was a cross-browser solution based on my quick google search — otherwise I would not have considered it. If you know differently, or know of bugs with this method, please let me know. Also, the opposite state of document.readyState == 'complete' is document.readyState == 'loading'.

Advertisements

About mpickell

I'm a java developer View all posts by mpickell

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: