Main BLOGGER
Google
WWW THIS BLOG
Monday, July 30, 2007
 
a way to communicate with orphan pop-up window

http://www.1pixelout.net/2005/04/19/cross-window-javascript-communication/

 

 

Cross-window Javascript communication

This solution is now officially obsolete. A much nicer and simpler solution is available here.

This is a solution to a problem that I’ve come accross several times before but never really had a go at solving. This time, I just had to get it right or we would have had to use a nasty system of hidden frames. Yuk!

The problem

Right, so you have this page you want to open in a popup and you want to be able to call JS functions defined in the popup from the main window. Nothing revolutionary here. When you create a popup with the window.open method, you can specify a name for the new window object and use it to reference the popup from the main window.

// Create a new popup window
var popupWin = window.open( url, "popupWin" );
 
// To call functions defined in the popup:
popupWin.doSomething();

Now, when the user navigates to another page in the main window, the reference to the popup window object is lost and you cannot communicate with the popup anymore. This can be problematic if you need the popup to stay open. You can re-create the popup with the open method but this always reloads the page and when your popup is a music player, you get a very rubbish user experience indeed.

So, using the music player as an example, what we would need is a way to maintain a persistent link between the player and its opener so that the main window can call methods to control the player.

The solution

What we need to do is get the popup to broadcast its existence to the main window. Fortunately, the popup is not as forgetful as our main window and still has a reference to its creator: self.opener. We can get the popup to remind the opener at regular intervals with this code:

function notifyOpener() {
        if(self.opener || !self.opener.popupWin) self.opener.popupWin = self;
}
 
setInterval( notifyOpener, 200 );

This ensures that any page opened in the main window gets notified. The interval time should be short enough for the window to be notified before the user gets a chance to click any links. Now, the main window can call functions of the popup:

// Initialise popup pointer
var popupWin = null;
 
function doSomething() {
        if( popupWin && !popupWin.closed ) {
               popupWin.doSomething();
        }
}

Initialising the popupWin variable ensures that the script doesn’t break if the popup is closed. Before making the call to the popup, we can also check the closed property of the popup window. When true, this property indicates that the popup window was closed by the user (hardly rocket science, heh?).

So, if we combine the code above with the open routine we get this for the main window:

// Initialise popup pointer
var popupWin = null;
 
function openPopup() {
        var url = "the_url_to_your_popup.htm"
        if( !popupWin || popupWin.closed ) {
               popupWin = window.open( url, "popupWin" );
        } else popupWin.focus();
}
 
function doSomething() {
        if( popupWin && !popupWin.closed ) {
               // The popup is open so call it
               popupWin.doSomething();
        } else {
               // The popup is closed so open it
               openPopup();
        }
}

And in the popup:

function notifyOpener() {
        if(self.opener || !self.opener.popupWin) self.opener.popupWin = self;
}
 
setInterval( notifyOpener, 200 );
 
self.focus();
 
function doSomething() {
        alert("I'm doing something");
}

That’s it. Well, actually no, there is one more thing: the popup script will start throwing errors if the user closes the main window or navigates to a page on a different domain. To prevent any unnecessary errors, we add the following lines to the popup script:

function handleError() { return true; }
window.onerror = handleError;

Download an example

17 comments

  1. Tom Gilkison's gravatar#1: Tom Gilkison Says:
    October 8th, 2005 at 7:48 am

I just finished an article to go over a similar problem I faced in the creation of a Web application - http://tom.gilki.org/programming/javascript/Login/

Seems like we run into these issues quite a bit, but nobody takes the time to document the solutions! Thanks for the info.

  1. Jiten's gravatar#2: Jiten Says:
    November 11th, 2005 at 4:20 am

Nice idea. Thanks a bunch for posting this!

It works like a charm in IE but seems to be spluttering in Firefox. Still working on it but if you have feedback that would help, would appreciate it.

Thanks.

  1. Jiten's gravatar#3: Jiten Says:
    November 11th, 2005 at 5:13 am

Replace the following line in the sample code here:
setInterval( notifyOpener, 200 );
with
setInterval(”notifyOpener()”, 200);

This should do the trick in Firefox. :-)

  1. Martin's gravatar#4: Martin Says:
    November 11th, 2005 at 10:00 am

Jiten: this shouldn’t be a problem in Firefox. I have just tested the code again and it worked just fine in Firefox. setInterval( notifyOpener, 200 ) is perfectly valid syntax.

Which version are you using and on which platform?

  1. Jiten's gravatar#5: Jiten Says:
    November 11th, 2005 at 7:11 pm

Martin, you are right. I think the problem was caused by a bunch of JavaScript alerts that i had put in there, they seem to have caused some issue (could not figure out what exactly). Once I removed the alerts both the versions (the one I posted and your original ones worked fine) in Firefox.

The strange thing is the alerts did not cause a problem in IE though!

BTW I was testing this on a Windows XP Professional. Version is Firefox 1.5.

Thanks once again for this neat piece of code! :)

  1. mitmaur's gravatar#6: mitmaur Says:
    December 1st, 2005 at 6:41 am

I can’t wait to try this out. From the previous posts, it seems that it works fine. Ive racked my brain for days trying to think of a solution to this nagging problem. thanks for finding the solution!

  1. Will's gravatar#7: Will Says:
    February 19th, 2006 at 2:23 am

Nice one! :)

Thanks a lot,

Will

  1. Ellie's gravatar#8: Ellie Says:
    March 19th, 2006 at 11:37 pm

Try this change to doSomething() so it waits for the function to load in the new window before trying to call it. If the function hasn’t loaded it will try again in 500 clicks. Also added an extra call at the end so only this function needs to be called if the new window hasn’t been opened yet.

function doSomething() {
if( popupWin && !popupWin.closed ) {
// The popup is open so call it IF the function is loaded
if (popupWin.doSomething) {
popupWin.doSomething();
}
else {
setTimeout(’doSomething()’,500);
}

} else {
// The popup is closed so open it
openPopup();doSomething();
}
}

  1. Ben Nadel's gravatar#9: Ben Nadel Says:
    June 19th, 2006 at 6:03 pm

This is awesome… I have been dealing with this situation for a while trying to get a pop-up rich text editor to be “cached” in the back ground and focused when available, or openned when it doesn’t exist. I think this is awesome and will do the trick.

Thanks!

  1. mazben's gravatar#10: mazben Says:
    September 22nd, 2006 at 3:44 pm

thanks for this code. It’s a good synthesis, and helps me a lot.

  1. Deva's gravatar#11: Deva Says:
    October 24th, 2006 at 5:57 pm

I have a situation where I am invoking function_A using setInterval function after every 500ms. If while processing any error happens in the function_A, I am using onError to handle it. It normally returns true as you mentioned. But the setInterval function stops invoking function_A from that point onwards. Is there anything I can do to make it continue its work?

  1. Martin's gravatar#12: Martin Says:
    October 25th, 2006 at 9:43 am

Deva: The onError solution isn’t very good at all. You should look at using try/catch instead. I wrote this article a long time ago. My JavaScript skills have improved a lot since. Maybe I should think about writing a better solution one day.

  1. Rob's gravatar#13: Rob Says:
    December 12th, 2006 at 9:35 pm

Wouldn’t the following do the same?

In the opener:

function openPopup() {
popupWin = window.open( “”, “popupWin” );
if( !popupWin || popupWin.closed ) {
popupWin = window.open( “the_url_to_your_popup.htm”, “popupWin” );
} else popupWin.focus();
}

In the popup:
No need for notifier.

  1. Martin's gravatar#14: Martin Says:
    December 12th, 2006 at 9:57 pm

Rob: can your code handle getting back control over orphaned popups? My code deals with the case where you open a popup, navigate to a different page and need to regain control of the popup. With your code, the next page won’t have a reference to the popup so can’t control it.

  1. Rob's gravatar#15: Rob Says:
    December 12th, 2006 at 10:03 pm

Slight correction to my previous post… You should also check for the presence of doSomething in the popup window (or any other non-null defined variable) since the open method will always return a valid object.

I believe the following code in the opener will do the same without the need for the notifier in the popup window.

function openPopup() {
popupWin = window.open( “”, “popupWin” );
if( !popupWin || popupWin.closed || !openWin.doSomething ) {
popupWin = window.open( “the_url_to_your_popup.htm”, “popupWin” );
} else popupWin.focus();
}

  1. Rob's gravatar#16: Rob Says:
    December 12th, 2006 at 10:09 pm

Martin,

My understanding is that so long as the orphaned popup has a unique window name (window.name) it will be accessible with open(”", windowName);

I would be curious if you knew of a case where this may not work.

Regards,
-Rob

  1. MC's gravatar#17: MC Says:
    February 19th, 2007 at 11:41 am

I’ve had the following problem: users browsing through the website, watching some external player version of a thumbnail show which is present at a small scale in multple site pages. I wanted to detect surely whether this popup is open, on the body onload of each page of the website (without reopening it). It seems to me that the notifyOpener() function is the only way available . Thank you!




<< Home

Powered by Blogger

Google
WWW THIS BLOG