2015/07/19

Creating a responsive Div with proportional dimensions

When you create a responsive layout, you might need sometimes to embed an element (classic example: a YouTube iframe) in a way that it scales down for smaller screens, keeping the aspect ratio.

The way to do that is to wrap that element in a parent div, and control the dimensions of that Div:
<div class="responsive-wrapper"> 
  <iframe></iframe> 
</div>
In the CSS you can then use something like this:
.responsive-wrapper {
    width:100%;
    padding-bottom:37.5%;
    position:relative;
}

.responsive-wrapper iframe {
    position:absolute;
    left:0;
    top:0;
    width:100%;
    height:100%;
}
The trick is that the .responsive-wrapper is a 0 height div, and it has only a padding-bottom defined as a percentage so that means that its total height is really based on the width of its parent.

http://jsfiddle.net/fsmnwefn/

Ok, we all have seen that trick, but: what happens when want to limit the width of the embedded element?
If your page has enough width and the embedded element takes 100% width it might look too big, so you might want to limit its width to 800px for example.

Well, the solution is not really complex after thinking a little: just wrap everything in another div with max-width, that way the total width that .responsive-wrapper will be always limited to our maximum and the height (given by padding-bottom) won't grow as we resize the page.

http://jsfiddle.net/fsmnwefn/2/

Anyway, defining the padding as a percentage ( = desiredHeight * 100 /desiredWidth) doesn't look nice to me, I wanted to be able to use only defined widths and heights.

What I want is the intrinsic behavior that img has, so that we can define width and height as well as max-width, max-height.

There's a solution for this using vw units, that way the element will keep the aspect ratio, but it's gonna be messy again to keep a maximum width that works correctly in mobile: if you work only with the .responsive-wrapper and try to set max-width:800px you can add now max-height:300px for example, but if you have set width:100vw then the element will create an horizontal scroll as soon as you keep some margin on its sides.

So you still need to keep that extra wrapper div.

But there's another interesting solution, why don't we use an image to give us that ratio automatically? This idea is explained on StackOverflow by Elliot Richerson 

It's good to have different options and I find this one interesting, you just need to add an image with the aspect ratio that you want as the first element of .responsive-wrapper and with width:100%, height:auto and then you can apply the max-width/height to .responsive-wrapper itself.

Obviously this still forces us to add an extra element and now you have an extra http request to download that image, that you have to recreate for every aspect-ratio that you need.

And then I thought: Would it be possible to create such image on the fly?
At the moment I'm working with javascript (not only static html), so my crazy mind starts to think: I can use a data-url, and even I can try to find out what's the basic structure of a .gif and create a fake one with the dimensions that I want, then base64 encode it and use it as the src of the image.

Wait a minute man! that's too much work, we're in 2015, you can just create a canvas, set it to the desired size and get its content as data-url without the need to find out how to create a .gif or .png

But, but...
Yes, I can use a canvas instead!

so the new trick is to use a <canvas height="300" width="800"> and then in the CSS
.responsive-wrapper {
    width:100%;
    max-width:800px;
    position:relative;
}

.responsive-wrapper iframe {
    position:absolute;
    left:0;
    top:0;
    width:100%;
    height:100%;
}

.responsive-wrapper canvas{
    width:100%;
    height:auto;
} 
The nice part of this solution is that you can use whatever units you want, and the aspect ratio is defined in the HTML, so you can use this class with different elements and even add other media queries for whatever other effect you might need.

http://jsfiddle.net/fsmnwefn/3/

 

No comments: