Setting an iframe's height to change automatically when a new page is loaded?

Hi there,

I have a quiz that I am adding as an iframe. The quiz has different steps/questions that appear on different pages which have different heights.

Is there a way to automatically change the height of the iframe when a page loads inside it?

Currently I have this which sets the starting page’s height when the main page loads:

  // Selecting the iframe element
    var iframe = document.getElementById("anxiety");
    
    // Adjusting the iframe height onload event
    iframe.onload = function(){
        iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 'px';
    }

But is there a way to change the height when each page is loaded inside the iframe?

Thanks!

Hi @toolman, you might include a script in each iframe page that would send a message to the parent window on load; for example:

window.addEventListener('load', () => {
  window.parent.postMessage({
    height: document.body.scrollHeight
  }, window.location.origin)
})

And on your main page:

const iframe = document.getElementById('my-iframe')

window.addEventListener('message', ({ origin, data }) => {
  if (origin === window.location.origin && data.height) {
    iframe.height = data.height
  }
})

Also if you don’t mind using 3rd party libraries, you might check out the iframe resizer for a more elaborate and battle-tested solution.

Thanks for the reply.

I have tried the above code and it seems to set the starting iframe page’s height, but the next page doesn’t seem to change from the initial size.

I am also getting this error:

test.html:229 Uncaught ReferenceError: resizeIframe is not defined
at HTMLIFrameElement.onload (test.html:229)

Do you know what this could be?

Hi @m3g4p0p

I’m not expert in JS, but I don’t understand…

  1. Why window.location.origin, not window.parent.location.origin in window.parent.postMessage(...)?

  2. What means ({ origin, data })?

Did you include the first snippet on that page as well?

Well you haven’t mentioned such a function or variable yet… where would that supposed to be defined? What does that script around line 229 look like?

Hey @igor_g, this is because we want to ensure that the receiver has the same origin as the sender; passing the parent.location.origin would kind of defeat this purpose. (In principle, at least – accessing the parent from within an iframe is only possible if both sides have the same origin anyway.)

This is called a destructuring assignment; it’s equivalent to

window.addEventListener('message', event => {
  const origin = event.origin
  const data = event.data
  // ...
})
1 Like

I have another one question. Do you know about resources loading in JS? How it should work? For example could I get the URL of current JS-script within this script? I mean something like…

function Foo()
{
    console.log(this.location) // print out '/js/library/tools/foo.js'
}

Hm… you mean something like this? It boils down to querying for all scripts present on the page, where the last script is the one currently being executed:

// Assuming we have 3 scripts foo.js, bar.js and baz.js
// included in that order, the next line would yield:
// [<script src="foo.js">] for foo.js
// [<script src="foo.js">, <script src="bar.js">] for bar.js
// [<script src="foo.js">, <script src="bar.js">, <script src="baz.js">] for baz.js
const scripts = document.querySelectorAll('script')
const srcURL = new URL(scripts[scripts.length - 1].src)

console.log(srcURL.pathname)

This would only work when run immediately though, not asynchronously at some later point when further scripts might have been added to the DOM. Also I’m not sure how robust this solution is with regard to deferred scripts, say.

I didn’t knew about class URL in JS, thanks.

Yes and no. Is there the way to get script source without DOM-model? Because, as you said…

Tricky. :-) Off the top of my head I’d think we might install a service worker that keeps track of the scripts being loaded… but then again we’d still need some sort of ID to get the request details for a given script, so we might just as well assign a DOM ID from the first:

<script src="foo.js" id="foo-script">
console.log(document.getElementById('foo-script').src)
</script>

Either way, the script would need to know its ID… I don’t think there’s a completely generic solution to this, but I’ll give it some thought tomorrow or so (I’m leaving the house right now).

Could service worker recognize loaded script without ID?

Yes, if script has ID, this solving the problem… But it is not internal JS-library solution. Developer should obligatory set ID to some script, and if not, this causes error… Not really elegant.

Turns out there’s actually document.currentScript, which seems to do the same as the above snippet with the same limitations regarding callbacks. I don’t see an immediate problem here though – just store a reference at the top of your script and you can then use it anywhere:

const { currentScript } = document

window.addEventListener('load', () => {
  const srcURL = new URL(currentScript.src)
  console.log(srcURL.pathname)
})

And when using modules there’s import.meta, which can indeed be accessed at any point:

<script type="module">
window.addEventListener('load', () => {
  const srcURL = new URL(import.meta.url)
  console.log(srcURL.pathname)
})
</script>
1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.