Franz Tesca Franz Tesca - 3 months ago 16
HTML Question

Keep aspect ratio css on absolute position and auto dimensions

I want to keep an aspect ratio on an absolute positioned div with auto dimensions. I tried some solutions from other stackoverflow questions, but they don't work or they need fixed dimensions.

I would like the div to wrap its content while maintaining a 4:3 ratio. I would like to solve it with css and less javascript.

Is there a clever way to do this with css?

My code is below.



$('span').hover(function() {
$(this).find('div').css('visibility', 'visible')
}, function() {
$(this).find('div').css('visibility', '')
})

.jsmbox {
position: absolute;
overflow: hidden;
-webkit-transition: all .3s ease;
transition: all .3s ease;
max-width: 100%;
max-height: 100%;
color: black;
background-color: gold;
padding: 6px;
visibility: hidden;
top: 0;
left: 0;
cursor: default;
text-align: justify;
z-index: 100;
}
.jsmbox:hover {
max-width: 400px;
max-height: 300px;
}
.custom {
width: 320px;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<span style="position: relative">[hover me1]
<div class="jsmbox">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ullamcorper eleifend augue, at eleifend lacus tincidunt sed. Nam et ipsum non dui pellentesque suscipit at finibus metus. Nullam vel augue eros. Sed sodales mi ut sodales posuere. Nullam eleifend nisl vel urna bibendum egestas. Sed eget dolor nunc. Curabitur elementum dolor sit amet enim pellentesque, vitae cursus tellus tempus. Maecenas non cursus nisl, sit amet volutpat nisl. Integer faucibus at nunc eget euismod.
</div>
</span>

<span style="position: relative">[hover me2]
<div class="jsmbox custom">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ullamcorper eleifend augue, at eleifend lacus tincidunt sed. Nam et ipsum non dui pellentesque suscipit at finibus metus. Nullam vel augue eros. Sed sodales mi ut sodales posuere. Nullam eleifend nisl vel urna bibendum egestas. Sed eget dolor nunc. Curabitur elementum dolor sit amet enim pellentesque, vitae cursus tellus tempus. Maecenas non cursus nisl, sit amet volutpat nisl. Integer faucibus at nunc eget euismod.
</div>
</span>

<span style="position: relative">[hover me3]
<div class="jsmbox custom">This text is much shorter!</div>
</span>





The first div (hover me1) is fully auto dimensioned and the text doesn't even fit. [not true! It has max dimensions when it is being hovered over.]


  • The second div (hover me2) has fixed width.

  • Third div (hover me3) also has fixed width, but the text is shorter and so aspect ratio is incorrect and div is too large for its content...


Answer

In answer to this question:

Is there a clever way to do this with css?

I don't know of one.

There's some dumb or inflexible ones:

  • Set width and height on .jsmcon so that all of them have the same dimensions and aspect ratio. [dumb--it looks horrible and it's inflexible.]

  • Do this. [doesn't work for this problem--despite its cleverness, this solution maintains its aspect ratio based on its container, not it's content.]


I decided to approach this problem a little differently than you.

Rather than approximate the necessary width of the box based on the length of its content, I estimate the necessary width based on how much area the text currently takes up.

Instead of:

var l = box.text().length;
l = (l > $(window).width()*0.9) ? $(window).width()*0.9 : l;

I use this:

/*Some math formulae
    (Been a little while since algerbra, so I'm not sure if I "showed my work"
     and "showed the relationships" properly. I guess it doesn't matter since
     it works. :D)
     *****
     Width = Ratio * Height;
     Area = Width * Height;
        Area = (Ratio * Height) * Height;
        Area = Ratio * Height^2;
        Height^2 = Area/Ratio;
        Height = sqrt(Area/Ratio);
     Width = Area / Height;
     */

     //get approximate area needed to display the text
     var tArea = (con.width() * con.height());
     var tRowsPerColumn = ratio; //ratio is global
     var tHeight = Math.sqrt(tArea/tRowsPerColumn);

     var l = tArea/tHeight;
     l = (l > $(window).width()*0.9) ? $(window).width()*0.9 : l;

What's the difference?

The difference between the 'old way' and the 'new way' differs based on browser, font-size, etc.

For illustration purposes, here are the results for 2 browsers with font-size: 17px; in the css and var ratio = 4/3 in the JS.

Vivaldi:

31.25% off vs. 4.76% off.
66.67% off vs. 3.18% off.

Firefox:

81.25% off vs. 28.71% off.
36.36% off vs. 8.25% off.


Notes:

  1. The intended aspect ratio is now found at the top of the javascript.

  2. min-width was removed from .jsmcon. (It throws the aspect ratio off on the little one.)

  3. We have some finangling to do so that we can calculate the area of each .jsmcon.

    • box.css("max-width", "unset"); removes the max-width on .jsmbox so that .jsmcon has a width greater than 0px.

    • box.css("max-width", ""); removes max-width from the inline style of .jsmbox so that the CSS in our stylesheet can take over.)

  4. A better way to calculate the area taken up by the text would involve changing things around, forgetting about the transitions, or making a dummy element to test how much area is taken up by the text.

JSFiddle here.