iframe-resizer icon indicating copy to clipboard operation
iframe-resizer copied to clipboard

single page app inside as the iframe height not working

Open fitprotracker opened this issue 3 years ago • 4 comments

When using the settings:

iFrameResize({ checkOrigin: false, log: true, heightCalculationMethod: 'lowestElement' }, ".xyz-form");

any value used for heightCalculationMethod does not seem to work when using a spa app. is this possible to use with spa apps?

thanks.

fitprotracker avatar Sep 06 '22 00:09 fitprotracker

Do you mean your main app is a single page app and you are trying to use an iframe, or you are loading a single page app into the iframe?

If the first case, I'm using this library in my single page app (Svelte) so it definitely can work, but I don't see why the 2nd case wouldn't work either.

Remember that the iframe itself needs to use iframeResizer.contentWindow.min.js in order for the library to work. You can copy that file from the /node_modules/iframe-resizer/js folder to somewhere the iframe can access it. This file would not be bundled with your main single page app. This is needed because the library uses postMessage between the main window and the iframe to handle the resizing.

As an example - this is my IFrame Svelte component I created which takes a plain text or html string and displays it in an iframe using iframe-resizer. Hope this helps get you on the right track.

Using Svelte 3.50.1 and iframe-resizer 4.3.2.

IFrame.svelte

<script>
  import { createEventDispatcher, onDestroy } from "svelte";
  import { iframeResize } from "iframe-resizer";

  export let srcdoc;
  export let title;
  export let isText = false;
  export const functions = {
    getIframeElem: () => {
      return elem;
    },
    isLoaded: () => {
      return iframeLoaded;
    },
  };

  const dispatch = createEventDispatcher();

  let elem;
  let iframeLoaded = false;

  function handleIFrameLoaded(e) {
    let doc = elem.contentDocument;
    let docBody = elem.contentDocument.body;

    // Set styles
    docBody.style.margin = "0";
    docBody.style.fontFamily = 'Roboto';

    if (isText) {
      docBody.style.whiteSpace = "pre-wrap";
    } else {
      // Set all anchor tags to open in new window
      let anchorTags = docBody.querySelectorAll("a");
      for (let i = 0; i < anchorTags.length; ++i) {
        anchorTags[i].setAttribute("target", "_blank");
      }
    }

    // Add iframeResizer.contentWindow.min.js script to iframe
    let scriptElem = doc.createElement("script");
    scriptElem.type = "text/javascript";
    scriptElem.src = "/vendor/iframe-resizer/iframeResizer.contentWindow.min.js";
    docBody.appendChild(scriptElem);

    // Start iFrameResize
    let thisDomain = window.location.protocol + "//" + window.location.hostname;

    if (window.location.port != "") {
      thisDomain += ":" + window.location.port;
    }

    iframeResize(
      {
        checkOrigin: [thisDomain],
        heightCalculationMethod: "lowestElement",
        onInit: handleIFrameResizeReady,
      },
      elem
    );
  }

  onDestroy(function () {
    // Cleanup iFrameResize
    if (elem.iFrameResizer) {
      elem.iFrameResizer.removeListeners();
    }
  });

  function handleIFrameResizeReady(e) {
    iframeLoaded = true;
    elem.style.display = "";
    elem.iFrameResizer.resize();
    dispatch("load");
  }

  function escapeHtml(html) {
    let temp = document.createElement('div');
    temp.innerText = html;
    return temp.innerHTML || '';
  }
</script>

{#if !iframeLoaded && srcdoc !== undefined && srcdoc !== null}
  <div class="p-6 text-center flex items-center justify-center">
    <div class="spinner" />
    <h3 class="mt-2 text-sm font-medium text-gray-600">Loading content...</h3>
  </div>
{/if}
<iframe
  {title}
  srcdoc={isText ? escapeHtml(srcdoc) : srcdoc}
  on:load={handleIFrameLoaded}
  bind:this={elem}
  style="width: 1px; min-width: 100%; height: 0; display: none"
  scrolling="no" />

And used like so:

MyPage.svelte

<script>
  import IFrame from "./IFrame.svelte";

  let iframeFunctions;
  let htmlContent = '<div style="height: 400px; border: 1px solid black; background-color: yellow;">This content appears inside the iframe</div>';

  function handleIframeLoaded() {
     console.log('iframe loaded, the iframe elem:', iframeFunctions.getIframeElem());
  }
</script>

<IFrame
  title="Content"
  bind:functions={iframeFunctions}
  srcdoc={htmlContent}
  on:load={handleIframeLoaded} />

Reikooters avatar Sep 21 '22 09:09 Reikooters

@fitprotracker I was having similar issues and I found that having height: 100% css on the body tag in the iframe screwed up the height calculation. I created a div inside the body with the 100% height then mounted my SPA within that. Once I did that I put iframe-resizer back to the default calculation method and everything worked out fine.

<!DOCTYPE html>
<html lang="en" class="h-full bg-white">
    <head>
        ...
    </head>
    <body class="text-black">
        <div class="h-full" id="app">
              // mount SPA app here
        </div>
    </body>
</html>

tinyfly avatar Aug 03 '23 20:08 tinyfly

dose any one find solution to this issue ?

Ahmed-Elkharadly avatar Nov 12 '23 14:11 Ahmed-Elkharadly

Just found out that iFrameResizer has problems if the iFrame is reducing his size. Expanding works fine.

Let's take a pagination as an example. If the first page has 12 items and the second page only the remaining 2 items the iFrame should be less high. In my tests the document object of the iFrame was at least the last height set of his DOM iFrame element.

The solution is to use heightCalculationMethod: "taggedElement" and implement at the lowest possible point <div data-iframe-height></div>

In case of my Nuxt app:

<template>
  <v-app>
    <v-main>
      <v-container fluid>
        <slot />
      </v-container>
      <!-- 
           IMPORTANT iframesizer hack. Needs to be inside v-main and not v-app.
           V-app will adapt to iFrame height. heightCalculationMethod: "taggedElement" 
      -->
      <div data-iframe-height></div>
    </v-main>
  </v-app>
</template>

Hope this helps and saves someone hours of debugging😊

crazymind avatar Jan 09 '24 16:01 crazymind

Downsizing issues are caused by element in the iframe having percentage heights on them.

davidjbradshaw avatar Feb 13 '24 14:02 davidjbradshaw