Passing Data From A Pop-Up Window To A Parent Window Using ColdFusion And Javascript

Posted May 2, 2007 at 2:16 PM by Ben Nadel

Tags: ColdFusion, Javascript / DHTML

Peter Bell was looking for an example of how to get data from a pop-up window back into a calling (opener, parent) window. I am not saying this is by any means a "best" practice, but this is how I do it. There are, of course, two pages: the pop-up and the primary. The pop-up needs a way to pass data back to the primary page. The primary page needs a way to handle the data.

The easiest way to accomplish this chain of communication is to pass the name of a Javascript call back method to the pop-up window. The pop-up window has a reference to the parent window (window.opener) and can then invoke the call back method through that window reference. Once the pop-up window has the reference, we can store it in a form field (if the page has a form with refresh-potential) so that we never lose the reference.

The beauty behind this is that we are coding to an interface. The pop-up window doesn't need to know how the data is being handled; it only needs to know that the data can be taken by some method. The parent (opener) doesn't need to know anything about the pop-up, it only needs to know that its call back method might be invoked. The only real agreement that has to happen between the two pages is that the data will be passed in some standard form (ie. an array, an object, a comma delimited list).

Ok, so let's take a look at the primary page:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Parent Window</title>
  •  
  • <script type="text/javascript">
  •  
  • function PopUp(){
  •  
  • // Open the new window with ID selector.
  • // Pass PopUpHandler() as the data handler
  • // to which the popup will broadcast its'
  • // data selection.
  • window.open(
  • "./pop_up.cfm?data_handler=PopUpHandler",
  • "",
  • "width=300,height=300"
  • );
  •  
  • void( 0 );
  • }
  •  
  •  
  • function PopUpHandler( strIDs ){
  • var objIDList = document.forms[ 0 ].lst_id;
  •  
  • // Set the ID list.
  • objIDList.value = (
  • objIDList.value +
  • (objIDList.value.length > 0 ? "," : "" ) +
  • strIDs
  • );
  • }
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <form>
  •  
  • <p>
  • ID List:
  • </p>
  •  
  • <p>
  • <input
  • type="text"
  • name="lst_id"
  • value=""
  • size="30"
  • />
  •  
  • <a href="javascript:PopUp();">Find IDs</a>
  • </p>
  •  
  • </form>
  •  
  • </body>
  • </html>

Not much is going on here. The page just launches the pop-up and sends, as part of the query string, the textual name of the call back method. Then, it just sits there and waits. Notice that the call back method, PopUpHandler(), doesn't know anything about the pop-up or where the data is coming from - it just knows that if it gets data to add it to the form field.

Now, let's take a look at the pop-up window:

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!---
  • Param the URL and FORM variables. We are doing
  • both as you never know where this value will be
  • coming from. This value is the name of the
  • Javascript method that will "handle" our data
  • broadcast.
  • --->
  • <cfparam
  • name="URL.data_handler"
  • type="string"
  • default=""
  • />
  •  
  • <!--- Param the form using the URL. --->
  • <cfparam
  • name="FORM.data_handler"
  • type="string"
  • default="#URL.data_handler#"
  • />
  •  
  • </cfsilent>
  •  
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Pop-Up Data Selector</title>
  •  
  • <script type="text/javascript">
  •  
  • <!---
  • Check to see if we have a data callback handler
  • to work with. If we do not, then alert the user
  • that something is wrong so they don't waste their
  • time doing stuff.
  • --->
  • <cfif Len( FORM.data_handler )>
  •  
  • function SendData( objForm ){
  • var strIDs = "";
  • var i = 0;
  •  
  • // Loop over the form fields to build the data.
  • for (i = 0 ; i < objForm.girl_id.length ; i++){
  •  
  • if (objForm.girl_id[ i ].checked){
  •  
  • strIDs = (
  • strIDs +
  • (strIDs.length > 0 ? "," : "") +
  • objForm.girl_id[ i ].value
  • );
  •  
  • }
  •  
  • }
  •  
  •  
  • // Try to pass the data back to caller.
  • try {
  •  
  • <cfoutput>
  • window.opener.#FORM.data_handler#( strIDs );
  • </cfoutput>
  •  
  •  
  • // Close current window.
  • window.close();
  •  
  • } catch (objError){
  •  
  • alert(
  • "There was an error passing the\n" +
  • "data back to the parent window"
  • );
  •  
  • }
  •  
  • }
  •  
  • <cfelse>
  •  
  • <!---
  • We have no data handler. Alert the user
  • before they do anything.
  • --->
  • alert(
  • "Something went wrong. No data handler\n" +
  • "can be found. Please try closing and\n" +
  • "opening this window again"
  • );
  •  
  • </cfif>
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <cfoutput>
  •  
  • <form>
  •  
  • <!---
  • Store the data handler in a hidden form
  • field. This way, if the page refreshes
  • (ie. pagination, search) then we will see
  • have the proper handler.
  • --->
  • <input
  • type="hidden"
  • name="data_handler"
  • value="#FORM.data_handler#"
  • />
  •  
  •  
  • <label for="girl_1">
  • <input
  • type="checkbox"
  • name="girl_id"
  • id="girl_1"
  • value="1"
  • />
  • Sammantha
  • </label>
  •  
  • <label for="girl_2">
  • <input
  • type="checkbox"
  • name="girl_id"
  • id="girl_2"
  • value="2"
  • />
  • Alexa
  • </label>
  •  
  • <label for="girl_3">
  • <input
  • type="checkbox"
  • name="girl_id"
  • id="girl_3"
  • value="3"
  • />
  • Sarah
  • </label>
  •  
  • <input
  • type="button"
  • value="Use Selected Girls"
  • onclick="SendData( this.form );"
  • />
  •  
  • </form>
  •  
  • </cfoutput>
  •  
  • </body>
  • </html>

Again, not a ton of stuff going on. The Javascript is more complicated because we are doing our best to alert the user if something went wrong (ie. the parent window was closed and the communication chain was no longer valid). Other than that, it's just a standard ColdFusion page with a form. The only difference is that instead of submitting the form, we are calling a method that bundles the data and attempts to send it to the call back method of the parent window.

Also, note that we are actually storing the name of the call back method in the form field. If this page was a fairly complex page with search capabilities and pagination, the page might refresh. In that case, we might lose the call back method name. By storing it in the FORM and CFParaming the URL and FORM scopes, we help to ensure that this method name will never be lost.

So again, maybe not a best practice, but this methodology has worked nicely for me when I need to use it.



Reader Comments

May 2, 2007 at 2:50 PM // reply »
3 Comments

That is a sensible approach. You can also do this by convention across a system and avoid having to have the server generate your javascript. You can document that popups can always call the WINDOWNAME_response(responseObject) function to post data back to the opener. This lets you have different handlers for different popups.

Same way from the javascript perspective, but you just cutout the CFOutput step in your popup javascript.


May 2, 2007 at 3:50 PM // reply »
11,314 Comments

@Chip,

Not a bad idea at all. Conventions always help write better code, the way I see it. The only slip up would be if there were two places on a screen that could handle the call back (with two different purposes). In that case, maybe there could be an override that could be used.


May 2, 2007 at 5:20 PM // reply »
111 Comments

This is great - just what I needed to know - and thanks Chip for the comment. Also a good point. Life is looking good. Even *I* can understand this Javascript!


May 2, 2007 at 5:21 PM // reply »
11,314 Comments

Sweeeet. Glad that we could help.


May 3, 2007 at 11:16 AM // reply »
95 Comments

Ben,

a couple of points to consider:

1. You could use something like "Form.serialize(objForm)" to serialize all the form values. I'm talking about Prototype.js but I'm sure jQuery (your favorite) has a similar method.

2. Popup windows are kind of old school now. Try overlay windows. There is one I've used called Lightwindow (based on Prototype and Scriptaculous): http://stickmanlabs.com/lightwindow/ or the jQuery equivalent: http://jquery.com/demo/thickbox/. The advantage of these are that 1) The look way more cool with much more options and 2) Your invoke happens from the same page and stays on the same page so there is no need to pass vars back and forth.


May 3, 2007 at 11:20 AM // reply »
11,314 Comments

@Boyan,

I agree. The pop-up is old school. I have just not gotten around to implementing something uber cool like the thick box or light box or whatever else there is. One day :)


May 3, 2007 at 11:24 AM // reply »
111 Comments

Overlay windows look cool, but I'm not sure how I'd implement that in practice as the window needs to be able to go back to the server for filtering or paging through the recordset, so I'd have to make the entire iFramed page using AJAX otherwise on clicking links it'd reload the whole page - not just the overlay window.


May 3, 2007 at 11:26 AM // reply »
11,314 Comments

Yeah, that's part of the reason I have not tried it yet.... the PopUp is just so easy cause there are no concerns.


May 3, 2007 at 11:38 AM // reply »
95 Comments

It's your lucky day I guess. You don't have to do anything special with LightWindow for calling server side pages. I just did one that calls an .cfm page with Ajax. The setting is just another parameter.


May 3, 2007 at 12:00 PM // reply »
111 Comments

Yep, but what happens when THAT page then wants to call another page into the same LightWindow without refreshing the whole page? That was the problem I had.


May 3, 2007 at 2:50 PM // reply »
95 Comments

Since you create the lightwindow in the first page, you can reinitialize it in script any time you want. You can even write javascript to change the content of the lightwindow.


May 3, 2007 at 4:59 PM // reply »
111 Comments

Right, but my use case is that I want to open a window (which uses almost identical code I would use elsewhere) to list, filter, order and search through a recordset so the code in the window I open needs to be able to go back to the server without knowing anything about the fact that it is in a sub window.

I know I could do this is the screen was AJAX based, but unless I make all calls back the server AJAX, I don't think anything other than a pop up window would work (unless I'm misunderstanding this).


May 4, 2007 at 8:49 AM // reply »
95 Comments

Hmm...maybe I didn't make it clear enough. You can tell lightwindow to use .cfm page for the content of the window (this pulls the page from the server with Ajax). That way you code your page like it would any other page but it's loaded in the lightwindow like anything else. In your use case, the contents of the lightwindog page will to go back to the server without knowing anything about the fact that it is in a sub window.

If you give me something more specific, I can probably put together an example.


May 4, 2007 at 8:53 AM // reply »
11,314 Comments

@Boyan,

I think what Peter means is that that pop-up is not displaying a single page of data. Imagine that the pop-up has a search page where the user can filter the data they are looking for. That would require a form submission. If this was in a pop-up, the form would submit, the page would refresh, the new data would be displayed.

If this was in a lightbox style interface, then how would that search form be submitted? It could not be done using a form submission as that would cause the entire page to submit, not just the lightbox area. Instead, he would have to have AJAX handle the form submission itself and then refresh the contents of the lightbox window.

I am not saying that it is not possible; I think Peter (and myself) is just concerned that having a "pop-up" live within the context of another page makes updating the pop-up more difficult.


May 4, 2007 at 9:00 AM // reply »
95 Comments

Ben, that's an interesting point. Let me try that today and see how it works. I'll let you know.


May 4, 2007 at 9:02 AM // reply »
11,314 Comments

@Boyan,

Certainly you could have some sort of flag in the URL for the "pop-up" that toggles the FORM action:

<cfif URL.use_ajax>

action="javascript:DoSomethingWithAjax();" onsubmit="return(false);"

<cfelse>

action="#CGI.script_name#"

</cfif>

Of course, this would assume that only standard submits work and that this can be "generalized" to work with lots of different pages. Just a thought.


May 4, 2007 at 9:04 AM // reply »
111 Comments

Hi Boyan,

That'd be great (and thanks for taking so much time on this). I know the long term solution is to use a lightwindow which loads a page that then uses AJAX for all its form submissions and links, but that now means that to get this feature working I have to recode all of my generic paging, filtering and ordering code that currently just returns to the server for a page refresh to use AJAX which is something I should do anyway but is not a completely trivial task.

That said, if there is any way I can load the html from a cfm into a lightwindow where the cfm page doesn't need to know it is in a sub window and can still return to the server and do a page refresh that only refreshes the content of the light window, that'd be very cool, but I'm not sure that makes any technical sense . . .


May 4, 2007 at 9:07 AM // reply »
111 Comments

@Ben, The problem with that is I need to then write a generalized back end that'll accept those requests at which point I probably wouldn't even both making it conditional - I'd just make all of my ordering, paging and filtering AJAX (which is something I'm considering anyway). But it isn't a trivial task - especially when you consider the security implications and the right approaches to handling those.


May 4, 2007 at 10:50 AM // reply »
11,314 Comments

@Peter,

Certainly not a trivial task at all. I can only imagine (as I still go the standard pop-up route). It will be interesting to see where this goes.


Jul 24, 2007 at 2:34 PM // reply »
5 Comments

Ben,

Thanks very much for this script. It worked like a charm, even for a js novice like me, but for one issue. Once completing my form, if I navigate away from the page and then back again, I lose all the data.

For example, if I submit the form without completing some required field, I get the data validation message inviting me to go back to the form. When I do so the all the fields are empty. This doesn't happen unless I've passed data using the popup.

Any suggestions? Thanks in advance.

--r


Jul 24, 2007 at 3:10 PM // reply »
11,314 Comments

@Rick,

I am not sure I am fully understanding. Which page is refreshing? The pop-up or the primary window?


Jul 24, 2007 at 4:07 PM // reply »
5 Comments

The primary window.


Jul 24, 2007 at 5:38 PM // reply »
5 Comments

Ben,

Further update. It appears to be an IE problem. Everything works fine in Firefox.

r


Jul 25, 2007 at 1:29 PM // reply »
5 Comments

Ben,

I found a workaround. If I save the form as .htm, rather than .cfm the form data persists. Don't know why this is so, but I can live with the form being .htm. If you have any ideas on this, I'd still be interested, as it may come up again. In any case, your script has been a huge help. Thanks.

rl


Jul 25, 2007 at 1:58 PM // reply »
11,314 Comments

@Rick,

Sorry for the delayed response. At first, I was confused, because I was trying to make the connection between the pop-up window and the error you are having... but, then when you said it was the primary window that was refreshing, I realized that this does not have anything to do with the pop-up :)

The issue you are having is dependent on the browser, I think, and how it caches form data. I don't really know anyway to get around that other than you should probably repopulate the form manually (via ColdFusion) when the people come back to the form maybe??


Jul 25, 2007 at 2:11 PM // reply »
5 Comments

Ben,

Thanks for your response. As I indicated in my previous post, it's a moot point, since I found a workaround.

FWIW, it does seem to have something to do with the popup. If I fill in the form data (except for the fields populated by the popup) and navigate away and then back, the form data persists. But, once I open the popup, if I navigate away, my date is lost. I emailed you a link to an example of this.

Thanks.


Jul 25, 2007 at 2:26 PM // reply »
11,314 Comments

@Rick,

That is interesting that the behavior changes... the link you sent doesn't seem to have any pop-up stuff (or maybe I am just not seeing it). But it looks like you are using ColdFusion built-in form field validation, is this correct?


Aug 3, 2007 at 5:58 PM // reply »
2 Comments

Nifty! Definitely the best description of this technique I've found.

I discovered something curious though. (I running a Windows version of IE6.) I don't believe this is a problem in Firefox.

It seems like any variable can be passed from the child to primary window except for 2-dim Arrays (or rather Array of Arrays). Weird, eh? Strings... good. Arrays look good. Even simple JSON objects pass back to the primary window.

I can see someone interested in passing a JSON object back to the primary window, but BEWARE. Don't include any 2-dim Arrays in your JSON. It appears to populate the primary window's global JavaScript variable, but after closing the popup window the variables return to their original primary window state.

Please somebody prove me wrong.

I guess the other possibility is to pass back a string and perform an eval() to create a real JSON object. Ain't pretty, but it'd work.


Aug 6, 2007 at 2:04 PM // reply »
11,314 Comments

@ArdMan,

Very strange indeed. I have not come across that, but I have also never tried that either.


May 28, 2008 at 7:10 PM // reply »
1 Comments

Thank you so much for this! I have been pulling my hair out trying to find away to do this. I have tried many different methods... none worked. This was easy and works perfectly.


May 28, 2008 at 7:11 PM // reply »
11,314 Comments

@Kim,

Glad to be of service.


Jun 27, 2008 at 4:26 PM // reply »
2 Comments

This worked like a champ! Thank you, thank you!


Jun 13, 2010 at 9:38 PM // reply »
6 Comments

@Ben,

Thanks for this wonderful little piece of info. You helped out big time as usual. Seems every time I search a problem I run into your site comes up with the answer!

Anyway, I should note one little bug with the javascript just in case someone runs into this in the future. If there is only one result in the pop_up.cfm page then the value will not get passed to the parent.

For example, if you get rid of girl_2 and girl_3 inputs, and keep only girl_1 and try to pass the value, nothing gets passed. I'm not a javascript pro so what I did to work around it was just something like this in my pop_up.cfm search results at the end of the form, simulating at least 2 records. This is a fix for now.

<cfif searchresults.recordcount eq 1>
<input type="hidden" name="girl_id" id="girl_2" value="" />
</cfif>


Jun 14, 2010 at 10:26 PM // reply »
11,314 Comments

@Rob,

Hmm, that's odd. I'm looking at the Javascript and without running it, nothing is jumping out at me. In any case, I'm glad that you could make it work for you with a bit if a workaround.


Jun 23, 2011 at 6:22 AM // reply »
1 Comments

@Ben
Thank you very much for this post.
Although I don't know ColdFusion at all, I managed to rewrite your example into php.

Regards
Darek


Apr 7, 2013 at 12:05 PM // reply »
2 Comments

thnx Ben,

Sorry my messages is too late.
How can replace popup with pirobox or lightbox etc ...


Apr 7, 2013 at 12:12 PM // reply »
2 Comments

Can you give an example Thanks again..
web developper


Apr 15, 2013 at 2:41 AM // reply »
1 Comments

I have 2 parent windows accessing the same child window on click of a button from parent window. while redirecting the value is always going to my first parent. but i opened the child window from second parent.( Note: First i am opening from first parent then again i am opening from second parent so the child window overwrites using javascript). please give me a solution asap :)



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
seb
Jun 20, 2013 at 2:32 AM
Working With Inherited Collections In AngularJS
@mike, @ben, The best article about scope and prototypal prototypical inheritance in angularjs is http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical- ... read »
Jun 20, 2013 at 2:17 AM
ColdFusion NumberFormat() Exploration
Nice read thanks Ben, Is there a way to mask a negative number? Long story short in the finance sector when you go 'short' on a stock you want the price to fall this is a good thing because you are ... read »
Jun 20, 2013 at 1:09 AM
The Beauty Of The jQuery Each() Method
my html code : <html> <head> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="nss.js"> ... read »
Jun 19, 2013 at 11:31 PM
Directive Link, $observe, And $watch Functions Execute Inside An AngularJS Context
@Ben, bunch to learn indeed, but thats fun part : ) ... read »
Jun 19, 2013 at 10:41 PM
Referencing ColdFusion Query Columns In A Loop Using Both Array And Dot Notation
Burdock-roots Are you going fat day by day? You need to be good for your family and make some money too. So we bring for you a best product that helps you to be more energetic every day. You will b ... read »
Jun 19, 2013 at 9:52 PM
Working With Inherited Collections In AngularJS
I recognize the applicability of your solution, and how easy it makes to share data across multiple views or even "submodules" of rather simple application. But it seems to me that it creat ... read »
Jun 19, 2013 at 9:38 PM
Directive Link, $observe, And $watch Functions Execute Inside An AngularJS Context
@Alesei, Glad you like it. Even after working with AngularJS for months, I still get a bunch of unexpected, "$digest is already in progress". So hard to debug sometimes! ... read »
Jun 19, 2013 at 9:36 PM
Working With Inherited Collections In AngularJS
@Mike, The relationship of $scope values is definitely an interesting thing! But it's not simple - it really forces you to understand prototypal inheritance, which is not at all a simple topic! Gla ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools