How to automatically adjust iframe height for embedded wordpress posts

I’ve been wondering about this for a while. Here’s a proof of concept (note that it doesn’t solve the issue of removing scrollbars from iframes).

Add a script tag to the post that you’re embedding:

<script>
    function sendHeight() {
        const body = document.body,
            html = document.documentElement;

        const height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

        window.parent.postMessage({
            'iframeHeight': height,
            'iframeId': 'zalgFrame' // Use a unique identifier if you have multiple iframes
        }, '*'); // Consider specifying the parent domain for security
    }

    // Send initial height
    window.onload = sendHeight;

    // Optional: Update height on resize or other events
    window.onresize = sendHeight;
</script>

I’m using the identifier "zalgFrame" in the script.

In your Discourse theme:

<script type="text/discourse-plugin" version="1.29.0">
let iframeHeight, iframeId;
window.addEventListener('message', (event) => {
  if (event.origin !== "http://wp-discourse.test") return; // my test domain, update it to your domain or comment out
  // gets the iframe height that's passed from `wp-discourse.test` and confirms that the iframeId matches the iframeID I set there    
  if (event.data.iframeHeight && event.data.iframeId === 'zalgFrame') {
      // visit the Discourse page with the iframe with your console open
      // you should see updated heights being sent from the parent site as you resize the window
      console.log("we got an event:" + event.data.iframeHeight); 
      iframeHeight = event.data.iframeHeight;
      iframeId = event.data.iframeId;
  }
  }, false);
</script>

In a Discourse post:

<div data-iframe-test-one>
<iframe src="http://wp-discourse.test/zalg_iframe/this-is-a-test-this-is-only-a-test/" width="100%" height="1659"></iframe>
</div>

So it’s possible to get the actual height of the rendered iframe from the parent window.

I don’t know how to get the height from the data from the event listener into a call to api.decorateCookedElement though. I’m not certain that would even work to remove the vertical scroll bar from long iframes. If I try hard coding a large height (1600px) into the iframe element, I’m still ending up with a scroll bar.

Edit: for the sake of completeness:

<script type="text/discourse-plugin" version="1.29.0">
api.decorateCookedElement(
  (e) => {
    let iframeHeight, iframeId;

    function handleMessage(event) {
      if (event.origin !== "http://wp-discourse.test") return;
      if (event.data.iframeHeight && event.data.iframeId === "zalgFrame") {
        iframeHeight = event.data.iframeHeight;
        iframeId = event.data.iframeId;
        // based on the assumption that there will only be 1 iframe wrapped in the data-zalgFram div
        let iframe = e.querySelector("[data-zalgFrame] iframe");
        if (iframe) {
          iframe.style.height = `${iframeHeight}px`;
        }
        // after setting the actual rendered height of the iframe
        // remove the event listener
        window.removeEventListener("message", handleMessage, false);
      }
    }
    window.addEventListener("message", handleMessage, false);
  },
  { id: "component-id", onlyStream: true }
);
</script>

For anything over ~1000px in height, there doesn’t seem to be any way of avoiding a scroll bar being added by Discourse, so I’m not recommending the approach.

I think the answer to the OP is that it’s kind of possible, but probably doesn’t accomplish much. (Except that I learned about the window.postMessage() method :slight_smile:

2 Likes