Using position:absolute while keeping it inside the document flow
Asked Answered
D

4

19

enter image description here

It's a screenshot from a page currently I'm building. I'm trying to make sure the green button is always on the bottom of the container. Here is a piece of the code:

HTML

<div class="list-product-pat">
  <article>

   <!-- title, image, spec ... -->

   <div class="pricing-pat">

     <!-- the button goes here -->

   </div>
  </article>
</div>

CSS

 .list-product-pat article {
    position: relative;
    min-height: 260px;
 }

 .list-product-pat .pricing-pat {
    position: absolute;
    bottom: 0;
    width: 100%;
 }

So far there is no problem... until the product spec gets too long and it breaks into the green button.

enter image description here

I want to maintain the green button in the most bottom position, but in the same time I also want the height to extend if the product title/product spec gets too long.

In the ideal world, it should be something like this:

enter image description here

So my idea is to maintain the absolute positioning while still keeping it inside the document flow (so the product spec knows the green button is there and doesn't break through it).

I need it only to extend if the spec height gets too long. In other words, if the spec is in normal height, it wouldn't extent. I'd like to avoid a weird gap between the spec and the green button.

Is there any idea how to do it?

Here is a fiddle to see how I did it: http://jsfiddle.net/xaliber/xrb5U/

Donniedonnish answered 3/9, 2013 at 17:21 Comment(2)
OMG! What's wrong with my ad-blocker... Ah, screen shot.Hungry
Duplicate: https://mcmap.net/q/146241/-how-to-make-columns-the-same-height-regardless-of-content-and-vertically-align-buttons-within-those-columnsInsectivorous
G
7

Adding position:absolute takes it out of the document flow, there's no way to keep it in it. But you can add padding-bottom equivalent to height of the button to the article container instead, which will prevent long text overrunning the button.

.list-product-pat article {
    position: relative;
    min-height: 260px;
    padding-bottom:80px;
    -moz-box-sizing:border-box;
    box-sizing:border-box; 
}

http://jsfiddle.net/xrb5U/3/

A separate issue is that two containers with different amount of texts will be different sizes (if one is larger than the min-height set). There's no easy fix for this in CSS positioning, you have to resort to Javascript, Flexbox or display:table-cell to keep the height of all them the same but each of them has their own issues too.

Gland answered 3/9, 2013 at 17:25 Comment(5)
I thought of that; I also thought of adding the article's min-height with the height of the button. But putting it that way would look odd (too long) when the spec is in normal height (there would be a huge gap between the spec and the green button). :/Donniedonnish
That's what the "box-sizing:border-box;" bit is for ;) It includes the padding inside your min-height, so if the text is short the distance between button and text is normal - there's no big space.Gland
You can see it on my jsfiddle - even with padding-bottom, the left-most one "article" is the same height as in your jsfiddle.Gland
@mikel You didn't add box-sizing property on your fiddle demo :)Blacktop
@mikel Since you answer is nice, I won't post another one, but you might want to consider this, too: jsfiddle.net/hashem/xrb5U/4Blacktop
C
4

As @mikel already pointed out, you can't keep an element with position: absolute inside the normal document flow, but you can workaround this problem by simulating it.

Considering the example below:

img {
  position: absolute;
}
<img src="https://dummyimage.com/300x400/d9ca29/ffffff">
<span>Lorem Ipsum is simply dummy text of the printing and typesetting industry</span>

The <img> element is out of flow, this cause the <span> to be hidden behind it.

You can wrap the absolute element inside an empty container, then add height and width to container equal to height and width of the absolute element. By doing so, an invisible box is created around the absolute element, which makes it appear as part of the document normal flow.

If you already know the exact dimensions of the <img> element, you can simulate normal flow using just css:

div {
  border: 2px dotted grey;
  position: relative;
  width: 300px;
  height: 400px;
}

img {
  position: absolute;
}
<div>
  <img src="https://dummyimage.com/300x400/d9ca29/ffffff">
</div>

<span>Lorem Ipsum is simply dummy text of the printing and typesetting industry</span>

Else, if you don't know the dimensions of the absolute element upfront you have to simulate the normal flow dynamically with javascript:

window.addEventListener('load', function() {
  var div = document.querySelector('div');
  var img = document.querySelector('img');
  var rect = img.getBoundingClientRect();

  div.style.height = rect.height + 'px';
  div.style.width = rect.width + 'px';
});
div {
  border: 2px dotted grey;
  position: relative;
  max-width: 200px;
}

img {
  position: absolute;
  width: 100%;
}
<div>
  <img src="https://dummyimage.com/300x400/d9ca29/ffffff">
</div>

<span>Lorem Ipsum is simply dummy text of the printing and typesetting industry</span>
Carotid answered 17/3, 2019 at 10:32 Comment(0)
C
3

At some point in the (hopefully near) future, you'll be able to use the subgrid feature of CSS Grids. Currently, only Firefox supports this, but other browsers should add support soon.

Subgrid enables you to use Grid features with a non-flat structure (eg, an unordered list). That is, you can line up children of one element with children of another element, or in this case, the image, title, description, and price button.

.list-product-pat {
  /* Create a grid with 5 columns that are 175px wide,
     each with 5 rows that are sized based on the smallest item in the row */
  display: inline-grid;
  grid-template-columns: repeat(5, 175px);
  grid-template-rows: repeat(5, min-content);

  /* Colors and spacing to match design */
  background: #f4f4f4;
  padding: 1em;
  grid-column-gap: 1em;
  border: 1px solid #ddd;
}

.list-product-pat li {
  /* Ensure this item takes up the column */
  grid-row: 1 / -1;

  /* Make children grid items */
  display: grid;

  /* Use parent's grid for children */ 
  grid-template-rows: subgrid;

  /* Styles to match design */
  text-align: center;
  justify-items: center;
  border: 1px solid #ddd;
  background: #fff;
}

/* STYLES TO MATCH DESIGN BELOW */

.list-product-pat > li > img {
  margin-top: 1em;
}

.list-product-pat > li > h1 {
  margin: .8em 0;
  font-size: 1em;
}

.list-product-pat > li > p {
  margin: 0;
  color: #bbb;
  font-size: .8em;
  margin: 0 .5em 1em;
}

.list-product-pat > li > a {
  text-decoration: none;
  color: white;
  font-weight: bold;
  background: linear-gradient(#60bb76, #48b161);
  border-radius: .5em;
  box-shadow: 1px 1px 2px rgba(0, 0, 0, .5);
  padding: .5em;
  min-width: calc(100% - 1em);
  margin-bottom: .5em;
  box-sizing: border-box;
}

.list-product-pat > li > a > small {
  display: block;
  font-weight: normal;
  font-size: .7em;
  margin-top: .2em;
}
<ul class="list-product-pat">
  <li>
    <img src="https://placehold.it/40x70/">
    <h1>HTC Desire C</h1>
    <p>GSM, GPS, WiFi, kamera 5MP, bluetooth, Android, touchscreen, 600MHz</p>
    <a href="#">1.699.000 <small>6 Produk/4 Website</small></a>
  </li>
  <li>
    <img src="https://placehold.it/40x70/">
    <h1>Samsung 19300 Galaxy S III</h1>
    <p>GSM, GPS, WiFi, kamera 8MP, bluetooth, Android, touchscreen, 1.4GHz</p>
    <a href="#">5.300.000 <small>8 Produk/5 Website</small></a>
  </li>
  <li>
    <img src="https://placehold.it/40x70/">
    <h1>Samsung Galaxy Grand i9082</h1>
    <p>GSM, GPS, WiFi, touchscreen, 1.2GHz</p>
    <a href="#">3.499.000 <small>10 Produk/8 Website</small></a>
  </li>
  <li>
    <img src="https://placehold.it/40x70/">
    <h1>Apple iPhone 5 16GB</h1>
    <p>GSM, GPS, WiFi, kamera 8MP, bluetooth, iOS 6, touchscreen, 1.2GHz</p>
    <a href="#">7.599.000 <small>6 Produk/5 Website</small></a>
  </li>
  <li>
    <img src="https://placehold.it/40x70/">
    <h1>BlackBerry Curve 9360 (Apollo)</h1>
    <p>GSM, GPS, WiFi, kamera 5MP, bluetooth, 800MHz</p>
    <a href="#">225.000 <small>9 Produk/4 Website</small></a>
  </li>
</ul>
Chasitychasm answered 6/1, 2020 at 19:24 Comment(0)
C
0

The solution is actually quite simple. Duplicate the absolutely positioned footer with visibility hidden.

<div style="background: silver; position: relative; height: 100px">
  Height is 100px
  <div style="position: absolute; bottom: 0; left: 0">Footer</div>
  <div style="visibility: hidden">Footer</div>
</div>
<br />
<div style="background: silver; position: relative">
  No height specified
  <div style="position: absolute; bottom: 0; left: 0">Footer</div>
  <div style="visibility: hidden">Footer</div>
</div>
Concatenation answered 24/6, 2021 at 7:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.