Responsive iframe with fixed height
I tried multiple ways to use CSS for this embeded player but it looks like it has to be JS with a resize
listener and absolute
positionning that does the job
Codepen here to load the video: https://codepen.io/savageGoat/pen/oNoBbMz
Full code:
const vid = document.querySelector('.video');
const container = document.querySelector('.video-wrapper');
const vidWidth = vid.offsetWidth;
const vidHeight = vid.offsetHeight;
const applyWidthHeight = () => {
const vidRatio = vidHeight / vidWidth;
const parentHeight = container.offsetHeight;
vid.height = parseInt(parentHeight) + 'px';
vid.width = parseInt(parentHeight*vidRatio) + 'px';
vid.style.height = parseInt(parentHeight) + 'px';
vid.style.width = parseInt(parentHeight*vidRatio) + 'px';
}
window.addEventListener('resize', applyWidthHeight);
document.addEventListener('DOMContentLoaded', applyWidthHeight);
.container {
background-color: green;
max-width: 1200px;
min-width: 700px;
height: 700px;
}
.video-wrapper {
background-color: red;
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.video {
height: 100%;
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
}
<div class="container">
<div class="video-wrapper">
<iframe class="video" src="https://player.vimeo.com/video/82481183?background=1" frameborder="0" allow="autoplay" ></iframe>
</div>
</div>
How to make an iframe responsive without aspect ratio assumption?
It is not possible to interact with a different origin iFrame using Javascript so to get the size of it; the only way to do it is by using window.postMessage
with the targetOrigin
set to your domain or the wildchar *
from iFrame source. You can proxy the contents of the different origin sites and use srcdoc
, but that is considered a hack and it won't work with SPAs and many other more dynamic pages.
Same origin iFrame size
Suppose we have two same origin iFrames, one of short height and fixed width:
<!-- iframe-short.html -->
<head>
<style type="text/css">
html, body { margin: 0 }
body {
width: 300px;
}
</style>
</head>
<body>
<div>This is an iFrame</div>
<span id="val">(val)</span>
</body>
and a long height iFrame:
<!-- iframe-long.html -->
<head>
<style type="text/css">
html, body { margin: 0 }
#expander {
height: 1200px;
}
</style>
</head>
<body>
<div>This is a long height iFrame Start</div>
<span id="val">(val)</span>
<div id="expander"></div>
<div>This is a long height iFrame End</div>
<span id="val">(val)</span>
</body>
We can get iFrame size on load
event using iframe.contentWindow.document
that we'll send to the parent window using postMessage
:
<div>
<iframe id="iframe-local" src="iframe-short.html"></iframe>
</div>
<div>
<iframe id="iframe-long" src="iframe-long.html"></iframe>
</div>
<script>
function iframeLoad() {
window.top.postMessage({
iframeWidth: this.contentWindow.document.body.scrollWidth,
iframeHeight: this.contentWindow.document.body.scrollHeight,
params: {
id: this.getAttribute('id')
}
});
}
window.addEventListener('message', ({
data: {
iframeWidth,
iframeHeight,
params: {
id
} = {}
}
}) => {
// We add 6 pixels because we have "border-width: 3px" for all the iframes
if (iframeWidth) {
document.getElementById(id).style.width = `${iframeWidth + 6}px`;
}
if (iframeHeight) {
document.getElementById(id).style.height = `${iframeHeight + 6}px`;
}
}, false);
document.getElementById('iframe-local').addEventListener('load', iframeLoad);
document.getElementById('iframe-long').addEventListener('load', iframeLoad);
</script>
We'll get proper width and height for both iFrames; you can check it online here and see the screenshot here.
Different origin iFrame size hack (not recomended)
The method described here is a hack and it should be used if it's absolutely necessary and there is no other way around; it won't work for most dynamic generated pages and SPAs. The method fetches the page HTML source code using a proxy to bypass CORS policy (cors-anywhere
is an easy way to create a simple CORS proxy server and it has an online demo https://cors-anywhere.herokuapp.com
) it then injects JS code to that HTML to use postMessage
and send the size of the iFrame to the parent document. It even handles iFrame resize
(combined with iFrame width: 100%
) event and posts the iFrame size back to the parent.
patchIframeHtml
:
A function to patch the iFrame HTML code and inject custom Javascript that will use postMessage
to send the iFrame size to the parent on load
and on resize
. If there is a value for the origin
parameter, then an HTML <base/>
element will be prepended to the head using that origin URL, thus, HTML URIs like /some/resource/file.ext
will get fetched properly by the origin URL inside the iFrame.
function patchIframeHtml(html, origin, params = {}) {
// Create a DOM parser
const parser = new DOMParser();
// Create a document parsing the HTML as "text/html"
const doc = parser.parseFromString(html, 'text/html');
// Create the script element that will be injected to the iFrame
const script = doc.createElement('script');
// Set the script code
script.textContent = `
window.addEventListener('load', () => {
// Set iFrame document "height: auto" and "overlow-y: auto",
// so to get auto height. We set "overlow-y: auto" for demontration
// and in usage it should be "overlow-y: hidden"
document.body.style.height = 'auto';
document.body.style.overflowY = 'auto';
poseResizeMessage();
});
window.addEventListener('resize', poseResizeMessage);
function poseResizeMessage() {
window.top.postMessage({
// iframeWidth: document.body.scrollWidth,
iframeHeight: document.body.scrollHeight,
// pass the params as encoded URI JSON string
// and decode them back inside iFrame
params: JSON.parse(decodeURIComponent('${encodeURIComponent(JSON.stringify(params))}'))
}, '*');
}
`;
// Append the custom script element to the iFrame body
doc.body.appendChild(script);
// If we have an origin URL,
// create a base tag using that origin
// and prepend it to the head
if (origin) {
const base = doc.createElement('base');
base.setAttribute('href', origin);
doc.head.prepend(base);
}
// Return the document altered HTML that contains the injected script
return doc.documentElement.outerHTML;
}
getIframeHtml
:
A function to get a page HTML bypassing the CORS using a proxy if useProxy
param is set. There can be additional parameters that will be passed to the postMessage
when sending size data.
function getIframeHtml(url, useProxy = false, params = {}) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
// If we use a proxy,
// set the origin so it will be placed on a base tag inside iFrame head
let origin = useProxy && (new URL(url)).origin;
const patchedHtml = patchIframeHtml(xhr.responseText, origin, params);
resolve(patchedHtml);
}
}
// Use cors-anywhere proxy if useProxy is set
xhr.open('GET', useProxy ? `https://cors-anywhere.herokuapp.com/${url}` : url, true);
xhr.send();
});
}
The message event handler function is exactly the same as in "Same origin iFrame size".
We can now load a cross origin domain inside an iFrame with our custom JS code injected:
<!-- It's important that the iFrame must have a 100% width
for the resize event to work -->
<iframe id="iframe-cross" style="width: 100%"></iframe>
<script>
window.addEventListener('DOMContentLoaded', async () => {
const crossDomainHtml = await getIframeHtml(
'https://en.wikipedia.org/wiki/HTML', true /* useProxy */, { id: 'iframe-cross' }
);
// We use srcdoc attribute to set the iFrame HTML instead of a src URL
document.getElementById('iframe-cross').setAttribute('srcdoc', crossDomainHtml);
});
</script>
And we'll get the iFrame to size to it's contents full height without any vertical scrolling even using overflow-y: auto
for the iFrame body (it should be overflow-y: hidden
so we don't get scrollbar flickering on resize).
You can check it online here.
Again to notice that this is a hack and it should be avoided; we cannot access Cross-Origin iFrame document nor inject any kind of things.
How do I make iframes responsive without using div?
If you can use viewport units that is doable without an extra wrapper element.
Full page width:
iframe { width: 100vw; height: 56.25vw; /*16:9*/}
<iframe width="560" height="315" src="https://www.youtube.com/embed/I4YoBuJCbfo" frameborder="0" allowfullscreen></iframe>
making an iframe responsive that is inside a div
Make iframe responsive for all screen
This will work for you sure.
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item" src="https://www.youtube.com/embed/PZEwPJzMKGg"></iframe>
</div>
Refrence Link
How to make an iframe responsive for mobile?
I figured it out... I did not end up having to add any custom CSS in order to solve the problem. I simply needed to change the iframe widths from 1000px to 100%.
Related Topics
Header and Footer in Each Page in Print Mode With CSS
How to Keep Two Divs on the Same Line
How to Make a Div Expand Vertically to Wrap the Content Within It
Download Attribute in <A> Tag Doesn't Work When File Url Called from Sub Domain
How to Make Bootstrap 4 Columns Have a Height of 100%
How to Use HTML to Print Header and Footer on Every Printed Page of a Document
How to Set the Height of Vuetify Card
Use CSS to Automatically Add 'Required Field' Asterisk to Form Inputs
Angular Material Mat-Drawer in Full Height Flex, Content Overflow Auto
Make a Scrollbar Always Visible in a Div - Chrome
Spring Boot + Thymeleaf CSS Is Not Applied to Template
Css @Media Print Issues With Background-Color;
Make Nested Div Stretch to 100% of Remaining Container Div Height
Fixed Position Div Inside Div Container
Should I Use Max-Device-Width or Max-Width
How to Combine Class and Id in CSS Selector
How to Select an Element Based on the State of Another Element in the Page With Css