How to Close Child Windows When the Parent Window Closes
The Motivation
When moving an application from Windows to web, I noticed that certain functionality available only in a Windows Form is expected to be carried over to the web form. Unfortunately, sometimes, achieving the said functionality can be a bit more difficult than expected.
The Objective
Being object oriented in nature, I wanted a drop in solution on the client side to achieve closing child windows. Doing a bit of research told me that the call to window.open
would return a window
object that I could then (at a later time) call close
on. The question to be answered was how to store each window object upon opening, and how to detect the browser closing in order to close the child windows.
Note: There are more than a few articles out there on creating objects in JavaScript, and it is beyond the scope of this article.
The Code
Persisting Window Handles
The first thing, persist the window object returned by the open
function call. I achieved this by storing it as a private Array
within my object.
function windowController(){
var loadedWindows = new Array()
…..
}
Encapsulate window.open
Now that I have a nifty array, I need a public method that would wrap the window.open
function to capture the returned object and add to my array.
this.popUpWindow = function(wndUrl, wndName, wndWidth, wndHeight){
var windObj = null;
try
{
if(typeof wndWidth == 'undefined') wndWidth=500;
if(typeof wndHeight == 'undefined') wndHeight=250;
windObj = window.open(wndUrl,
wndName, 'toolbar=0,menubar=0,resizable=0,location=0,
directories=0,width='+wndWidth+',height='+wndHeight);
windObj.registerID = wndName;
loadedWindows[loadedWindows.length] = windObj;
}
catch(ex) {
alert('WindowController.popUpWindow: ' +
'Exception occured, message: ' + ex.message)
}
return windObj;
}
Further Enhance Encapsulation
Since I am wrapping window.open
, I decided to only allow a single instance of each handle ID to be opened, by checking the ID against each window handle already loaded, and if it exists, I simply call focus
(this is similar to what is expected from Windows Forms).
this.popUpWindow = function(wndUrl, wndName, wndWidth, wndHeight){
var windObj = null;
try{
windObj = findWindow(wndName);
if (windObj != null)
{
windObj.focus();
}
else
{
......
}
where the findWindow
function simply iterates the array, and returns null
if not found.
function findWindow(winHandle){
for (var i=0; i< loadedWindows.length; i++){
if (loadedWindows[i].closed == true){
loadedWindows.splice(i,1);
i--;
}
else{
if (loadedWindows[i].registerID == winHandle)
return loadedWindows[i];
}
}
return null;
}
Pulling it all Together
Finally, I needed a way to know when the browser was closing in order to close all my child windows. Of course, IE, compared to other browsers, has different ways to register to events, so I created a generic method to achieve this.
function WireEvent(elem,target,func){
if (elem.addEventListener)
elem.addEventListener(target, func, false);
else if (elem.attachEvent)
elem.attachEvent(target, func);
}
WireEvent(window,'onunload',_windowController.closeAllWindows);
where the onunload
function is handled as follows:
this.closeAllWindows = function(){
for(var x = 0; x < loadedWindows.length; x++){
try{
loadedWindows[x].close();
}
catch(err) {
alert('WindowController.closeAllWindows: ' +
'Exception occured, message: ' + err.message)
}
}
}
Looks like all is in place, the last thing I needed was the ability to init the object so it would be available from code, so I added the call at the top of the js file, as follows….
var _windowController = new windowController();
Using the Object
Now, if the file is included, we have access to the window controller, so as long as all opened child windows are done through this object, we can be certain they will close upon browser exit as long as we are not cross domain.
_windowController.popUpWindow('ChildForm.aspx', 'ChildForm');