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

4

12

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!

Chronometer answered 5/5, 2015 at 12:38 Comment(3)
I really doubt if there is a pure css solution (the madness of positioning) well, there might be another way for doing it if you can explain the usage. otherwise, i guess you'll have to wait and start a bounty and see.Vergievergil
@sdcr I can script my way out of this problem (like this: jsfiddle.net/AshwinPrabhuB/2o5whkmq/9), but only as a last resort if there is no non-hacky CSS solution.Chronometer
Similar question: #14499018Chronometer
C
7

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.

Chronometer answered 7/5, 2015 at 10:15 Comment(0)
S
3

The div .pillar is given position:absolute , so it is not taking height according to it's parent i.e, .container.

Simply remove position:absolute from css of .pillar.

Here is the FIDDLE.

Changes in CSS:

.pillar {
        border: 1px dotted red;
        bottom: 0;
        height: 100%;
        float:left;
        /*position: absolute;*/
        top: 0;  
        left:0 !important;
        width:32% !important;
    }

I have given a width of 32% because the borders currently used won't let it fit in the given width. Also, there is no need to specify the values for left explicitly for each of the pillars now. So I have just overridden these values.

EDIT: I understood it wrong. Now here's the corrected one.

Give height of the pillar as 100vmax. This unit will give it 100/100 size of the viewport, as I understood. Although the log is still showing unmached values for the heights. But I guess this is close enough. Also, the blue box is coming in it's way.

Check the FIDDLE.

.pillar {
    border: 1px dotted red;
    bottom: 0;
    height: 100vmax;
    float:left;
    /*position: absolute;*/
    top: 0;  
    left:0;
    width:32%;
}
Sobriety answered 5/5, 2015 at 12:51 Comment(5)
I still see the height not occupied? Am I the only one?Quag
I tried your suggestion to remove absolute from pillar. Did not find any difference and the console logs also indicate this. (Pillers have height : 246, Somehow make them (via CSS): 375)Chronometer
And just to let you know, //position: absolute; is only a comment in SCSS. /*position: absolute;*/ is the correct way.Screeching
@KunjanThadani Thanks for the tip, but 100vmax stretches upto viewport height and also ends up increasing the scrollheight of the div to match the view port height. While this appears to work, it is not the ideal solution, since this alters the srollheight of the container.Chronometer
Ok I understand. Will look for some solution.Sobriety
A
2

here are the solutions in my mind. I think you have to either add a wrapper div inside the container like this:

<div id="container" class="container">  
    <div style="height:400px;">
         <div id="pillar1" class="pillar" ></div>
         <div id="pillar2" class="pillar" ></div>
         <div id="pillar3" class="pillar" ></div>     
         <div id="fixed-and-not-movable">
    </div>   
</div>

Or you have to add specific height to all the pillars. This is because you are using height:100% on the pillar. So it's matching the closest element that has a height specified. In addition, I don't know if it is because of your scenario requirement that you have to add that .stretch div. For this case, you don't need that div at all. The code below does the same thing as yours.

<div id="root">
    <div id="container" class="container">  
        <div style="height:400px;">
            <div id="pillar1" class="pillar" ></div>
            <div id="pillar2" class="pillar" ></div>
            <div id="pillar3" class="pillar" ></div>     
            <div id="fixed-and-not-movable"></div> 
        </div>
    </div>
</div>
Ablation answered 8/5, 2015 at 18:58 Comment(1)
That is what I ended up doing. See my answer. So I was looking for something that does not involve hard coding the heights which would bring in JavaScript. Thanks for attempting!Chronometer
E
1

The best approach to get the max-height possible of the parent is to make its position fixed or absolute and instead of providing a height value you set the top attribute to zero and the bottom attribute to zero.

.parent {
  position: relative;

  .child {
    position: fixed; // or absolute
    top: 0; // anchors element to the top
    bottom: 0; // anchors element to the bottom
  }
 }
Endow answered 17/5, 2018 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.