infodev
infodev

Reputation: 5235

postMessage event to a specific event listener

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 ?

Upvotes: 0

Views: 41

Answers (2)

Kaiido
Kaiido

Reputation: 136568

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

Upvotes: 0

David Bradshaw
David Bradshaw

Reputation: 13077

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.

Upvotes: 0

Related Questions