The humble <iframe>
is a useful way of presenting content from another web page, but can be quite difficult to make work seamlessly. This short article presents a way of making them a little easier to integrate into a responsive design.
Setting up an iframe
Adding some nested content is as simple as adding an <iframe>
tag with a src
attribute pointing at the content to include.
<iframe src="/nested-content.html"></iframe>
Out of the box, this leaves a little to be desired - notably the height and width take no notice of the size of the host page or the nested content!
The width is fairly easily fixed by setting the width of the iframe element to 100%. The quickest way to do this is with a default size.
iframe { width: 100%; }
How tall is this iframe?
The height is a slightly different matter. While it is possible to set height the same way (or via an attribute on the iframe
), there's the question of how large that should be. Ideally we'd set this to the height of the iframe
content, but there's no foolproof way of knowing this. For a simple nested page it's fairly easy.
#nested-content { height: 5.5rem; }
This looks much better! There's a problem though: this is a fixed height, so the content will not respond to changes in screen size or content in the nested page. If the content is longer than the size we set, we end up with a scroll bar in the iframe
, which may not be what we want.
Making the height responsive
The good news is, we can achieve this dynamic resizing using a trivial amount of JavaScript! Because of limitations introduced by CORS (Cross-Origin Resource Sharing) to prevent attacks, we have to use the postMessage()
method to safely share information between the parent and nested page.
The basic flow is to calculate the size of the nested page using the getBoundingClientRect()
method of the top-level element of the nested page. This is accessible using document.documentElement
. We then message the parent page with the new height. An event listener makes this reactive to changes in height of the page.
function sendResizeMessage() {
const height = document.documentElement
.getBoundingClientRect().height;
parent.postMessage({ height }, parent.location);
}
sendResizeMessage();
addEventListener('resize', sendResizeMessage);
All we need to do now is listen for this message in the parent page, extract the height and set the iframe size accordingly.
const iframe = document.getElementById('iframe-id');
addEventListener('message', (event) => {
const height = event.data.height;
iframe.style.height = height + 'px';
});
Like good citizens, these are both loaded in DOMContentLoaded
handlers.
addEventListener(
'DOMContentLoaded',
<initialisation code here!>
);
Making it a bit more secure
Although the code we're running is fairly harmless, there is a possibility that someone could message us and set the height of the iframe. The postMessage()
documentation suggests confirming the sender's identity by checking the event source and origin.
The updated message listener now looks like this:
const iframe = document.getElementById('iframe-id');
addEventListener('message', (event) => {
if(!(
event.source === iframe.contentWindow &&
event.origin === 'https://example.com'
)) return;
const height = event.data.height;
iframe.style.height = height + 'px';
});
All we've done here is to test that
- the event source is the iframe content window
- the event origin is the expected url
Note that this is negated so if it doesn't match, the function returns early.
I've got a slightly more fully worked out example in the iframe-resizer repo on GitHub, and you can see it in action.
I hope this has been helpful, and thanks for reading!
Top comments (0)