In the vue component below sending a message to an iframe
using the iframe.contentWindow.postMessage(...)
call has no effect.
The component waits for a message from the iframe and sends a response message to the iframe in the mesageHandler(...)
. Within the messageHandler(...)
there are two ways to send the message to the iframe:
- Calling
sendResponse1(...)
works as expected - Calling
sendResponse2()
has no effect.
The component has also to send the message in the onBeforeUnmount(...)
lifecycle hook. There calling sendResponse1(...)
is not an option because there is no event
present that has to be passed as argument.
The origin of the iframe is not the same as the origin of the embedding component. The content at the origin is not under our control.
The problem is not browser specific. It persists in Edge and Chrome.
Vue Component
<template>
<div ref="elm" />
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
const props = defineProps<{
src: string;
}>();
const elm = ref<HTMLElement | null>(null);
const box = ref<HTMLElement | null>(null);
const iframe = ref<HTMLIFrameElement | null>(null);
onMounted(() => {
if (!elm.value) {
return;
}
box.value = document.createElement('div');
iframe.value = document.createElement('iframe');
iframe.value.src = props.src;
box.value.appendChild(iframe.value);
elm.value.appendChild(box.value);
window.addEventListener('message', messageHandler, { passive: true });
});
onBeforeUnmount(() => {
iframe?.value?.remove?.();
box?.value?.remove?.();
window.removeEventListener('message', messageHandler);
// sendResponse1(...) <-- cannot call from here because there is no event
sendResponse2(); // NOK does not work
});
function messageHandler(event: MessageEvent) {
if (!iframe.value || event.origin !== new URL(iframe.value.src).origin) {
return;
}
// Call 1 OK: message sent
sendResponse1(event);
// Call 2 NOK: message not sent
sendResponse2();
}
function sendResponse1(event: MessageEvent) {
event.source?.postMessage({ message: 'Response' }, <any>event.origin);
}
function sendResponse2() {
iframe.value?.contentWindow?.postMessage(
'Response',
new URL(iframe.value.src).origin,
);
}
</script>
<style scoped>
/* Some stylesheets */
</style>