crevulus
crevulus

Reputation: 2418

iFrame has fixed width of 300px, without any styling defining 300px

I'm using the iframe-resizer package to dynamically change the size of an iframe depending on content.

However, before even trying any dynamic resizing, I run into an issue with the basic iframe: it's always 300px in width. Whatever content I place inside the iframe, the width is always 300px. Will not move; overflow is hidden if I add something >300px and still takes up 300px if my content is smaller than that.

EDIT: To be clear, My problem is that I want to dynamically change the width of an iframe (cross-domain), but it always renders at 300px and will not change. I'm using the iframe-resizer package to successfully dynamically change the height, but that's nto working on the width.

In the parent site, I'm embedding the iframe as follows:

<script type="text/javascript"
    src="https://cdn.jsdelivr.net/npm/[email protected]/js/iframeResizer.min.js">
</script>
<script type="text/javascript">
    var i = new URLSearchParams(window.location.search).get("openWidget"); document.write('<iframe id="inlineFrameExample" title="Inline Frame Example" style="position: fixed; zIndex: 1000; bottom: 0; right: 0" frameBorder="0" scrolling="no" src="http://localhost:3001?openWidget=' + i + '"></iframe>');
    window.iFrameResize({ log: true }, '#inlineFrameExample')
</script>

And the content in my for my framed site, I have the following elements and styling:

    <div className="App" id="App2">
      {openWidget && <div className="button">OK</div>}
      {showMessage && <section>This is a message</section>}
      <div>
        <button className="button" onClick={() => setShowMessage(!showMessage)}>
          Msg
        </button>
        <button className="button" onClick={() => setOpenWidget(!openWidget)}>
          {openWidget ? "-" : "+"}
        </button>
      </div>
    </div>

.App {
  text-align: center;
  float: right;
}

.container {
  display: flex;
  justify-content: flex-end;
  float: right;
}

section {
  background-color: mediumseagreen;
  color: white;
  width: 500px;
}

.button {
  width: 100px;
  height: 100px;
  background-color: mediumseagreen;
  color: white;
  text-align: center;
  float: right;
}

Note that there's nothing in there about width being 300px. There are other widths, but the iframe seems to ignore them and always set itself to 300px.

I also made two code sandboxes, and embedded one site in the other via an iframe. Unfortunately the iframe is hidden for some reason, but if you inspect and look for the iFrame with id="inlineFrameExample" (and not code sandbox's button iframe) you'll see it's 300px wide: https://exzts.csb.app/ The code for the parent site is here: https://codesandbox.io/s/eloquent-flower-exzts?file=/index.html The code for the framed site is here: https://codesandbox.io/s/iframe-test-ss8fs

UPDATE: I also removed all css styling from both the parent site and the framed site. Still always stuck at 300px. I also viewed the CSS specs which says default will be default set to 300px under certain conditions, but I cannot figure out how to dissatisfy those conditions so that the 300px rule doesn't apply.

I want the iframe to resize based on width. The current set up works perfectly on height, and I understand there's something in the default specs for iframes that sets them to 300px if x conditions are met: w3.org/TR/CSS2/visudet.html#inline-replaced-width What I need is whatever styling/setting/attribute will disable this, so the width will act in the same way as the height (i.e. completely dynamic).

Upvotes: 4

Views: 2467

Answers (4)

Lajos Arpad
Lajos Arpad

Reputation: 76426

The good news

If the page loaded by the iframe is on the same domain as yours, then you can easily find out the initial state by defining an onload attribute to the iframe and then access the contentWindow of the iframe and do some measurements and then apply them on your main page. But, as you said, the content of the iframe is from another origin. Bad luck, read on.

Some hope

Well, we know that you cannot directly access the content of the iframe. But, the content of the iframe may let you know when certain events happen. If the content inside the iframe does send messages, then you can addEventListener for message type events and specify the origin to be the site you are using inside the iframe. More info here: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

So, the site inside the iframe does some messaging? Maybe yes. But probably not. If it does not message you at the time of events of interest, then read on.

The bad news

Modern browsers do not allow you to directly run Javascript at other origins due to security reasons. One obvious example for this is that cookies can be read for a page and you absolutely do not want some third-party sites to mess with your cookies while you are doing a banking transactions or something of the like.

Some glimmer of hope

You may be able to somehow determine the current state of the iframes content. For example you may have access to API functions or something of the like that you can ask about your status. If those are leaking direct information about your situation inside the iframe (was the button clicked already, for instance?), then you may be able to poll such a source and see whenever a size changing event happens and if so your code could adapt your view accordingly. This way you would use another source of information to find out what is happening inside the iframe.

Sad but true

In general, the answer to your main question is NO. The explanation is that it is not allowed due to security reasons.

You may have some workarounds in your specific situation that may work out for you, but such workarounds are rarely available, most websites are working without providing supports for other websites that would like to use them in iframes. If the workarounds are not available for you, then you may want to reach out to the owner of the website shown in your iframe and see whether he/she is okay with providing support for this. Otherwise you will have to accept that the situation is beyond repair. Is it? If you are not satisfied yet, read on.

Desperation

If you really, really need to resolve this, then here's what you can do:

  • create a page in the same domain as your main page
  • from the browser's point of view both your main page and the page inside the iframe are using the very same domain, so they can reach each-other via Javascript
  • this new page will request the site to be shown in the iframe where the page of the other domain was shown previously (you don't create a further nested iframe, you just change its src)
  • and it will display it
  • but you add a little bit of Javascript, doing some measurements
  • and of course you adjust resource URLs accordingly, changing all relative URLs into absolute URLs

This might work, but it's very cumbersome and you will always need to adjust the given page whenever the site you are to use changes.

Upvotes: 0

the Hutt
the Hutt

Reputation: 18398

The iframe-resizer doesn't resize iframe width by default. You have to explicitly specify it using sizeWidth setting.

window.iFrameResize({
  log: false,
  sizeWidth: true,
  checkOrigin: false,
  widthCalculationMethod: "rightMostElement",
  heightCalculationMethod: "lowestElement"
}, "#myFrame");
body { background-color: wheat;}
iframe { border: 3px dashed green; }
span { font-size: 1.5rem;}
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/js/iframeResizer.min.js"></script>

<div>Following iframe grows automatically!</div>
<iframe id="myFrame" scrolling="no" src="https://mvyom.csb.app/"></iframe>
<span>😵😜🤪</span>

The iframe content is animated so it affects iframe's width and height according to the specified width and height calculation methods. And the iframe-resizer updates dimensions on every animations start and end event.
Also, the iframe source is from codesandbox and the parent document is this StackOverflow answer page, so we need to set checkOrigin: false as well.

Note: Had to set log: false because the animations are happening very fast creating ton of logs in console.


You can provide your own size calculations form inside the iframe.ref:

    <script>
      window.iFrameResizer = {
        widthCalculationMethod: function () {
          return document.querySelector(".one").clientWidth;
        }
      };
    </script>

In following demo the iframe resizes exactly to the width of green box.

window.iFrameResize({
  log: false,
  sizeWidth: true,
  checkOrigin: false,
  widthCalculationMethod: "rightMostElement",
  heightCalculationMethod: "lowestElement"
}, "#myFrame");
body { background-color: wheat;}
iframe { border: 3px dashed green; }
span { font-size: 1.5rem;}
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/js/iframeResizer.min.js"></script>

<div>Following iframe grows automatically!</div>
<iframe id="myFrame" scrolling="no" src="https://mvyom.csb.app/custom.html"></iframe>
<span>😵😜🤪</span>

Refer implementation of custom.html for better understanding. You can implement similar method for heightCalculationMethod.

Upvotes: 3

Juliy V. Chirkov
Juliy V. Chirkov

Reputation: 183

@crevulus, the inner content of iframe has no impact on the dimensions of it's parent iframe by design

Also, as far as I remember, 300x150px is the common default width and height applied by browsers to any «iframe» element faced in html code on parsing incoming html and building the DOM stages, when the dimentions have been not predefined explicitely

The thing is a browser requires the dimentions of any visible element (i.e. any element that takes space in page layout - for simplicity, let's say any element except those where «display» property has been set to «none») either to be strictly predeclared with «width» and «height» properties (via element attributes with the same names, via internal / external css rule or css rule applied to element directly thru «style» attribute - the way doesn't matter) or to be calculable thru initial (default) values defined in html / css standards (again, for simplicity, if «div» element has no dimentions set, but all the neighbours do, its width will be calculated by browser as all available horizontal space between the left and right neighbours of the element, i.e. the «div» will always have initial width, defined by standard, which equals 100%; similary, the height of this «div» will be calculated as sum of heights of all its internal elements) - the dimentions are strictly required because w/o concrete values for every visible element on page a browser won't be able to render the page content for the current viewport correctly

So, when a browser parses incoming html code and faces «iframe» elements with no predefined dimentions (via attributes, css et cetera), to allocate the space for the element in page layout and thus to be able to move on to parse / render the whole page, a browser assumes the dimentions of the «iframe» as common defaults, which, as I mentioned above, equals square figure 300x150px («recumbent skyscraper»)

To override this behaviour, you should predeclare in any convenient way initial values, which will be suitable for your case


<iframe id="frame1" width="100%" height="250px" />

or

<style>
#frame1 {
  width: 100%;
  height: 250px;
}

</style>
<iframe id="frame1" />

et cetera, and then, when a page is rendered, if needed, use JavaScript to tune the dimensions of this iframe accoring to the width and height of its content (or any other condition on your choice), like

var frame1 = document.getElementById('frame1')
var width1 = parseFloat(frame1.style.getPropertyValue('width')) | 0
var height1 = parseFloat(frame1.style.getPropertyValue('height')) | 0

/*
 * some stuff to define the conditions for iframe dimentions tuning and 
 * to calculate new width and height and to assing calculated values
 * to width1 and height1 variables respectively
 */

frame1.style.setProperty('width', width1 + 'px')
frame1.style.setProperty('height', height1 + 'px')

As a postscript I'd like to note the above is the working concept, 'cause a few month ago I've designed and developed the close scheme to force the iframe of TinyMCE editor, built in Magento 1.9.x admin backend, to keep editor's width always equal to the width of CMS page on site's frontend and to expand / shrink editor's height dynamically accoring to the height of CMS page content being edited

The goal was to simplify and optimise the working process for the content department by making CMS pages in Magento admin area always look in TinyMCE editor exactly the same as on site's frontend

Unfortunately, the whole 1st gen of Magento, up to the last 1.9.4.5 release, is supplied with unspeakable outdated release of TinyMCE editor - 3rd gen of TinyMCE, published in early 2014 even before html5 officially arrived, in comparison even with it's own current 5th gen (which, in turn, frankly, is much inferior to alternative modern lightweight apps of WYSIWYG class) for 2022 feels like the Stone Age, a clumsy craft developed with stone axe by cave dwellers, both in terms of stability and functionality

In other words, that TinyMCE is missing the lion's share of useful builtin APIs / plugings, including the modern one to maintain the editor's dimentions in nominally simplified way, so the only way to achieve the goal I've researched has been to utilize onInit and onChange events provided by TinyMCE3, and to maintain the whole routine with custom JavaScript handlers over the editor, applying the dimentions directly to iframes on the parent level in Magento admin page code, which Magento core utilizes as containers for TinyMCE3 instances

Anyway, the experiment has been successful, so please feel free to ask any additional practical questions or to request some tech details on routines

Upvotes: 0

T-S
T-S

Reputation: 747

Not sure if I understand the question. I think you've made an App and a page. On the page you want to use an iframe to show your App and the iframe should be scaled by default to the size of the app. Did I get that right?

I think JavaScript might by your solution. Get the content from the iframe, check in the content for a class App en that's the width you want to use for your iframe.

const iframe = document.querySelector("#inlineFrameExample");
const iWindow = iframe.contentWindow;
const iDocument = iWindow.document;
const iElement = iDocument.querySelector(".App");

iframe.style.width = iElement.style.width;

A problem with this script is that it can only be executed after the content from the iframe was loaded. So make sure you wait for that with a timer,delay, promise, or something. If you don't want to show the resizing to your visitors make sure the iframe has visibilty: hidden and add visibilty: visible of the iframe to the end of the script above.

Upvotes: 0

Related Questions