Jamiec
Jamiec

Reputation: 136074

Change body height based on dynamic header height

I have a layout with a top section which can grow in height, and a content section below which contains a chart. The basic HTML structure can be characterized as:

<div class="wrapper">
    <div class="top">
        <span class="box">Box 0</span>
        <span class="box new">
            <button>+</button>
        </span>
    </div>
    <div id="chart">
       Chart
    </div>
</div>

Each box element is a fixed width and height, and is a fluid design so that as the boxes cannot fit onto the same line, they wrap round to the next, making their container (top) grow.

Here is an interactive demo at this stage: http://jsfiddle.net/d1d6bwbc/ where you can see that the wrapper is 100% width & height (red border). The top has a magenta border, and grows as the boxes need to wrap and the chart has a blue border.

I am trying to make that chart element fill 100% of the remaining height in wrapper.

Here is my css so far

body{margin:0}
.wrapper{
    position:absolute;
    border:1px solid red;
    height:calc(100% - 2px); /* account for border in this example */
    width:calc(100% - 2px); /* account for border in this example */
}

.top{
    border: 1px solid magenta;
    padding-bottom:5px
}

#chart{
    border: 1px solid blue;
    /* style chart to fill 100% of wrapper
}

I have tried numerous css hacks and tricks, such as those found in this question and this one too but they do not seem to work on my layout.

I have complete control over the layout so if my markup requires some work that will be fine, but a simple way to achieve a fill on that chart element is what I'm after.

Upvotes: 1

Views: 1329

Answers (2)

Stickers
Stickers

Reputation: 78676

Looks like a perfect case for CSS table layout. Set the wrapper as table, top and bottom box as table-row, and the bottom box with height:100% that pushes the top box to its minimal height to fit the content inside.

function ViewModel() {
    var self = this;

    self.boxes = ko.observableArray([new Box(0)]);

    self.addBox = function () {
        self.boxes.push(new Box(self.boxes().length));
    }
}

function Box(index) {
    self.text = "Box " + index;
}

ko.applyBindings(new ViewModel())
html, body {
    height: 100%;
    margin: 0;
}
.wrapper {
    display: table;
    border-collapse: collapse;
    border: 1px solid red;
    box-sizing: border-box;
    height: 100%;
    width: 100%;
}
.top {
    border: 1px solid magenta;
    display: table-row;
}
#chart {
    border: 1px solid blue;
    display: table-row;
    height: 100%;
}
.box {
    width: 150px;
    height: 50px;
    border: 2px solid black;
    display: inline-block;
    vertical-align: top;
    margin: 5px;
}
.box.new {
    border: 1px dashed black;
}
.box.new button {
    display: inline-block;
    width: 100%;
    height: 100%;
    text-align: center;
    font-size: 3em;
    border: none;
    background: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div class="wrapper">
    <div class="top">
        <!-- ko foreach:boxes -->
        <span class="box" data-bind="text:text">            
        </span>
        <!-- /ko -->
        <span class="box new">
            <button data-bind="click:addBox">+</button>
        </span>
    </div>
    <div id="chart">
       Chart
    </div>
</div>

http://jsfiddle.net/d1d6bwbc/4/

Upvotes: 2

Max Novich
Max Novich

Reputation: 1169

I am not sure if this behavior is achievable via pure CSS . But it is easy to create using javascript. I've added this JS to your example

var Fill = function (){
    var top =document.getElementById("top").offsetHeight;
    var wrapper =document.getElementById("wrapper").clientHeight;
    document.getElementById("chart").setAttribute("style","height:"+(wrapper-top-1)+"px");

}

var addEvent = function(elem, type, eventHandle) {
    if (elem == null || typeof(elem) == 'undefined') return;
    if ( elem.addEventListener ) {
        elem.addEventListener( type, eventHandle, false );
    } else if ( elem.attachEvent ) {
        elem.attachEvent( "on" + type, eventHandle );
    } else {
        elem["on"+type]=eventHandle;
    }
};

addEvent(window, "resize", Fill);
Fill();

and also edited the addBox function to fire the event

self.addBox = function(){
        self.boxes.push(new Box(self.boxes().length));
        Fill();
    }

I also Had to add id's to top and wrapper for this example but you can use class obviously.

Here is the working Fiddle

Upvotes: 1

Related Questions