Search code examples
htmlcsslayoutpositioncss-position

Stretch a block element to fill 100% vertical scroll height of its parent element


I have a layout with a vertical scroll. One of the child elements within the scrollable div is absolutely positioned with a large top value, inducing a vertical scrollbar on the parent. The scrollable parent div also has some child div elements (lets call them pillars) positioned horizontally adjacent to each other via position: absolute and some left value.

Here's the HTML markup:

<div id="root" style="height: 250px; position: relative;">
<div class="stretch">
    <div id="container" class="container">  
         <div id="pillar1" style="left: 0.0%; width:33.25%;" class="pillar" ></div>
         <div id="pillar2" style="left: 33.05%; width:33.25%;" class="pillar" ></div>
         <div id="pillar3" style="left: 66.05%; width:33.25%;" class="pillar" ></div>     
         <div id="fixed-and-not-movable" style="background: blue; width: 25px; height: 25px; top:350px; left: 150px; position: absolute;">
    </div>          
</div>

and the CSS:

.stretch {
    bottom: 0; 
    left: 0; 
    right: 0; 
    top: 0; 
    position: absolute; 
    height: auto; 
    width: auto;    
}

.container {
    border: 2px solid;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
    overflow-x: hidden;
    overflow-y: scroll;
    position: absolute;
}

.pillar {
    border: 1px dotted red;
    bottom: 0;
    height: 100%;
    position: absolute;
    top: 0;    
}

I want the pillar divs to capture the entire scroll height of the parent "container". Right now their height is the parents client height (not scroll height). So when you scroll down you will notice the pillars are not filling all the available height inside the overflow:scroll.

Can someone suggest changes to CSS classes (.container and/or .pillar) to make this work.

Here's a link to js fiddle showing the same problem: http://jsfiddle.net/AshwinPrabhuB/2o5whkmq

Thanks!


Solution

  • After a lot of experimenting and hair pulling, I finally figured out that there is no perfect CSS solution to this problem. I would love it if someone can prove me wrong.

    The problem as I understand it is, there is no way via pure cross browser compatible CSS for a child element to vertically stretch 100% to fill its parents scrollHeight, if the parents height is not explicitly defined.

    So with the above conclusion, I have worked around the problem by placing a explicitly sized div under the scrolling container and setting a explicit min-height on the pillars. I can calculate the height of this new go-between div via JavaScript.

    Here's the modified HTML markup (only the fixedMinHeight div is new)

    <div id="root" style="height: 250px; position: relative;">
    <div class="stretch">
        <div id="container" class="container">  
            <div id="fixedMinHeight" class="stretchFixed">
                 <div id="pillar1" style="left: 0.0%; width:33.25%;" class="pillar" ></div>
                 <div id="pillar2" style="left: 33.05%; width:33.25%;" class="pillar" ></div>
                 <div id="pillar3" style="left: 66.05%; width:33.25%;" class="pillar" ></div>     
                 <div id="fixed-and-not-movable" style="background: blue; width: 25px; height: 25px; top:350px; left: 150px; position: absolute;">
            </div>
        </div>          
    </div>
    

    and the CSS (.stretchFixed is an addition from earlier)

    .stretch {
        bottom: 0; 
        left: 0; 
        right: 0; 
        top: 0; 
        position: absolute; 
        height: auto; 
        width: auto;    
    }
    
    .container {
        border: 2px solid;
        bottom: 0;
        left: 0;
        right: 0;
        top: 0;
        overflow-x: hidden;
        overflow-y: scroll;
        position: absolute;
    }
    
    .pillar {
        border: 1px dotted red;
        bottom: 0;
        height: 100%;
        position: absolute;
        top: 0;    
    }
    
    .stretchFixed {
      min-height: 375px;
      position: relative;
      height: 100%;
    }
    

    Here's the fiddle link with the changes: https://jsfiddle.net/AshwinPrabhuB/2o5whkmq/10/

    Alternatively, the same scheme can be applied on each individual pillar thereby not necessitating DOM insertion.

    I would love to be proved wrong, but for time being I can live with this workaround.