I’m really sorry about this question, because I’ve already seen many similar questions on here, but I still can’t figure it out.
I’d like to create an overlay
element with controls for a display
element, where the controls are stacked vertically and aligned to the right and bottom of the display. There is one fixed-size element buttons
containing some controls, and then an dynamically-sized element info
which only pops up when some parts of the display are active. The info
always contains a button to close itself, and some information of dynamic height. I would like the info
container to always shrink to its content. However, if the height of the content is too big, so that the containing info
element would be taller than the remaining vertical space of the overlay
container, I would like the content to be scrollable.
I thought I’d just use a flex container and overflow-y
attributes, but that doesn’t seem to cut it. It seems that the overflow is only triggered if the parent has some explicit height, but in my case, the parent container is dynamic, meaning it should be growing up to a defined height, and then stop, while the content becomes scrollable.
This was my attempt so far:
const info = document.querySelector(".info");
const content = document.querySelector(".content");
document.querySelector("#open-info").addEventListener("click", () => {
info.style.display = "flex";
});
document.querySelector("#close-info").addEventListener("click", () => {
info.style.display = "none";
});
document.querySelector("#increase-height").addEventListener("click", () => {
content.style.height = `${content.clientHeight + 30}px`;
});
document.querySelector("#decrease-height").addEventListener("click", () => {
content.style.height = `${content.clientHeight - 30}px`;
});
.display {
position: relative;
width: 400px;
height: 300px;
background-color: gray;
}
.overlay {
position: absolute;
display: flex;
width: 100%;
height: 100%;
top: 0;
left: 0;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
}
.info {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
flex-shrink: 0;
background-color: orange;
padding: 10px;
max-height: 230px;
/* overflow works, but fixed size is undesirable, as the remaining space in main-axis direction should be used up */
/* max-height: 100%; overflow doesn't work, as it refers to parent's height, not to remaining available space in main-axis direction*/
}
.info-content-wrapper {
padding: 5px;
overflow-x: hidden;
overflow-y: auto;
}
.content {
width: 80px;
height: 150px;
background-color: yellow;
}
.controls {
background-color: lightblue;
padding: 5px;
flex: 0 0 auto;
}
<div class="display">
<div class="overlay">
<div class="info">
<button id="close-info">Close</button>
<div class="info-content-wrapper">
<div class="content"></div>
</div>
</div>
<div class="controls">
<button id="open-info">Open info</button>
<button id="increase-height">+</button>
<button id="decrease-height">-</button>
</div>
</div>
</div>
In the example, you can use the buttons to hide/show the info, as well as increase/decrease the content height. I used a fixed max-height: 230px
on .info
to demonstrate how it should work. However, instead of using a fixed max-height
, it should use the remaining vertical space of the flex container. But when I use max-height: 100%
, it will (as per definition) refer to the total height of the parent flex container. If I nest it further (like introducing a wrapper around the inner HTML of .info
) and set the max-height
of that element to 100%
, it still doesn’t work.
I feel like the answer should be very simple, but I can’t seem to figure out a solution with just flex layout and without resorting to some workarounds with calc
. I feel very stupid about this and would be really happy about some help!
2
Answers
I have compiled this code snippet that you can try:
Just remove
.info { flex-shrink: 0; }
and add.info { overflow: hidden; }
or.info { min-height: 0 }
to make this block shrinkable: