Access to Sibling Windows after closing a parent

I am creating a webapp in which I would like the parent child windows to be able to communicate with each other. The reason I want siblings to be able to communicate is that there is no guarantee that the parent will remain open, since the parent iFramed is in Microsoft CRM and the parent will close if they change their tab (if it weren’t I would just pass the message to the parent message).

It works in Chrome, but not IE, and I'm wondering if there is another way to do this.

In the parent window:

var children = new Array();
function openWindow(...){
  children.push(window.open(...));
}

In the "Children" window:

var siblings = window.opener.children;

Then, if the parent window closes, this line still works in chrome, but not IE

siblings[0].close();

, , , ( ). , Chrome, , IE

, , , , ( IE ). , , ,

+4
1

, -, , . . , , . , , /, . , . , , .

Edit

, , . , loaded unloaded, , . , . , , - .

ParentModule.spawnChild(). , . , , . . , same-origin policy.

, .


parent.html

<!DOCTYPE html>
<html>
   <head>
      <title>Parent</title>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width">
      <style></style>
      <script src="parent.js"></script>
   </head>
   <body onload="main();" onunload="ParentModule.notifyChildren();">
      <button id="closer">Close</button>
      <div>New Child Name</div>
      <input type="text" id="newChildName">
      <button id="spawner">Spawn Child</button>
      <div id="log"></div>
   </body>
</html>

parent.js

/**
 * The namespace for the parent window
 * @namespace ParentModule
 */
var ParentModule = (function() {
   // Create an object to keep functions we want to be public
   var exports = {};

   /**
    * The children windows
    * @type Object
    */
   exports.childWindows = {};

   /**
    * The number of children we have
    * @type Number
    */
   exports.numChildren = 0;

   /**
    * Create a new window and keep other windows in sync with the event
    * @param {String} childName The name of the new child
    * @returns {undefined}
    */
   exports.spawnChild = function(childName) {
      // Create a new window
      var newChild = window.open(
              "child.html",
              childName,
              "height=200, width=200, top=200, left=" + (200 * exports.numChildren)),
              parent = window;

      // Whenever the new window is finished loading, tell the window its
      // name, its parent, and its siblings.  Then tell the other children
      // about their new siblings as well so that they can message him
      // and mourn him if he gets closed.
      newChild.addEventListener("load", function() {

         // Log that the child was made
         document.getElementById("log").innerHTML = "New child: " + childName;

         // Tell the child its name
         newChild.ChildModule.giveName(childName);

         // Tell the child its parent
         newChild.ChildModule.setParent(parent);

         // Tell new child about its siblings
         for (var child in exports.childWindows) {
            newChild.ChildModule.addSibling(exports.childWindows[child], child);
         }

         // Tell all children about the new child
         for (var child in exports.childWindows) {
            exports.childWindows[child].ChildModule.addSibling(newChild, childName);
         }

         // Keep track of the new child yourself
         exports.childWindows[childName] = newChild;

         // Tell the child to say hi
         newChild.ChildModule.start();
      });
   };

   /**
    * Function called whenever a child is closed
    * @param {String} childName Child that is getting closed
    * @returns {undefined}
    */
   exports.removeChild = function(childName) {
      var log = document.getElementById("log");
      log.innerHTML = "My child: " + childName + " is gone";
      delete exports.childWindows[childName];
   };

   /**
    * Let all children know that you are being closed
    * @returns {undefined}
    */
   exports.notifyChildren = function() {
      for (var child in exports.childWindows) {
         exports.childWindows[child].ChildModule.removeParent();
      }
   };

   /**
    * Shortcut to be able to close all children
    * @returns {undefined}
    */
   exports.closeAllChildren = function() {
      for (var child in exports.childWindows) {
         exports.childWindows[child].close();
      }
   };

   // Allow functions to get called
   return exports;
}());

/**
 * Function to setup the listeners
 * @returns {undefined}
 */
function main() {
   document.getElementById("spawner").addEventListener("click", function() {
      ParentModule.spawnChild(document.getElementById("newChildName").value);
   });
   document.getElementById("closer").addEventListener("click", function() {
      ParentModule.closeAllChildren();
   });
}

child.html

<!DOCTYPE html>
<html>
   <head>
      <title>Child</title>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width">
      <style></style>
      <script src="child.js"></script>
   </head>
   <body onload="main();" onunload="ChildModule.notifyKinOfDeath();">
      <div>Destination Window</div>
      <input type="text" id="whichWin">
      <div>Message</div>
      <input type="text" id="message">
      <button id="sendMessage">Send Window a Message</button>
      <div id="myName">I'm a child</div>
      <div id="log"></div>
   </body>
</html>

child.js

/**
 * The namespace for the child window functions
 * @namespace ChildModule
 */
var ChildModule = (function() {
   // Create an object to keep functions we want to be public
   var exports = {};

   /**
    * The other siblings that this window should know about
    * @type Object
    */
   exports.siblingWindows = {};

   /**
    * This child name
    * @type String
    */
   exports.name = "";

   /**
    * This child parent
    * @type Window
    */
   exports.parent = null;

   /**
    * This function is written from the perspective of another window
    * This is the way that another window can send THIS window a message
    * @param {String} envelope Message for the child to get
    * @returns {undefined}
    */
   exports.sendMessage = function(envelope) {
      var log = document.getElementById("log");
      log.innerHTML = "Got: " + envelope.message + " from: " + envelope.sender;
   };

   /**
    * This is written from the child perspective
    * This will actually send the message to the target sibling
    * @param {String} targetSibling The sibling to message
    * @param {String} message The message to send
    * @returns {undefined}
    */
   exports.passMessage = function(targetSibling, message) {
      var log = document.getElementById("log");
      if (exports.siblingWindows[targetSibling]) {
         exports.siblingWindows[targetSibling].ChildModule.sendMessage({
            "sender": exports.name,
            "message": message
         });
      }
      else {
         log.innerHTML = "I have no sibling: " + targetSibling;
      }
   };

   /**
    * This function is written from the perspective of another window
    * Give this child its name
    * @param {type} name
    * @returns {undefined}
    */
   exports.giveName = function(name) {
      exports.name = name;
      document.getElementById("myName").innerHTML = "My name is: " + exports.name;
   };

   /**
    * Function to get the child name
    * @returns {String}
    */
   exports.getName = function() {
      return exports.name;
   };

   /**
    * Set the parent of this window
    * @param {Window} parent The window that spawned this child
    * @returns {undefined}
    */
   exports.setParent = function(parent) {
      exports.parent = parent;
   };

   /**
    * What this child should do once started
    * @returns {undefined}
    */
   exports.start = function() {
      var log = document.getElementById("log");
      log.innerHTML = "Hello, my name is: " + exports.name;
   };

   /**
    * Understand that a we have a new sibling that we can message
    * @param {Window} sibling The new sibling
    * @param {String} siblingName The name of the new sibling
    * @returns {undefined}
    */
   exports.addSibling = function(sibling, siblingName) {
      var log = document.getElementById("log");
      exports.siblingWindows[siblingName] = sibling;
      log.innerHTML = "I have a brother named: " + siblingName;
   };

   /**
    * Understand that a sibling has left us so we can't message them
    * @param {String} siblingName Name of sibling that is gone
    * @returns {undefined}
    */
   exports.removeSibling = function(siblingName) {
      var log = document.getElementById("log");
      log.innerHTML = "My brother: " + siblingName + " is gone";
      delete exports.siblingWindows[siblingName];
   };

   /**
    * Understand that the parent has been closed
    * @returns {undefined}
    */
   exports.removeParent = function() {
      var log = document.getElementById("log");
      exports.parent = null;
      log.innerHTML = "My parent is gone";
   };

   /**
    * Whenever a child is unloaded, notify everyone of its death
    * @returns {undefined}
    */
   exports.notifyKinOfDeath = function() {
      // Tell parent of your closing
      if (exports.parent) {
         exports.parent.ParentModule.removeChild(exports.name);
      }

      // Tell siblings of your closing
      for (var sibling in exports.siblingWindows) {
         exports.siblingWindows[sibling].ChildModule.removeSibling(exports.name);
         console.log("I've told them");
      }
   };

   // Allow functions to get called
   return exports;
}());

/**
 * Function to setup listeners
 * @returns {undefined}
 */
function main() {
   document.getElementById("sendMessage").addEventListener("click", function() {
      // Get the message and the window to send to
      var whichWin = document.getElementById("whichWin").value,
              messageToSend = document.getElementById("message").value;

      // Send the message
      ChildModule.passMessage(whichWin, messageToSend);
   });
}
+2

All Articles