skip to Main Content

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


  1. I have compiled this code snippet that you can try:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <style>
            .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: none; /* Initially hidden */
                flex-shrink: 0;
                background-color: orange;
                padding: 10px;
                max-height: 230px; /* Shrink to content */
            }
    
            .info-content-wrapper {
                padding: 5px;
                overflow-y: auto; /* Scrollable if content exceeds height */
            }
    
            .content {
                width: 80px;
                height: 150px;
                background-color: yellow;
            }
    
            .controls {
                background-color: lightblue;
                padding: 5px;
                flex: 0 0 auto;
            }
        </style>
    </head>
    <body>
        <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>
    
        <script>
            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`;
            });
        </script>
    </body>
    </html>
    
    Login or Signup to reply.
  2. Just remove .info { flex-shrink: 0; } and add .info { overflow: hidden; } or .info { min-height: 0 } to make this block shrinkable:

    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: hidden;
    }
    
    .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>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search