How do I use CSS to position a fixed variable height header and a scrollable content box?

Css

Css Problem Overview


I'm trying to make a web page with a fixed header and a scrollable content area. This is trivial when the header has a known height but I'm struggling to find a solution for when the header is fluid.

The layout I want is:

--------------
head
--------------
content
--------------

where "head" is whatever height its content needs it to be and "content" has no minimum height but will reach a maximum height of the bottom of the viewport before becoming scrollable.

Is this possible these days in pure CSS? I'm targeting IE8+.

To clarify what I want, here is what I would do if I knew the height of the header:

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">

body {
    margin: 0;
}

#head {
    background: yellow;
    height: 20px; /* I can't rely on knowing this. */
}

#content {
    background: red;
    position: absolute;
    top: 20px; /* here also */
    bottom: 0;
    width: 100%;
    overflow: auto;
}

        </style>
    </head>
    <body>
        <div id="head">some variable height content</div>
        <div id="content">
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
        </div>
    </body>
</html>

Css Solutions


Solution 1 - Css

Assuming by "fixed" you mean position:fixed, I don't think it's possible in pure CSS, as position:fixed takes the element out of the document flow.

However, it should just take a line or two of JavaScript to get what you want. Something like this (untested, only for example purposes, will need syntax tweaked to actually work):

var height = document.getElementById("head").offsetHeight;
document.getElementById("content").style.marginTop = height + 'px';

Something like that should get you the rendered height of the fixed <div> and set the content <div>'s margin accordingly. You'll also need to explicitly set a background color on the fixed <div>, otherwise the content will appear to bleed into the fixed one when scrolling.

Solution 2 - Css

Here's a solution, but it's a cheat. Basically, you have a duplicate header element, to push down the content, under the fixed position one:

<div class="outer">
    <div class="header">Header content goes here</div>
    <div class="header-push">Header content goes here</div>
    <div class="content">
        ...
    </div>
</div>

Solution 3 - Css

I did a combination of both the accepted and Eric's answer. An empty div is used to push the content bellow "head". The width of this div is set by jQuery when window.onresize is fired:

function resizeHeader() {
    $(".header-push").height($(".header").height());
}
$(document).ready(resizeHeader);
$(window).resize(resizeHeader);

See this jsFiddle for more info.

Solution 4 - Css

This javascript will take the variable height of a fixed header and set the top margin of the content to flow underneath. Just call page load.

<script type="text/javascript">
    function AdjustHeight() {
        var height = document.getElementById("fixedheader").offsetHeight;
        document.getElementById("content").style.marginTop = height + 'px';
    }    
</script>

Solution 5 - Css

Here is a full code example of Shauna's solution above for the lazy/confused/people who can't get this working.

<html>
<head>

<style>
#FreezePaneHeader {
    position: fixed; top:0; left: 0; width: 100%;background-color: gray;
}

#FreezePaneBody {
    margin-top: 30px;
} 
</style>

 <script type="text/javascript">        
        function resizeFreezePane()
        {
            var height = document.getElementById("FreezePaneHeader").offsetHeight;
            document.getElementById("FreezePaneBody").style.marginTop = height + 'px';
        }

        //Resizes content to allow static header with dynamic height on postbacks
        function pageLoad() {
            resizeFreezePane();
        }

        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;
            }
        };

        //also when page is resized.
        addEvent(window, "resize", resizeFreezePane);

	function GenerateLorem(ele){
		var x = document.getElementById(ele);
		x.innerHTML = x.innerHTML + "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur pretium euismod leoquis convallis. In ornare suscipit massa eu commodo. Pellentesque vel nibh volutpat, commodo libero et, 

porta tellus. Duis eu fringilla arcu. Etiam imperdiet lectus diam, dignissim viverra dui hendrerit id. Fusce condimentum bibendum tincidunt. Aenean fringilla felis elit, et dapibus ante tempus at. Phasellus quis dolor odio.</p>";
		resizeFreezePane();
		}
    </script>
</head>

<body>
<div id="FreezePaneHeader">Frozen Page Header</div>
<div id="FreezePaneBody">
<button id="ButtonAddText" onclick="GenerateLorem('FreezePaneHeader'); return false;">Add more header text</button>
<button id="ButtonAddText" onclick="GenerateLorem('FreezePaneBody'); return false;">Add more body text</button>
<p id="PageContents"></p>
</div>
</body>

</html>

Solution 6 - Css

The easiest way is to simply replace position: fixed; with position: sticky;

header {
  position: sticky;
  top:0;
  background-color: red;
  color: #fff;
}

main {
  background-color: green;
  color: #fff;
  min-height: 150vh;
}

DEMO: https://jsfiddle.net/4zndve6p/

Solution 7 - Css

I solved this problem using getComputedStyle. This method allows you to take all CSS styles. And based on them, change the style of another element. Here is all the code using native JS:

<style>
#head {
    position: fixed;
    width: 100%;
    top: 0;
    left: 0;
    background: rgb(170, 170, 170);
    /* height: 50px; for example no fixed height*/
    z-index: 2;
}

#content {
    position: relative;
    width: 100%;
    background: rgb(180, 37, 37);
  }

<body>
<div id="head">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Enim, quisquam.</div>
<div id="content">
    Some content text<br />

</div>
<script>
    const header = document.querySelector('#head'),
        content = document.querySelector('#content');
    //Now get all possible CSS styles from #head using getComputedStyle
    const marginTopOffset = getComputedStyle(header);
//Resize event is for autosearch width of a browser window
window.addEventListener('resize', ()=> {
    function marginOffset() {
        content.style.marginTop = marginTopOffset.height;
        // you can add changes to another CSS style 
        // content.style.backgroundColor = marginTopOffset.backgroundColor;
    };
    marginOffset();
    // You can see how changes margin-top in console on the fly
    console.log(content);
});
</script>
</body>

Solution 8 - Css

Here is my simple and working solution with just four lines of code.

Description

  • getBoundingClientRect calculates the effective properties of an element, including the margins. I use this to get the effective bottom value.
  • This value is assigned to the marginTop property of the content element.
  • An event listener for onResize is required to react to changes in the window size, which can effect the header height.

See it working

My fiddle (adjust width to see the effect): https://jsfiddle.net/z1uac3Lw/

function adjustHeaderHeight() {
   let header = document.getElementById('head');
   let rect = header.getBoundingClientRect();
   document.getElementById('content').style.marginTop = rect.bottom + 'px';
}

window.addEventListener('resize', function() {
   adjustHeaderHeight();
});

body {
    margin: 0;
}

#head {
    background: yellow;
//    height: 20px; /* I can't rely on knowing this. */
}

#content {
    background: red;
    position: absolute;
    top: 0px;
    bottom: 0;
    width: 100%;
    overflow: auto;
}

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body onLoad='adjustHeaderHeight();' >
        <div id="head">some variable height content ... with a very long long long long long long long long long long long long long long long long long long long long long line to show the effect</div>
        <div id="content">
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
            scrollable content<br/>
        </div>
    </body>
</html>

Code with inline javascript / CSS

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
body {
    margin: 0;
}

#head {
   background: yellow;
//    height: 20px; /* I can't rely on knowing this. */
}

#content {
   background: red;
   position: absolute;
   top: 0px;
   bottom: 0;
   width: 100%;
   overflow: auto;
}

        </style>
        <script type="text/javascript" type="text/javascript">        
    
function adjustHeaderHeight() {
   let header = document.getElementById('head');
   let rect = header.getBoundingClientRect();
   document.getElementById('content').style.marginTop = rect.bottom + 'px';
}

window.addEventListener('resize', function() {
   adjustHeaderHeight();
});

      </script>        
        
   </head>
   <body onLoad='adjustHeaderHeight();' >
      <div id="head">some variable height content ... with a very long long long long long long long long long long long long long long long long long long long long long line to show the effect</div>
      <div id="content">
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
         scrollable content<br/>
      </div>
   </body>
</html>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid RoeView Question on Stackoverflow
Solution 1 - CssShaunaView Answer on Stackoverflow
Solution 2 - CssEricView Answer on Stackoverflow
Solution 3 - CssgecaView Answer on Stackoverflow
Solution 4 - CssKeithView Answer on Stackoverflow
Solution 5 - CssRavendarkskyView Answer on Stackoverflow
Solution 6 - CssDimChtzView Answer on Stackoverflow
Solution 7 - CssAntonio MView Answer on Stackoverflow
Solution 8 - CssBogisWView Answer on Stackoverflow