1

What I'm looking for is to have a container div with any number of headers and footers which would then have another scrolling div in between the various headers/footers. The catch is here that the headers and footers can't scroll and the container/headers/footers can take any height depending on their content.

I've only been able to get this working with a statically sized header/footer. In the code I've added the problem I've come across: the text in the header/footer may wrap, which will bring it to two lines. So the header should grow to allow this extra line and the content should shrink to give space to the header. Is there any way to do this without using javascript? I will know the number of headers/footers ahead of time if that helps.

This is going to be a component in a page possibly with one or more per page, for example in a 2x2 setup where each takes up a 4th of the browser window. The functionality is mainly needed as the width of the browser might change causing the header content to break a new line. This could be done pretty easily with javascript but I've always heard that resize event handlers are evil.

For example if my wrapper is 600px high, I may have 2 headers and 1 footer. Lets say the first header's content causes it to break to a new line, but the second header's doesn't. So the first headers height is 20px where the second's is 10px. And lets say the footer's content also causes a line break and thus has 20px height. This gives us 50px worth of headers and footers so now the height of the scroller should be 550px.

Ascii Art:

 ____________________________________________
|         HEADER 1                           |
|________breaks to new line__________________|
|____________________________HEADER2_________|
|                                         | ||
|                                         | ||
|    This is my scroller                  | ||
|                                         | ||
|                                         | ||
|                                         | ||
|_________________________________________|_||
|     Some                                   |
|_____Footer_________________________________|

HTML:

<div class="wrapper">
    <div class="header">
        Some<br/>
        Header
    </div>
    <div class="scroller">
    Content<br />
    Content<br />
    Content<br />
    Content<br />
    ...
    </div>    
    <div class="footer">
        Some<br/>
        Footer
    </div>
</div>

CSS:

body{
    height:100%;
}
.wrapper{
    height:400px;
    border-left:1px solid red;
}
.header, .footer{
    background-color: #EEE;
    height:27px;
}
.scroller{
    height:calc(100% - 54px);
    background-color: #CCC;
    overflow:auto;
}

the red border is to show how far the wrapper goes down, so the footer doesn't show up below. I did this instead of overflow: hidden simply for debugging.

http://jsfiddle.net/yKTdz/4/

Don
  • 3,987
  • 15
  • 32
  • So, am i right. the wrapper must be `400px` high, the header and footer can be as big as they need inside the wrapper, so if the header and footer are both `150px` high. the content must be `100px` i don't understand what you mean when you say there will be multiple headers/footers – iConnor Jul 12 '13 at 13:08
  • The wrapper can be any arbitrary size, even a percentage such as 50%. By multiple headers/footer I mean there will be two different divs that represent 2 different headers that both should not scroll and should effect the height of the scroller. You have your sizes correct, but the 150px wouldn't be set, it should only reach that through expanding due to content. (I'll try to clarify more in the question) – Don Jul 12 '13 at 13:12
  • This is simply a variation on the fixed header/footer, fluid content theme that's been asked many times on SO (http://stackoverflow.com/questions/10474354/layout-with-fixed-header-and-footer-fixed-width-sidebar-and-flexible-content). You probably won't be able to do fixed, fluid-height headers that work in IE < 10 without JS. Got jQuery? – isherwood Jul 12 '13 at 13:14
  • No jQuery, I have dojo though. Thanks for the further reading, I couldn't seem to find any ;) – Don Jul 12 '13 at 13:26
  • @isherwood, mine solution should work on IE above 7... I'd be curious to know if it's true, please open it in IE9 if you can; I don't have it, and IE8 can't even open jsFiddle :D – Andrea Ligios Jul 12 '13 at 15:47

6 Answers6

2

Achieved with pure CSS (not even CSS3), by mixing table display with relative and absolute positioning.

Running Demo: http://jsfiddle.net/x4vhW/

HTML

<div class="wrapper">
    <div class="tr stretch">
        <div class="td">a header</div>
    </div>
    <div class="tr">
        <div class="td">
            <div class="contentWrapper">
                <div class="content">
                    content 0
                    ...............
                    ...............
                    ...............
                    content 29
                </div>
            </div>
        </div>
    </div>
    <div class="tr stretch">
        <div class="td stretch">a footer</div>
    </div>
</div>

CSS Dark Magic(tm)

.wrapper {
    height  : 200px;
    width   : 200px;
    display : table;
}

.tr { display : table-row; }

.td { display : table-cell; }

.stretch { height : 1%; }

.contentWrapper {
    position   : relative;
    overflow-y : scroll;
    height     : 100%;
}

.content {
    position : absolute;
    right    : 0; 
    left     : 0;
}

Note: the stretch class is used in order to prevent headers and footers to grow if content has a small content; with stretch, they will auto-fit.

And finally, according to CanIUse, it is supported by almost all browsers, including IE8.

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
  • 1
    Very nice solution! I really need to read up on how to use table displays to my advantage like this. – Don Jul 12 '13 at 16:08
  • is there a way the `wrapper` could be a percentage height? This isn't totally needed, I'm just curious as setting height to 50% seems to break it. – Don Jul 12 '13 at 16:53
  • This doesn't work for me on IE10, the content section is blank.. Is it just the fiddle? – Daniel Moses Jul 12 '13 at 17:11
  • Sorry guys, I don't have IE9 nor IE10, can't tell what is not working there... I coded it on Firefox, and opened now from the phone on Chrome Mobile, and it is running perfectly... Let's start from here and try to play with the code, you asked for a pure CSS solution, here it is – Andrea Ligios Jul 12 '13 at 19:50
  • I appreciate the help, I'm going to fiddle with it some today and see what I can get – Don Jul 15 '13 at 13:59
  • It appears the td class' height is collapsing to 0. Looking for a workaround >.> – Don Jul 15 '13 at 14:21
  • Went ahead and gave you the bounty since this is probably as close to a pure css answer as you can get... Minus the lack of IE support it's perfect :P – Don Jul 17 '13 at 18:40
  • yeah, it probably is... thanks for the bounty; meanwhile, I discovered that Opera too suffered of a problem similar to the IE one, then I developed a solution working on Opera, that doesn't work on FF / Chrome, but not even in IE :O I've forked something, but you can find most of the tests in the fiddle (by incrementing the number in the URL)... btw, a different HTML (not only CSS) is needed in order to work on IE, or Opera, or all the others... three versions for being crossbrowser :/ I guess you were right, this is the closest we'll get ;) at least it worked from my Chrome mobile on GNex :) – Andrea Ligios Jul 17 '13 at 21:07
1

I took Matt Cooper's solution and updated it a bit. This resolves the inability to add more than one widget to a page, and also the footer sizing issue.

http://jsfiddle.net/XSADu/10/

Steps:

  1. Make the wrapper relative so that absolute children can be position
  2. Make header,footer,scroller absolute. Position the footer at the bottom
  3. Add javascript to size the scroller top/bottom.

CSS

.wrapper{
    height:400px;
    border-left:1px solid red;
    position: relative;
}

.header, .footer, .scroller {
    background-color: #EEE;
    position:absolute;
    width: 100%;
}
.footer {
    bottom: 0;
}

.scroller{
    background-color: #CCC;
    overflow:auto;
}

JS

$(document).ready(function(){
    $('.scroller').css("top", $('.header').height()); 
    $('.scroller').css("bottom", $('.footer').height());     
});

NOTE: If the header/footer sizing are dynamic, you may want to give them some max heights as a percentage of the wrapper, so that the content isn't hidden.

Daniel Moses
  • 5,872
  • 26
  • 39
  • it still bugs me that the scroll bar is covered by the header/footer. Good addition though. – Don Jul 12 '13 at 16:05
1

Building on DMoses answer, this solution involves just a bit more jquery, but it removes the issue of the scrollbar being slightly hidden by the footer/header

the jQuery:

$(document).ready(function(){
    $('.scroller').css("margin-top", $('.header').height());

    var height = $('.wrapper').height() - ($('.footer').height() + $('.header').height());

    $('.scroller').css("height", height);     
});

jsfiddle.net/XSADu/5/

Brian Phillips
  • 4,302
  • 2
  • 25
  • 40
  • Good alternate solution, thanks. Still hoping for some crazy css trickery though... – Don Jul 12 '13 at 16:06
1

This can now be done using CSS grids pretty easily. without the need of an filler elements even.

If you want multiple headers, simply put a few divs in there, or make the "header" div a flexbox if that's what you want.

HTML stays the same:

<div class="wrapper">
    <div class="header">
        Some<br/>
        Header
    </div>
    <div class="scroller">
    Content<br />
    Content<br />
    Content<br />
    Content<br />
    ...
    </div>    
    <div class="footer">
        Some<br/>
        Footer
    </div>
</div>

CSS:

.wrapper{
    height:400px;
    border-left:1px solid red;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: [header] auto [content]1fr [footer]auto;
}
.header{
    background-color: #EEE;
    grid-row: header;
}
.footer{
    background-color: #EEE;
    grid-row: footer;
}
.scroller{
    background-color: #CCC;
    overflow:auto;
    grid-row: content
}

http://jsfiddle.net/yKTdz/15/

Don
  • 3,987
  • 15
  • 32
  • 1
    Good to know @Don – Andrea Ligios Jul 21 '17 at 21:47
  • Yeah, I just learned about this stuff the other day. Blowing my mind. Still has issues with older browsers though. IE 10 and 11 support it a bit, though I don't think they support this `auto` feature. – Don Jul 24 '17 at 17:48
0

There's no way to do this with just Css, You'd need to use Javascript to check the height of the header and footer when the page loads and adjust things accordingly. Otherwise when you use position:fixed you'll get some overlap. See the Attached fiddle! http://jsfiddle.net/XSADu/

I use jQuery to adapt the padding-top on the scroller on document ready. like so

$(document).ready(function(){
    $('.scroller').css("padding-top", $('.header').height()); 
});
Matt Cooper
  • 1,004
  • 7
  • 12
  • Your fiddle does not work correctly. I don't want the position of the header to be fixed as there may be multiple on a scrolling page. While the content is not hidden the scroll bar is. Overall ugly solution. – Don Jul 12 '13 at 13:32
  • Position:fixed is definitely what you're looking for if you're wanting to solve this kind of problem. But if not, The general idea for doing this kind of thing would be to let the heights of the headers and footers be auto, then on load you can grab those heights and change the scroller height based on them. – Matt Cooper Jul 12 '13 at 13:41
0

As stated by Matt Cooper, I don't think there's a css pure solution. Here's my attempt: jsfiddle

var CalcHeight = function() {
    this.init = function() {
        var totHeight = $('body').height();
        var componentsHeight = $('.headers').height() + $('.footers').height();
        var diff = totHeight - componentsHeight;
        $('.hider').css({
            height: diff
        });
    }

    this.init();
}

var calcHeight = new CalcHeight;
Jonas Grumann
  • 10,438
  • 2
  • 22
  • 40