1

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.

Community
  • 1
  • 1
Jamiec
  • 133,658
  • 13
  • 134
  • 193

2 Answers2

2

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/

Stickers
  • 75,527
  • 23
  • 147
  • 186
1

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

Max Novich
  • 1,169
  • 9
  • 20
  • Thanks, thats a good workaround. Ill adapt it easily to my situation if there isn't a pure css fix for this. – Jamiec Jun 28 '15 at 14:21