在父窗口关闭后访问同级窗口

4
我目前正在开发一个Web应用程序,在这个应用程序中,我希望父窗口的子窗口能够相互通信。我想让这些兄弟姐妹之间进行通信是因为不能保证父窗口会一直打开,由于父窗口被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();

我还没有尝试除close之外的其他方法,但我认为所有函数都应该是可访问的(因为它们在同一个域中)。它在Chrome中工作的事实让我感到惊喜,但我们需要支持IE。
编辑:
刚刚想到的一种方法是当父级关闭时,将子级的父级更改为其他子级之一(即父级重新分配父级为子窗口)。但我不确定这是否有效或可能,因为我觉得它仍然会使引用消失。
1个回答

2

好的,我写了一些代码来测试这个功能,因为之前从未听说过。但是,它的效果很不错。我有一个父窗口,它在数组中跟踪它产生的子窗口。每次它产生一个新窗口时,它会告诉子窗口他们的新兄弟姐妹,以便他们也能跟踪他。然后,每个子窗口都有自己的方法让它们接收来自其兄弟姐妹的消息。我还为它们提供了简单的输入来相互发送消息并打印出它们接收到的消息。

编辑

好的,我完全重写了代码,使其更加健壮,并使您不必手动告诉子代彼此的存在。现在,父级已钩入事件,以便在加载和卸载时通知其子代它将不再存在。但由于在创建它们时就通知了子代彼此的存在,所以这并不重要。唯一有点棘手的是,如果在父窗口关闭后创建更多的窗口。

父窗口通过ParentModule.spawnChild()函数协调子窗口。这就是让子窗口知道新子窗口的方法。因此,如果您想在父窗口关闭后创建新窗口,则必须调整一些函数。但是,这里是即使在父窗口关闭后进行父子窗口之间交互的代码。请记住,这依赖于同源策略

还添加了一种让父窗口知道其子代何时关闭的方法。


父级

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

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);
   });
}

好的,最终我通过使用会话存储来解决了这个问题,但我确实想要给你回答问题(非常详尽)的功劳。不过我在Chrome上遇到了一些问题。关闭按钮只是让我的Chrome窗口无响应。我还遇到了每个兄弟都说它没有兄弟和消息未被发送的问题。 - Dan Drews

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接