Skip to content

Latest commit

 

History

History
297 lines (220 loc) · 10.1 KB

File metadata and controls

297 lines (220 loc) · 10.1 KB

Client Functions

This part of the library helps you to communicate with helper page iframes. It comes with methods to alter the iframe's content and receive size updates.

Table of contents

Glossary

Term Explanation
Host message A message sent from the host page to the helper page iframe.
Iframe message A message sent from the helper page iframe to the host.

Usage

Altering iframe content

Attention: The helper page html generated by the server side code comes with some head tags (title, meta) and a JS script tag in the body.

All "set content" commands will be handled by the helper page in such a way, that all tags that the helper came with are preserved. When you send a "set content" command again, the head or body will be reset to the set of tags the helper page was delivered with.

You must send "set content" commands after the iframe has loaded.

Set head content

You can use the method sendSetHeadContentMessage to append content to the iframe's <head> tag.

import {sendSetHeadContentMessage} from "@perspective-software/cross-origin-html-embed"

const iframe = document.querySelector("iframe#html-embed")

iframe.onload = () => {
    sendSetHeadContentMessage(
        iframe,
        `
        <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
        <link rel="stylesheet" href="https://necolas.github.io/normalize.css/latest/normalize.css">
        `
    );
}

iframe.src = "https://sandbox.myservice.com"

Set body content

You can use the method sendSetBodyContentMessage to append content to the iframe's <body> tag.

import {sendSetBodyContentMessage} from "@perspective-software/cross-origin-html-embed"

const iframe = document.querySelector("iframe#html-embed")

iframe.onload = () => {
    sendSetBodyContentMessage(
        iframe,
        `
        <h1>Pickle Rick</h1>
        <script>
            console.log("Pickles are delicious. But not, if they could be Rick.");
        </script>
        `
    );
}

iframe.src = "https://sandbox.myservice.com"

Options

The two set content methods outlined above share the same options.

The first argument is necessary to send the message to the correct destination. You need to pass the iframe element.

Internally the methods obtain the targetOrigin used for the postMessage communication from the iframe src.

You can override the targetOrigin via the third optional options parameters.

Method parameters:

Parameter Required Description
iframe yes Iframe to sent message to.
content yes HTML to be appended to the iframe's <head>.
options Optional options object. You can pass target origins explicitly via { targetOrigins: string[] }.

Sending arbitrary host messages

Theoretically you can also use the underlying method sendHostMessage to send messages to the helper page iframe.

This method is used by the "send (head|body) content" methods explained above.

We won't go into more details here, as there are the abstraction methods. In case you still want to use sendHostMessage, the TypeScript types and comments should answer all your questions.

Receiving iframe updates

We advise you to call "receive" methods once the iframe has loaded/has a src. This is important for automatically obtaining the origin to listen for messages from.

Dimensions updates

Use the method receiveIframeDimensionsUpdates to listen for any dimensions updates the helper page iframe sends to the host.

This allows you to size the iframe element so that the complete content is shown.

import {receiveIframeDimensionsUpdates} from "@perspective-software/cross-origin-html-embed"

const iframe = document.querySelector("iframe#html-embed")

iframe.onload = () => {
    receiveIframeDimensionsUpdates(iframe, (message) => {
        iframe.style.height = `${message.data.documentElementHeight}px`;
    })
}

iframe.src = "https://sandbox.myservice.com"

Remove listener

You can also unregister underlying message event listeners. The method returns a function. When you call the function, the message reception is stopped.

import {receiveIframeDimensionsUpdates} from "@perspective-software/cross-origin-html-embed"

// ...

let stopDimensionUpdatesReception: (() => void) | null = null;

iframe.onload = () => {
    stopDimensionUpdatesReception = receiveIframeDimensionsUpdates(iframe, (message) => {
        iframe.style.height = `${message.data.documentElementHeight}px`;
    })
}

// ...

// Embed teardown
stopDimensionUpdatesReception?.();
iframe.remove();

Options

Parameter Required Description
originOrIframe yes Used for listening to messages of the correct origin. Iframe, or explicit targetOrigin(s) as string or Array of strings. If you pass an iframe, the library checks that event.source === iframe.contentWindow. You can change this via the options.
callback yes Callback to be executed on message reception.
options You can change the iframeEmitterCheck mode. The mode strictSourceCheck (default) checks that event.source === iframe.contentWindow. The mode sourceOrOriginCheck additionally checks if event.origin matches the iframe's src.

Size and set content commands

It might be necessary to reset the iframe height to 0px when you set new body content.

Otherwise, you might have too much whitespace. Why? Because the new content may be smaller than the previous content and thus doesn't cause any iframe content size changes. This way your host does not get the new actual content size.

Receiving arbitrary iframe messages

Similar to the send methods, you can theoretically also use the underlying base method receiveIframeMessages.

Currently, it does not make any difference, as there is only one type of iframe messages.

We advise you to use the method receiveIframeDimensionsUpdates as it provides more precise types.

Examples

Vanilla JS

Check out also the vanilla JS example directory that is connected to the backend examples: Vanilla JS Code Example 🔗

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Vanilla JS example</title>
</head>
<bod>
    <textarea id="custom-html"></textarea>

    <iframe id="custom-html-sandbox"></iframe>
</bod>
</html>

Your JavaScript/TypeScript:

import {receiveIframeDimensionsUpdates, sendSetBodyContentMessage} from "@perspective-software/cross-origin-html-embed"

const textarea = document.querySelector("textarea#custom-html");
const iframe = document.querySelector("iframe#custom-html-sandbox");

iframe.onload = () => {
    receiveIframeDimensionsUpdates(iframe, (message) => {
        iframe.style.height = `${message.data.documentElementHeight}px`;
    })

    textarea.addEventListener("change", (event) => {
        sendSetBodyContentMessage(iframe, event.target.value);
    })
}

iframe.src = "https://sandbox.myservice.com"

React

Check out also the React example that is connected to the backend examples: React Code Example 🔗

import { useEffect, useRef, useState } from "react";
import {
  receiveIframeDimensionsUpdates,
  sendSetBodyContentMessage,
} from "@perspective-software/cross-origin-html-embed";

export default function HtmlSandboxEmbed({ html }: { html: string }) {
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
  const [iframeLoaded, setIframeLoaded] = useState(false);
  const [iframeHeight, setIframeHeight] = useState(0);

  useEffect(() => {
    if (iframeRef.current) {
      return receiveIframeDimensionsUpdates(iframeRef.current, ({ data }) => {
        setIframeHeight(data.documentElementHeight);
      });
    }
  }, []);

  useEffect(() => {
    if (iframeRef.current && iframeLoaded) {
      sendSetBodyContentMessage(iframeRef.current, html);
    }
  }, [html, iframeLoaded]);

  return (
    <iframe
      id="custom-html-sandbox"
      src="http://localhost:4042"
      ref={iframeRef}
      onLoad={() => {
        setIframeLoaded(true);
      }}
      style={{ height: `${iframeHeight}px` }}
    />
  );
}