开发者

Retrieve child window reference after refresh

I have an HTML5 offline app (i.e. there's no server-side component/code).

It basically has two windows (parent and child). However, in some instances I need to programmatically refresh the parent window. When this happens, it loses its reference to the child, and the child's reference to window.opener is no longer valid. So my thinking was that I'd serialize the child window and store that in localStorage. Then when the parent refreshed it could pick up the window reference from localStorage and still interact with the child.

The problem is that this doesn't work (as per my previous question here Stringify DOMWindow object). I can't serialize a DOM window like any other object.

So how can I have my newly refreshed window pick up a reference to its predecessor's child?

Edit: Stressed the fact that it's an offline app. There's no server-side component.

I should also add that the reason I need to refresh the parent is to check for application updates (changes in the cache manifest). Since the parent is the first page in the application that loads, it basically manages the caching (in fact as an aside, in Safari if this window is closed during any of the caching process the entire browser crashes). So "parent" is essentially "first page that the user l开发者_如何学运维oads". This means that I can't have the "parent" in a frame, as the topmost window would then manage the caching, and require the refresh in order to look for updates. Actually, it appears that I can use the frame method, as refreshing any of the pages in the application will trigger the update check. Cumbersome though.


You can obtain the reference of child window simply by following trick.

newWin = window.open("", "child_window_name", "width=...");
if (newWin.location.href === "about:blank") {
    newWin = window.open("a.html", "child_window_name", "width=...");
} else {
    // We've already obtained the reference.
    // However, IE and FireFox won't put focus on an already opened window.
    // So we have to do that explicitly:
    newWin.focus();
}

Note you must have a fixed child window name to make this trick works.

Example URL: http://josephj.com/lab/2011/window-open-reconnect/demo.html


See here: http://www.w3.org/TR/html5/offline.html#dom-appcache-update

applicationCache.addEventListener("updateready", function() {
  applicationCache.swapCache();
  //now reload the body html
}, false);
setInterval(60000, applicationCache.update);//check for updates every minute

(updateready fires every time a new version has been downloaded)


I achieved this scenario using Window.postMessage, which enables cross-origin communication between Window objects. It supports: https://caniuse.com/#feat=mdn-api_window_postmessage

Basic working example for establishing communication between parent and child Window objects

In the parent app,

var childWindow = window.open(CHILD_URL);
setTimeout(() => { // postMessage on some browsers may need setTimout
  childWindow.postMessage( // STEP 1
     "INITIATE_CONNECTION", // message
     CHILD_URL // targetOrigin
  );
}, 500)

function receiveMessageFromChild(event) {
  if (event.origin === CHILD_URL) { // we should always check with the event.origin
    if (event.source) {
      if (event.data === "CONNECTION_SUCCESSFUL") {
        setTimeout(() => {
            event.source.postMessage( // STEP 3 and STEP 7
                "SEND_SOME_DATA_TO_CHILD",
                CHILD_URL);
        }, 500);
      }
      // other messages handling from child
    }
  }
}

if (window.addEventListener) {
  window.addEventListener("message", receiveMessageFromChild, false);
} else if (window.attachEvent) { // for IE
  window.attachEvent("onmessage", receiveMessageFromChild);
}

In the child app,

function receiveMessageFromParent(event) {
  if (event.origin === PARENT_URL) { // we should always check with the event.origin
    var parentWindow = window.opener || event.source;
    if (parentWindow) {
      if (event.data === "INITIATE_CONNECTION" || event.data === "RE_NITIATE_CONNECTION") { // "RE_NITIATE_CONNECTION" msg from parent when parent is refreshed
         setTimeout(() => {
            parentWindow.postMessage( // STEP 2
                "CONNECTION_SUCCESSFUL",
                PARENT_URL);
         }, 500);
      }
      // other messages handling from parent
    }
  }
}

if (window.addEventListener) {
  window.addEventListener("message", receiveMessageFromParent, false);
} else if (window.attachEvent) { // for IE
  window.attachEvent("onmessage", receiveMessageFromParent);
}

To handle parent window refresh and retrieve child window reference after the parent refresh make the following changes in the above codes,

In the parent app,

document.body.onunload = () => { // before refreshing parent, this function will be called
  if (childWindow) {
    setTimeout(() => {
      childWindow.postMessage( // STEP 4
        "PARENT_REFRESHING",
        CHILD_URL
      );
    }, 500);
  }
};

function receiveMessageFromChild(event) {
  // ... above mentioned code in the parent app for this function
  .
  .
  .
  // other messages handling from child
  if (event.data === "PARENT_REFRESHED") {
    setTimeout(() => {
      event.source.postMessage( // STEP 6 - this event.source is a child window reference
        "RE_NITIATE_CONNECTION",
        CHILD_URL
      );
    }, 500);
  }
}

In the child app,

function receiveMessageFromParent(event) {
  // ... above mentioned code in the child app for this function
  .
  .
  .
  // other messages handling from parent
  if (event.data === "PARENT_REFRESHING") {
    setTimeout(() => {
      event.source.postMessage( // STEP 5
        "PARENT_REFRESHED",
        CHILD_URL
      );
    }, 500);
  }
}

In the above example, please refer to the STEPs from 1 to 7. STEP 6 is the child window reference when the parent is refreshed.


Sounds like a pretty complex solution. Why don't you put the content of the parent window in an invisible iframe (i.e. one without a border) and just refresh the frame?

That would allow you to save the reference to the frame and child window in the top document where both could access it.


Can you call something on your opener that tells it to reload and reopen the popup to a page?

e.g.

//in popup
window.opener.doReload('popup_page_name.html');
window.close();

//in opener
function doReload(popupURL){
  setSomeFormField(popupURL);
  someForm.submit();
}

then in your server-side code, if you had the popup set, add the code to re-launch it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜