13

I'm using the awesome Headroom.js plugin to build an auto-hiding header. The header is static from start and becomes pinned after an offset (when scrolling down) and then back to static (when it's back to the top).

Here is what I have done: http://codepen.io/netgloo/pen/KmGpBL

but I got 2 problems:

  • scrolling down from top: when the header becomes pinned, I see it slides down and suddenly slides up
  • scrolling up from middle page: when the header arrives to the offset it disappears, but I need keeping it pinned to the top

Someone could give me some help or ideas? Thanks

Here is how I initialize the plugin:

var myElement = document.querySelector("header");

var headroom  = new Headroom(myElement, {
  "offset": 150,
  "tolerance": 0,
});

headroom.init();
Fred K
  • 13,249
  • 14
  • 78
  • 103

1 Answers1

9

The headroom.js script mainly handles the addition/removal of some classes for you. It's up to you to add the appropriate styles to achieve the effect you want. Let's start with the simplest part, the HTML:

HTML

<header>
  Header
</header>

That's it!

Now for the JS setup:

JS

var myElement = document.querySelector("header");

var headroom  = new Headroom(myElement, {
  "offset": 220,
  "tolerance": {
    up: 0,
    down: 0
  },
  "classes": {
    "initial": "header--fixed",
    "pinned": "slideDown",
    "unpinned": "slideUp",
    "top": "top",
    "notTop" : "not-top",
  }
});

headroom.init();

The first line selects the header element. The second creates a new Headroom object using the configuration values. I've set the values based on the effect it sounds like you're trying to achieve - the header should slide away when the page is scrolled down quickly, and should slide into view when the page is scrolled up.

The offset of 205px is the distance from the top when the header can become unpinned.

The tolerance of 5px is the scroll tolerance before the state changes.

And finally we're going to define the classes that will be added to the element for the different states. At the start the element will be assigned a class of header--fixed. When pinned, the element will receive the additional slideDown class. And when unpinned the element will receive the additional slideUp class.

The final line initializes the headroom object.

Using the state-based classes we can now create the CSS needed to achieve the effect you want.

CSS

We'll start with the .header--fixed class:

.header--fixed {
  position: absolute;
  top: 0; 
  width: 100%; 
}

main {
  padding-top: 110px;
}

This sets the initial placement of the header (at the top) and sets a padding for the main content of the page so it's not covered by the header.

Next we need to define the styles for various states - .top, .not-top, .slideDown and .slideUp:

.header--fixed.top {
  transition: none;
  transform: translateY(0);
}
.header--fixed.not-top {
  position: fixed;
  transform: translateY(-100%);
}
.header--fixed.slideDown.not-top {
  transition: transform 0.3s ease-in-out;
  transform: translateY(0);
}
.header--fixed.slideDown.top {
  transition: transform 0.3s ease-in-out;
  position: fixed;
}
.header--fixed.slideUp.not-top {
  transition: transform 0.3s ease-in-out;
  transform: translateY(-100%);
}
.header--fixed.slideUp.top {
  transform: translateY(-100%);
  position: absolute;
}

Most of these styles are related to setting the position and transition for each state. In short, the .not-top class is applied when the page is scrolled below the header. .top is applied when the page is scrolled near the top (within the height of the header).

In addition to this critical CSS you would need the CSS for the styling of the header - a background color, font, etc. This could be applied by targeting the header element, or header--fixed class.

The final piece, and crux of the problem is resetting the header when the page is scrolled back to the very top - ie a window.pageYOffset of 0. When the page reaches this point, we need to remove the .slideDown class so the header scrolls with the page.

window.addEventListener('scroll', function() {
  if (window.pageYOffset === 0) {
    myElement.classList.remove('slideDown')
  }
})

The Full Code

Putting it all together we get this:

// Headroom.js
// https://github.com/WickyNilliams/headroom.js
var myElement = document.querySelector("header");

var headroom  = new Headroom(myElement, {
  "offset": 220,
  "tolerance": {
    up: 0,
    down: 0
  },
  "classes": {
    "initial": "header--fixed",
    "pinned": "slideDown",
    "unpinned": "slideUp",
    "top": "top",
    "notTop" : "not-top",
  }
});

headroom.init();

// When the page is at the top, remove the slideDown class.
window.addEventListener('scroll', function() {
  if (window.pageYOffset === 0) {
    myElement.classList.remove('slideDown')
  }
})
.header--fixed {
  position: absolute;
  top: 0;
  width: 100%;
}
.header--fixed.top {
  transition: none;
  transform: translateY(0);
}
.header--fixed.not-top {
  position: fixed;
  transform: translateY(-100%);
}
.header--fixed.slideDown.not-top {
  transition: transform 0.3s ease-in-out;
  transform: translateY(0);
}
.header--fixed.slideDown.top {
  transition: transform 0.3s ease-in-out;
  position: fixed;
}
.header--fixed.slideUp.not-top {
  transition: transform 0.3s ease-in-out;
  transform: translateY(-100%);
}
.header--fixed.slideUp.top {
  transform: translateY(-100%);
  position: absolute;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  text-align: center;
}

header {
  background: #4ECDC4;
  padding: 40px;
  font: normal 30px/1 sans-serif;
}

main {
  padding: 110px 0 0 0;
}
<script src="https://unpkg.com/headroom.js"></script>
<header>
  Header
</header>

<main>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
  <p>Lorem ipsum 1</p>
  <p>Lorem ipsum 2</p>
  <p>Lorem ipsum 3</p>
  <p>Lorem ipsum 4</p>
  <p>Lorem ipsum 5</p>
  <p>Lorem ipsum 6</p>
  <p>Lorem ipsum 7</p>
  <p>Lorem ipsum 8</p>
  <p>Lorem ipsum 9</p>
  <p>Lorem ipsum 10</p>
  <hr>
</main>

And with that we have everything we need. For a working example with the CSS done in SCSS like your example, see this Codepen.

Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184
  • Hi Brett, very clear and good except for one thing: the effect I wanted is with the header `static` at initial state. In your example, instead, the header is always `fixed` from the start. Could you help me about that? – Fred K Jun 07 '17 at 07:04
  • You can see this behavior in the site mentioned in first post: www.chandeliercreative.com – Fred K Jun 07 '17 at 07:07
  • Ah, I see now. The answer has been updated, and Codepen too. – Brett DeWoody Jun 07 '17 at 12:48
  • @FredK I made some minor improvements to when/how the header scrolls with the page. – Brett DeWoody Jun 09 '17 at 14:54
  • Hi Brett! Thanks for the new answer, I'm going to test it and let you know – Fred K Jun 09 '17 at 15:11
  • Hi Brett, wow very good job! I think it's needed only some fine tuning because I see this strange behavior: when you scroll up (or down) around the offset (in the codepen is at 110px) the header enters (or exit) without the transition. I recorded a video of this bug here: https://s7.postimg.org/g0i64mknv/image.gif – Fred K Jun 09 '17 at 16:14
  • I noticed that, in the demo here on SO I increased the offset to 220px to eliminate that issue. It could be increased even more if needed. It might also need some work on the transition time to fix it but its close enough you should be able to take it the rest of the way. – Brett DeWoody Jun 09 '17 at 17:19
  • Hi Brett, the bug is still there also increasing the offset (see here https://codepen.io/anon/pen/xrVxxy). If you can help me to solve that it'll be appreciated, otherwise thanks for all! – Fred K Jun 10 '17 at 13:54
  • Looks like the solution is to add a `transition` to the `.header--fixed.slideDown.top` class. I've made the change in the demo above. – Brett DeWoody Jun 10 '17 at 14:15
  • I'm back here but your solution doesn't work, check your Codepen too. The header stays static at beginning of scroll but after a while it appears and abruptly hides. You know how fix it? – Fred K Jun 24 '21 at 07:45