Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED Express NYC (Apr. 2009) with: Nafisa Sabu

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

By Ben Nadel on

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

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.

Reply to this Comment

@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.

Reply to this Comment

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!

Reply to this Comment

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.

Reply to this Comment

@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 :)

Reply to this Comment

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.

Reply to this Comment

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

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

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

Reply to this Comment

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.

Reply to this Comment

@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.

Reply to this Comment

@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.

Reply to this Comment

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

Reply to this Comment

@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.

Reply to this Comment

@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.

Reply to this Comment

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

Reply to this Comment

@Rick,

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

Reply to this Comment

Ben,

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

r

Reply to this Comment

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

Reply to this Comment

@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??

Reply to this Comment

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.

Reply to this Comment

@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?

Reply to this Comment

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.

Reply to this Comment

@ArdMan,

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

Reply to this Comment

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.

Reply to this Comment

@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>

Reply to this Comment

@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.

Reply to this Comment

@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

Reply to this Comment

thnx Ben,

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

Reply to this Comment

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 :)

Reply to this Comment

This really helps me out, but I was wondering if there is anyway to get help on something else that kind of relates to this.

I am working on a web interface where I want users to go to the beginning page where there is a drop-down with options. When the user selects one of the options I want another drop-down to appear with more options. And so on.

It would also be good to have the first selection open a popup where the further items can be selected and then added to the a form on the main page.

I'm confusing myself.

so I'd have input1.php which has the first drop-down menu. When the selection is made and submit clicked either the form will become unhidden or redirect to a new page, but then there would be a new form and a popup would come up with options. So to start there would be a list of items related to the first selection in the database. Then the user would click add new item2 and a add new item3 in the popup then after this is done the items would then be applied to the original form on the input1.php page. These new items would take data from the mysql database and display the output. Then there would be links to customize each item, each link opening a popup and once the customization is done these would also be applied to the input1.php form. I don't want any of this data to be submitted to the database until after everything is complete and the user is happy with the results.

After that I'm going to make it so the user can come back and update the data with the same links and such, but that should be easy to do once the first part is done. I just don't know javascript too well, and I really don't want to get into jquery.

Reply to this Comment

Hi,
I have a project that child window pass the value
to parent window. But in child window, I must search based on id or name. So, it will list down the information, then id from child window will pass to parent. How can I make this?
Please help me. I am stuck. :'(

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.