Capture global window object of subwindow accounting for refresh

Viewed 89

Hi friendly folks of the internet, quick scenario: I've got an application that uses subwindows for tooling, opened with window.open('/tools', '_blank'). It returns a window object that I can attach event listeners to, but when the user hits the refresh button, or the window performs a redirect (which mine does, because it's got a search function), the window object is destroyed, or at least I can't use it to get the current window. So my application thinks the window is closed, even though it isn't.

This becomes an issue when I need to use the window objects to exchange messages between the main application and the window. If the user has changed the window object, this won't work anymore. I'm just wondering if there's a better way to do this.

Obviously, I could have prompts built into the application with HTML, but I'm trying to avoid that for cleanliness reasons, and so the user can use their operating system's windowing system to make using the application easier.

In terms of code examples, there's not much to show, I have two scripts, one on the main, and one on the tool window,

main

$("#btn").on('change', function() {
  const windowRef: Window | null = window.open('/tools', '_blank');
  if (windowRef)
    $(windowRef).on('load', function() {
      windowRef.connect = function() {
        // Initialise tools and functions here
      };
      $(windowRef).on('beforeunload', () => $("#btn").prop('checked', false));
    });
});

tool window

...
<body>
  <script defer>
    new Promise((ok, err) => 'connect' in window ? ok(window.connect()) : err());
  </script>
</body>
1 Answers

The issue is not, that the connection to the sub window is destroyed, since this doesn't happen on a refresh or redirect. You can check this via windowRef.closed. My guess is, that your initialization function contains logic that has to be reapplied after a refresh/redirect. With the following code:

windowRef.connect = function() {
   // Initialise tools and functions here
};

you defined a global function for the newly opened window, which subsequently is called in the new window. When a page is refreshed/redirected, all temporary changes done by javascript are lost. This is true for windowRef.connect, but also for $(windowRef).on('load'; ...):

$("#btn").on('change', function() {
  const windowRef: Window | null = window.open('/tools', '_blank');
  if (windowRef)
    // The registration of this listener is lost when the subwindow is refreshed
    $(windowRef).on('load', function() {
      windowRef.connect = function() {
        // Initialise tools and functions here
      };
      $(windowRef).on('beforeunload', () => $("#btn").prop('checked', false));
    });
});

This means when the subwindow is refreshed, the following script from the subwindow:

...
<body>
  <script defer>
    new Promise((ok, err) => 'connect' in window ? ok(window.connect()) : err());
  </script>
</body>

will never have a function window.connect, since the listener is not called again. You can solve this initialization issue with window.opener:

Main window:

let windowRef: Window | null;
$("#btn").on('change', function() {
  windowRef = window.open('/tools', '_blank');
});
// has to be a global function accessible via window.initialize
function initialize() {
  // Initialise tools and functions here
  $(windowRef).on('beforeunload', () => $("#btn").prop('checked', false));
}

Tool window:

// on load in the tool window will be called after redirect/refresh
$(window).on("load", () => {
  if(window.opener) {
    // Get main window, which opened the sub window and call initialize.
    window.opener.initialize();
  }
})
Related