10

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 loads". 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.

Community
  • 1
  • 1
ggutenberg
  • 6,880
  • 8
  • 39
  • 48

5 Answers5

16

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

Martin Andersson
  • 18,072
  • 9
  • 87
  • 115
josephj
  • 329
  • 2
  • 8
  • Wow perfect, just what I needed and it works in all browsers. Thank you so much. I didn't know about the `about:blank`. David Flanagan talks about this URL in his book "JavaScript - The Definitive Guide", around page 354 (6th edition). One little thing though, IE and FireFox didn't want to put focus on an already opened window. Solved that by adding `newWin.focus()` in the else clause. – Martin Andersson Dec 14 '13 at 17:30
  • Thank you! This code snippet resolved my issue! My scenario is that when spawning a child window via window.open() that contains a long running Javascript then immediately closing that child window causes the parent window to refresh unexpectedly on IE. – Barney Jan 13 '15 at 10:12
  • Yes, it's cool! But child window reloads after reloading parent... or it shouldn't ? (can't compare with example - link not working) – basil Jun 02 '16 at 06:47
1

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)

thejh
  • 44,854
  • 16
  • 96
  • 107
  • 1
    It's not the updateready event I was looking for (I already have a listener for that), but rather the applicationCache.update part of your snippet. Thanks. – ggutenberg Nov 03 '10 at 14:46
1

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.

Aniruddha Shevle
  • 4,602
  • 4
  • 22
  • 36
0

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.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
0

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.

scunliffe
  • 62,582
  • 25
  • 126
  • 161
  • There is no server-side code. It's an HTML5 offline app. 100% client-side. Also, the popup in this case is the "main" application. So closing it at any point would be quite disconcerting to the user. – ggutenberg Nov 02 '10 at 17:38
  • I'm confused... if the popup is the "main" application, why is there an opener window that launches it? – scunliffe Nov 02 '10 at 17:58
  • Because my client wants the main application to "not look like a browser", so it's being opened by the launcher without a toolbar, address bar, status bar, etc. Also, this way the launcher manages the update/download process with progress bar etc., while the user is free to user the main application in a separate window. – ggutenberg Nov 02 '10 at 18:04
  • ok, so the main window just launches the "chromeless" popup window that is the "main" application. I'm not sure I get what is needed from the original window after launch that can't be retrieved from a regular HTTP request or a background AJAX request? – scunliffe Nov 02 '10 at 23:26
  • The original window has the event listeners for the caching process. It displays different status messages, progress bar, etc. depending on the current caching state. It needs a hook into the main application window because some of those messages need to be displayed in that window (application requirement). – ggutenberg Nov 03 '10 at 14:40