0

I have .container > .wrapper > img. Here is a task:

  1. img must have maximum width/height possible, keeping it's aspect ratio, being 100% visible and not exceeding the .container's size.
  2. Image must not exceed it's natural size.
  3. Width/height of the image are not known.
  4. .container is known to be of a fixed (in pixels) width/height, but exact dimensions are not known.
  5. .wrapper must tightly fit the img (must have same width and height as the image). Wrapper is a special element to put content over the image, e. g. badges. I added example labels to the snippet to demonstrate this. This should somehow be possible.

Markup:

<div class="container">
  <div class="wrapper">
    <div class="label">new!</div>
    <img src="https://via.placeholder.com/150x300">
  </div>
</div>

I thought I can use display: block; max-height: 100% for the img, but it does not work, because .wrapper (image parent) height is not fixed, and it can't be fixed - see point 5.

What else can I do to achieve described task with pure CSS? I'd prefer solution that works in IE11, but others will be also appreciated.

EDIT: It is really important that container and image can be of any size. I added settings to the snippet for tests on different sizes.

If an image is larger than container, it should render not larger than container.

If an image is smaller than container, it should render not larger than is's natural size.

It should work with horizontal container/vertical image AND vertical container/horizontal image.

EDIT 2: It is also really important that .wrapper is not just a "nasty interfering" element. It is functional: wrapper is used to place absolute positioned content over the image inside it (e.g. labels, badges), it must support transforms (mirror, translate), css filters etc, generally speaking - all the stuff we usually do with block elements.

Playground:

$(function() {
  $('input[name=container-width]').on('change', function() {
    $('.container').css('width', $(this).val() + 'px')
  })

  $('input[name=container-height]').on('change', function() {
    $('.container').css('height', $(this).val() + 'px')
  })

  $('input[name=image-width]').on('change', function() {
    var width = $(this).val()
    var height = $('input[name=image-height]').val()
    $('img')[0].src = 'http://via.placeholder.com/' + width + 'x' + height
  })
  
  $('input[name=image-height]').on('change', function() {
    var height = $(this).val()
    var width = $('input[name=image-width]').val()
    $('img')[0].src = 'http://via.placeholder.com/' + width + 'x' + height
  })

})
.container {
  width: 200px; /* can have any value in pixels */
  height: 200px; /* can have any value in pixels */
  background-color: green;
}

.wrapper {
  outline: 1px solid yellow;
  position: relative; /* for label */
}

.label {
  position: absolute;
  background-color: blue;
  color: white;
}

.label.-top-left {
  top: 10px;
  left: 10px;
}

.label.-bottom-right {
  bottom: 10px;
  right: 10px;
}
<h2>Settings</h2>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Container: width <input type="number" step="10" name="container-width" value="200"> height <input type="number" step="10" name="container-height" value="200">
<br>
Image: width <input type="number" step="10" name="image-width" value="150"> height <input type="number" step="10" name="image-height" value="300">
<br>
<br>

<h2>Demo</h2> 

<div class="container">
  <div class="wrapper">
    <div class="label -top-left">new!</div>
    <div class="label -bottom-right">good!</div>
    <img src="http://via.placeholder.com/150x300">
  </div>
</div>

<br>
<br>
<br>
<br>
<br>
<br>
<br>

<h2>How it should look like</h2>

<p>This is not the solution, because is has many hardcoded dimensions. It's just a visual demo of what I want to achieve.</p>

<div style="width: 200px; height: 200px;background-color: green">
  <div style="width: 100px; height: 200px; position: relative;outline: 1px solid yellow">
    <div style="position: absolute;
  top: 10px;
  left: 10px;
  background-color: blue;
  color: white;">new!</div>
      <div style="position: absolute;
  bottom: 10px;
  right: 10px;
  background-color: blue;
  color: white;">good!</div>
    <img style="max-width: 100%; max-height: 100%;" src="http://via.placeholder.com/150x300">
  </div>
</div>
cronfy
  • 1,001
  • 1
  • 15
  • 32

5 Answers5

1

Does this work for you?

.container {
  width: 200px;
  /* can have any value in pixels */
  height: 200px;
  /* can have any value in pixels */
  background-color: green;
}

.container-2 {
  width: 300px;
  height: 300px;
}

.wrapper {
  outline: 1px solid yellow;
  display: inline;
  height: inherit;
}

img {
  width: auto;
  max-height: 100%;
  display: inline-block;
  vertical-align: bottom;
}
<div class="container">
  <div class="wrapper">
    <img src="http://via.placeholder.com/150x300">
  </div>
</div>
<br>
<div class="container container-2">
  <div class="wrapper">
    <img src="http://via.placeholder.com/30">
  </div>
</div>
sol
  • 22,311
  • 6
  • 42
  • 59
  • No. If container-2 is 300x300 and image is 30x30, image will be much larger than it's natural size. – cronfy May 11 '18 at 06:58
  • @cronfy I replaced `height` with `max-height` - I think this is what you want? Also updated the `display` property of `.wrapper` – sol May 11 '18 at 08:13
  • It's pretty close. I need to do some tests. – cronfy May 11 '18 at 09:11
  • unfortunately, no :( If I place absolutely positioned element with `top: 0;` inside the .wrapper, it does not go to the top of the wrapper. I updated the post snippet to clarify this. I need wrapper to not just sit there - it's a functional element that must support things that we usually do with block elements - absolute positioning inside it, transforms etc. – cronfy May 11 '18 at 09:46
1

check out below code this should work for you you need to apply max-width: 100% and max-height:100% to your img and display: inline; to your img parent div

.container {
  width: 200px; /* can have any value in pixels */
  height: 200px; /* can have any value in pixels */
  background-color: green;
}

.wrapper {
  outline: 1px solid yellow;
  display: inline;
}
.wrapper img{
  max-width: 100%;
  max-height: 100%;
  height: auto;
  vertical-align: top;
}
<div class="container">
  <div class="wrapper">
    <img src="http://placehold.it/150x300">
  </div>
</div>

<hr/>

<div class="container">
  <div class="wrapper">
    <img src="http://placehold.it/1500x800">
  </div>
</div>

Update Added vertical-align: top; to image to fixed extra gap at bottom as pointed out by @TemaniAfif

Rahul
  • 4,294
  • 1
  • 10
  • 12
  • Thanks, but the wrapper does not fit the image - there is an empty line at the bottom of the the wrapper. – cronfy May 11 '18 at 07:41
  • 1
    @cronfy add `vertical-align:top` to the image to remove this empty line – Temani Afif May 11 '18 at 13:37
  • @Rahul it is same approach as sol's one. Unfortunately, .wrapper becomes not functional in terms of that it is impossible to place absolutely positioned elements inside it. While it looks like wrapper tightly wraps the `img`, indeed it's height does not equal img's height. Try adding absolute positioned labels to the top and the bottom of the wrapper (as in my post), and you'll see. // Wrapper is required to be usable, it's not just redundant element. – cronfy May 16 '18 at 06:38
0

Add image using background-image on wrapper element

.wrapper {
    outline: 1px solid yellow;
    background: url(http://via.placeholder.com/150x300);
    background-size: cover;
    width: 100%;
    height: 100%;
    background-repeat: no-repeat;
}
Nandita Sharma
  • 13,287
  • 2
  • 22
  • 35
  • Thanks, but if image's natural size is smaller than container, it wil be stetched. Image should not exceed it's natural size. – cronfy May 11 '18 at 07:39
0
.container{
  width: 200px;     
  height: 200px;    
  background-color: green;
}
.wrapper {
  outline: 1px solid yellow;
  height:100%;
}
img{
  height: 100%;
}
dhingu
  • 1
  • 2
0

$(function() {
  $('input[name=container-width]').on('change', function() {
    $('.container').css('width', $(this).val() + 'px')
  })

  $('input[name=container-height]').on('change', function() {
    $('.container').css('height', $(this).val() + 'px')
  })

  $('input[name=image-width]').on('change', function() {
    var width = $(this).val()
    var height = $('input[name=image-height]').val()
    $('img')[0].src = 'http://via.placeholder.com/' + width + 'x' + height
  })
  
  $('input[name=image-height]').on('change', function() {
    var height = $(this).val()
    var width = $('input[name=image-width]').val()
    $('img')[0].src = 'http://via.placeholder.com/' + width + 'x' + height
  })

})
.container {
  width: auto; /* can have any value in pixels */
  height: auto; /* can have any value in pixels */
  background-color: green;
}

.wrapper {
  width: 100%;
  height: 100%;
  outline: 1px solid yellow;
  
}

.container > .wrapper > img {
  width: 100%;
  height: 100%;
  opacity: .8;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Settings</h2>
Container: width <input type="number" step="10" name="container-width" value="200"> height <input type="number" step="10" name="container-height" value="200">
<br>
Image: width <input type="number" step="10" name="image-width" value="150"> height <input type="number" step="10" name="image-height" value="300">
<br>
<br>

<h2>Demo</h2> 

<div class="container">
  <div class="wrapper">
    <img src="http://via.placeholder.com/150x300">
  </div>
</div>

What about this ?