skip to Main Content

I have one app ( let’s call it A ) that incorporatesan app B

I would like to send data from B ( child ) to A ( parent )

I can use this code

window.parent.postMessage(value, '*');

And then in parent

window.addEventListener("message", onMessage, false);        

My issue is that the parent has multiple event listeners.

I don’t want to edit all of them to filter the events ( exclude from other the B postMessage event domain )

Is there a solution to attache the message event listner to only an iframe ?

2

Answers


  1. The trick is to just have one event listener on the page, that can then route messages via a pubsub library on your page to your different components.

    Login or Signup to reply.
  2. The best here is to use "private" MessageChannel instances. This way you can listen on the global event only until you get your own MessagePort, and then remove your listener and use that received port only for your own use, without worrying about tripping potential other listeners or being tripped by others.

    You still need a global "initiator" message though, and that one might trip other listeners in case they don’t all use that strategy. That would occur for a single message though. To avoid being too noisy you can prevent further bubbling when handling it, but unfortunately since the event fires on the Window object directly we can’t really capture it1.. So if you have a way of adding it before all the others that would be best.

    In frame:

    const { port1, port2 } = new MessageChannel();
    parent.postMessage(some_key_to_id_ourselves, main_page_origin, [port2]);
    port1.onmessage = (evt) => {
     // only the messages we're expecting to receive, no noise.
    };
    

    And in the main page:

    const controller = new AbortController();
    addEventListener("message", (evt) => {
      const { data, ports, origin } = evt;
      if (origin === frame_origin && data === expected_key) {
        ports[0].onmessage = (evt) => {
          // only the messages we're expecting to receive, no noise.
        };
        controller.abort(); // no need to listen for main's messages anymore
        evt.stopImmediatePropagation(); // don't trigger other listeners
      }
    }, { signal: controller.signal, capture: true /* works in FF */ });
    

    1. Or at least it’s how it’s supposed to work, Firefox is happy reordering capturing listeners in front of bubbling ones…

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search