Search code examples
javascriptpopupwindow

Why does JavaScript "window.postMessage" create duplicate messages?


I'm trying to open a popup window, do some stuff, and send a message back to the opening window so I can do some more stuff with the data it sends.

Essentially, I'm adapting the process outlined here.

This is my code on the "opening window". It runs when a social connect button is clicked. The code opens a popup window and assigns a listener event on the opening window to receive a message from the popup:

//Do the operation
let windowObjectReference = null;
let previousUrl = null;

const openSignInWindow = (url, name) => {

    // remove any existing event listeners
    window.removeEventListener('message', receiveMessage);

    // window features
    const strWindowFeatures =
        'toolbar=no, menubar=no, width=600, height=700, top=100, left=100';

    if (windowObjectReference === null || windowObjectReference.closed) {
        /* if the pointer to the window object in memory does not exist
         or if such pointer exists but the window was closed */
        windowObjectReference = window.open(url, name, strWindowFeatures);
    } else if (previousUrl !== url) {
        /* if the resource to load is different,
         then we load it in the already opened secondary window and then
         we bring such window back on top/in front of its parent window. */
        windowObjectReference = window.open(url, name, strWindowFeatures);
        windowObjectReference.focus();
    } else {
        /* else the window reference must exist and the window
         is not closed; therefore, we can bring it back on top of any other
         window with the focus() method. There would be no need to re-create
         the window or to reload the referenced resource. */
        windowObjectReference.focus();
    }

    // add the listener for receiving a message from the popup
    window.addEventListener('message', event => receiveMessage(event), false);

    // assign the previous URL
    previousUrl = url;

};


const receiveMessage = event => {
    // Do we trust the sender of this message? (might be different from what we originally opened, for example).
    if (event.origin !== websiteHomeUrlNoSlash) {
        return;
    }

    const { data } = event;
    console.log(data); //<--- THIS WHERE I'm SEEING DUPLICATES

};

//Invoke the function
openSignInWindow(url, name);

In the popup users login to their social account and then get redirected to a page on my app where the below code is run. The code posts a message back to the opening window and then closes the popup:

// Get the message data
const messageObj = {
    pluginReason: pluginReasonVar,
    displayName: displayNameVar,
    provider: providerVar,
};

if (window.opener) {
    // send them to the opening window
    window.opener.postMessage(messageObj, websiteHomeUrlNoSlash);
    // close the popup
    if (closePopup) {
        window.close();
    }
}

Everything almost works as expected. Users can login to their social accounts and all the redirections and opening and closing of the popup works fine.

The Problem:

If users go through the Social Connect process multiple times without refreshing the page, then the message data that is printed to the console is duplicated more and more each run.

For example:

  • On the 1st run console.log(data) is printed once. So far this works as expected.
  • On the 2nd run console.log(data) prints twice. It should only be printed once.
  • On the 3rd run console.log(data) prints three times. It should only be printed once.

Each time the Social Connect process is run it should only print once. But somehow it's adding a duplicate copy on each subsequent run.

This duplication keeps growing until the users refreshes the page, which starts the count back at one.

I want to do more data manipulation at the point of the console.log(data) but I can't do that while it's creating duplicates copies on each subsequent run.

How do I stop that from happening?

Maybe it's because the listener event is not detaching? If so, how do I fix that?


Solution

  • You have created an anonymous method (event) => { } as a wrapper and attached it to the addEventListener method.

    window.addEventListener('message', event => receiveMessage(event), false);
    

    It can't be removed by

    window.removeEventListener('message', receiveMessage);
    

    To fix it, make changes like this:

    window.addEventListener('message', receiveMessage, false);
    

    Meanwhile, if the method receiveMessage gets lost every time the window has been closed, it's better to move the removeEventListener part inside the receiveMessage.

    const receiveMessage = (event)=> {
      window.removeEventListener('message', receiveMessage);
      // do something else
    }