johny why
johny why

Reputation: 2201

CSS To Make Vertical Scrolling Region Under Fixed Header?

Goal: Emulate the fixed header and scrollable content of this StackOverflow page:

<div id="header">This is the header.</div>

<div id='content'>This is the content.</div>

<div id='footer'>This is the footer.</div>

Url of site to be applied: https://gunretort.xyz/index.php/Portal:Tag?Tag=CommonGround

Body Padding Fails: A common recommendation is body{padding-top:...}, to push #content down. But it doesn't really push content down-- content still scrolls behind the header, so as you page down, the top of the content gets clipped by the header. Anchors land behind the header, where they can't be seen.

Example of content behind header:

enter image description here

Click 'Go to anchor' in this example to see the problem. https://codepen.io/johnyradio/pen/pKxBwQ

StackOverflow: StackOverflow does it right. The footer scrolls up with content, as desired. #content and #footer are sibs. I'm seeing the following in the CSS for this page, but i don't know if this is the magic.

#content {
    max-width: 1100px;
    width: calc(100% - 164px);
    background-color: #FFF;
    padding: 24px;
    box-sizing: border-box;
}

div {
    display: block;
}

#content:before, #content:after {
    content: "";
    display: table;
}

body *, body *:before, body *:after {
    box-sizing: inherit;
}

#content:after {
    clear: both;
}

Flex: I also see a lot of Flex in the .css files of this SO page. Flex is used in answers below. Problem with those answers-- they break the #footer.

Grid: Grid seems like it should be the way. But i'm concerned about browser-compatibility. Also, so far, i haven't found a grid solution.

Overflow: This approach uses fixed position to place the #content below the header, without overlap. Making it a fixed-size box with overflow:auto gives the #content div it's own scrollbar. We could fix the #footer position below #content, but drawback is the footer would not scroll up-- it would have to occupy permanent real-estate at the bottom of the viewport.

https://codepen.io/johnyradio/pen/aKReor

+Padding -Margin: Some solutions involve padding and margins. That doesn't work consistently for span-anchors, a-links, coming in from same page or a different page. Using :target when target-id's aren't known doesn't work in all cases. Also, it doesn't address correct page-down. Also, it just seems more efficient to apply style to the major divs, instead of every single link-target. Any solution requiring knowledge of id or class name won't work, because we won't have that knowledge-- besides, a robust CSS solution covering all current and future cases should not require such knowledge.

a[name] {
  padding-top: 40px;
  margin-top: -40px;
  display: inline-block;
}

https://css-tricks.com/hash-tag-links-padding/

Frame: I'm thinking a frame might be an option, but there's the problem that i cannot change the HTML.

Not a duplicate: This question is not a dupe of other threads here. Solutions i've seen on SO don't handle unknown link-targets, page correctly, and scroll the footer (like this page). Eg,

Body top gap; should I use margin or padding?

Upvotes: 1

Views: 4666

Answers (3)

H&#229;ken Lid
H&#229;ken Lid

Reputation: 23064

Use overflow-x: auto for the content you want to scroll.

In this case the <main /> block gets its own scrollbar. The content will not go behind the header element.

body {
  margin: 0;
  background-color: red;
  flex-direction: column;
  display: flex;
  height: 100vh;
}

header {
  opacity: .3;
  background-color: yellow;
  height: 50px;
}

main {
  overflow-x: auto;
  flex: 1;
}
<body>
  <header>Header is semi-opaque</header>
  <main>
    <p><a href='#myAnchor'>Go to anchor</a> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
    <p>Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
    <p><a href='#myAnchor'>Go to anchor</a> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et earebum.
    <p><span style='color:yellow' id=myAnchor>ANCHOR</span>  Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
    <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea
    <p> rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
  </main>
</body>

Upvotes: 2

H&#229;ken Lid
H&#229;ken Lid

Reputation: 23064

Here's a trick to make the anchor seem bigger so that clicking the link will not cover the actual anchor text. Since you have negative margin, the anchor element will not take up more space. But it will have a scroll position that is 50px taller.

:target {
  padding-top: 50px; 
  margin-top: -50px
}

This uses the :target pseudo-class.

The :target CSS pseudo-class represents a unique element (the target element) with an id matching the URL's fragment.

Example: https://codepen.io/haakenlid/pen/oyarva

Upvotes: 2

Kyle Lin
Kyle Lin

Reputation: 833

Give your content container a fixed position, and set its top and height accordingly. Finally, make it scrollable with overflow: auto.

In short, just replace your #content style with this:

#content{
  position: fixed;
  top: 50px;
  height: calc(100vh - 50px);
  overflow: auto;
  flex: 1;
}

EDIT:

Here's a link to a fork of your pen with the working solution.

Upvotes: 2

Related Questions