在Parent关闭后访问Sibling Windows

时间:2014-01-20 17:26:00

标签: javascript html html5

我目前正在开发一个webapp,我希望父母的子窗口能够相互通信。我希望兄弟姐妹沟通的原因是因为不能保证父母会保持打开状态,因为父母会被iFramed带入Microsoft CRM,如果他们更改了他们的标签,家长会关闭(如果不是这样我会让父母处理通信。)

以下适用于Chrome,但不适用于IE,我想知道是否还有其他方法可以完成。

在父窗口中:

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

在子窗口中:

var siblings = window.opener.children;

然后,如果父窗口关闭,此行仍然在chrome中工作,但不在IE

siblings[0].close();

我还没有尝试过关闭以外的任何东西,但我相信所有功能都应该是可访问的(因为它们位于同一个域中)。它在Chrome中运行的事实对我来说是一个惊喜,但我们确实需要支持IE

修改

刚想到的一种方法是,当父级关闭以将子级的父级更改为其中一个子级时(IE父级将父级重新分配为子窗口)。但我不确定这是否有效甚至可能,因为我觉得它仍然会使引用消失

1 个答案:

答案 0 :(得分:2)

好吧,我呕吐了一些东西尝试测试它,因为我以前从未听说过它。但是,它运作得很好。我有一个父窗口,可以跟踪它在数组中生成的窗口。然后每当它产生一个新窗口时,它会告诉孩子他们的新兄弟/姐妹,以便他们也可以跟踪他。然后每个孩子都有自己的方法,让他们从兄弟姐妹那里接收消息。我还给了他们简单的输入,以便能够互相发送消息并打印出他们收到的消息。

<强> 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's 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's 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's name
    * @type String
    */
   exports.name = "";

   /**
    * This child's 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's 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's 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);
   });
}