Responsively change div size keeping aspect ratio [duplicate]
Asked Answered
P

5

226

When I give an image a percent width or height only it will grow/shrink keeping its aspect ratio, but if I want the same effect with another element, is it possible at all to tie the width and the height together using percentage?

Pennell answered 25/8, 2012 at 10:13 Comment(0)
F
544

You can do this using pure CSS; no JavaScript needed. This utilizes the (somewhat counterintuitive) fact that padding-top percentages are relative to the containing block's width. Here's an example:

.wrapper {
  width: 50%;
  /* whatever width you want */
  display: inline-block;
  position: relative;
}
.wrapper:after {
  padding-top: 56.25%;
  /* 16:9 ratio */
  display: block;
  content: '';
}
.main {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  /* fill parent */
  background-color: deepskyblue;
  /* let's see it! */
  color: white;
}
<div class="wrapper">
  <div class="main">
    This is your div with the specified aspect ratio.
  </div>
</div>
Fein answered 25/8, 2012 at 10:47 Comment(18)
i have to say this is awesome, and it even work on IE. big +1Contrapose
It surely is, never would have though of that :) but why did you delete your jQuery answer? it could be useful tooPennell
@Abody97 why would the padding-top be related to the width of the element?Pennell
@IlyaD Take a look at this from the CSS2 specification: little link.Fein
Thanks! Is it possible to make it vice versa: let the width depend on the height?Footplate
You, sir, have won the Internet. This is huge, especially for background images on responsive designs. Thank you!Instructor
For the mathematically impaired like me: to calculate the padding-top percentage of something else than 16:9 ratios, use this formula (for example using a 22:5 ratio where 22 is A and 5 is B): B / (A / 100) = C%. So 22:5 is 5 / .22 = 22.72%.Butterball
To those, like me, who hate the fiddles of CSS and JavaScript, this is - I believe - the best solution you'll find (and I've just wasted a morning looking), including within ASP.NET MVC. Many thanks.Reinertson
This isn't exact. It's actually off by 50-200 pixels for those looking for an EXACT aspect ratio.Industrialize
@bryan: Good luck finding anything that isn't an approximation.Stodge
@Chris, may I ask you to have a look at a responsive design related question here : tinyurl.com/nadfh2u ?Giblet
If html had been designed to only display squares, there wouldn't be web developers, but everyone at home would know the few commands to color and size a square. I don't know why everyone assumes an "element" is just a colored square. In this case, there can't be content in there with all the padding feeling the box. An extra abs-pos element could not contain text normally since it would overflow once it reaches the end. In other words, it won' grow with it. I won't vote it up because it annoys me that I can't even vote it down (since everyone else seem to like colorful squares)Backus
Amazing solution to the problem that works brilliantly, wish i could upvote moreAsset
How to set vertically center <div class="main"> from body ?Prent
What if I don't want an absolute position?Bogart
Just FYI: Bootstrap 3.2+ uses this approach with classes embed-responsive (wrapper in this answer) and embed-responsive-item (main here) it has 2 additional classes like embed-responsive-16by9 and embed-responsive-4by3 which has to be added to the wrapper div to determine the required proportions.Mcentire
Is the display:inline-block necessary?Charlena
The only thing I would change here is the "display: block;" on the .wrapper class.. The reason is that inline-block elements produce a few pixel of blank (vertical) space after the element.Owe
K
46

Bumming off Chris's idea, another option is to use pseudo elements so you don't need to use an absolutely positioned internal element.

<style>
.square {
    /* width within the parent.
       can be any percentage. */
    width: 100%;
}
.square:before {
    content: "";
    float: left;

    /* essentially the aspect ratio. 100% means the
       div will remain 100% as tall as it is wide, or
       square in other words.  */
    padding-bottom: 100%;
}
/* this is a clearfix. you can use whatever
   clearfix you usually use, add
   overflow:hidden to the parent element,
   or simply float the parent container. */
.square:after {
    content: "";
    display: table;
    clear: both;
}
</style>
<div class="square">
  <h1>Square</h1>
  <p>This div will maintain its aspect ratio.</p>
</div>

I've put together a demo here: http://codepen.io/tcmulder/pen/iqnDr


EDIT:

Now, bumming off of Isaac's idea, it's easier in modern browsers to simply use vw units to force aspect ratio (although I wouldn't also use vh as he does or the aspect ratio will change based on window height).

So, this simplifies things:

<style>
.square {
    /* width within the parent (could use vw instead of course) */
    width: 50%;
    /* set aspect ratio */
    height: 50vw;
}
</style>
<div class="square">
  <h1>Square</h1>
  <p>This div will maintain its aspect ratio.</p>
</div>

I've put together a modified demo here: https://codepen.io/tcmulder/pen/MdojRG?editors=1100

You could also set max-height, max-width, and/or min-height, min-width if you don't want it to grow ridiculously big or small, since it's based on the browser's width now and not the container and will grow/shrink indefinitely.

Note you can also scale the content inside the element if you set the font size to a vw measurement and all the innards to em measurements, and here's a demo for that: https://codepen.io/tcmulder/pen/VBJqLV?editors=1100

Kazbek answered 12/6, 2014 at 2:7 Comment(5)
This is fantastic since you don't need a wrapping element. Great work!Attu
How can I stop the child content from expanding the height of the parent?Dalt
I was not able to get this shorter method working in IE10Quintuplicate
Yeah this doesn't work if the child can expand the parent.Lewin
Instead of using the clearfix hack you can use display: flow-root or contain: content on the .square element: codepen.io/fcalderan/pen/xxRPwgLHying
L
31
<style>
#aspectRatio
{
  position:fixed;
  left:0px;
  top:0px;
  width:60vw;
  height:40vw;
  border:1px solid;
  font-size:10vw;
}
</style>
<body>
<div id="aspectRatio">Aspect Ratio?</div>
</body>

The key thing to note here is vw = viewport width, and vh = viewport height

Lister answered 15/5, 2014 at 8:37 Comment(3)
Just a note that if the screen's ratio is different, the element's ratio will be different, losing the aspect ratio.Andra
@RyanKilleen except it looks like he is using "vw" for both width and height. So the percentage will still have the same aspect ratio.Cromorne
@Cromorne .... that's what I get for skimming. You're correct.Andra
O
3

That's my solution

<div class="main" style="width: 100%;">
    <div class="container">
        <div class="sizing"></div>
        <div class="content"></div>
    </div>
</div>

.main {
    width: 100%;
}
.container {
    width: 30%;
    float: right;
    position: relative;
}
.sizing {
    width: 100%;
    padding-bottom: 50%;
    visibility: hidden;
}
.content {
    width: 100%;
    height: 100%;
    background-color: red;
    position: absolute;
    margin-top: -50%;
}

http://jsfiddle.net/aG4Fs/3/

Objurgate answered 5/11, 2013 at 11:25 Comment(0)
C
2
(function( $ ) {
  $.fn.keepRatio = function(which) {
      var $this = $(this);
      var w = $this.width();
      var h = $this.height();
      var ratio = w/h;
      $(window).resize(function() {
          switch(which) {
              case 'width':
                  var nh = $this.width() / ratio;
                  $this.css('height', nh + 'px');
                  break;
              case 'height':
                  var nw = $this.height() * ratio;
                  $this.css('width', nw + 'px');
                  break;
          }
      });

  }
})( jQuery );      

$(document).ready(function(){
    $('#foo').keepRatio('width');
});

Working example: http://jsfiddle.net/QtftX/1/

Contrapose answered 25/8, 2012 at 10:19 Comment(4)
This is great! I am using this script, however, if I use it on multiple DIV's and they each have different heights...once the script kicks in, it adjusts all heights to be the same...I cannot figure out why! Any ideas? I'd like to maintain the aspect ratio like this script does, but maintain the different heights for each.Alienism
Because the function applied for a set of elements. The script should handle each element separately. Here is the updated code: jsfiddle.net/QtftX/100Duane
If you refresh the page the width and height resets again.. How can you block this?Unburden
This doesn't work on iOSIndustrialize

© 2022 - 2024 — McMap. All rights reserved.