Making a Fixed Header React to Scrolling

Creating a fixed header which dynamically reacts to scroll events isn't that difficult. You don't need a library or any kind of widget. All you need is a few lines of JavaScript and a little bit of CSS. Well... and you need a header element somewhere in your document.

Lets assume, that we already created an element with the id header. It has some basic styles and is positioned at the top of the document. Like the header I created for this website.

<header id="header">
  <!-- Some content -->
</header>
#header {
  /* Required styles */
  left: 0;
  position: fixed;
  top: 0;
  width: 100%;

  /* Just for the looks */
  background: #0088aa;
  height: 100px;
}

We need to attach an event handler to the window scroll event, to be able to react to scroll events. Please note, that on mobile phones scroll events might react differently.

var header = document.getElementById('header');
if (header) {
  window.addEventListener('scroll', function _scroll() {
    // This is where the magic happens.
  });
}

We will need to check, if the user has scrolled the website. And we will than update the header's state depending on the scrolling state. We will do this by adding a class to the header, when the website has been scrolled. If the user returns back to the top of the site, we will remove this class.

var header = document.getElementById('header');
if (header) {
  window.addEventListener('scroll', function _scroll() {
    var scrollY = window.scrollY || window.pageYOffset;
    var isScrolled = scrollY > 0;

    header.className = isScrolled ? 'scrolled' : '';
  });
}
#header.scrolled {
  /* Just for the looks */
  background: #aaeeff;
  height: 60px;
}

That's it. We have successfully created a dynamic fixed header. We are finished, right? But wait... scrolling events are fired really often and DOM manipulations are expensive. We actually just need to access the DOM, if isScrolled has changed. We can accomplish that by storing the isScrolled state in a more persistent way.

var isScrolled = false;
var header = document.getElementById('header');
if (header) {
  window.addEventListener('scroll', function _scroll() {
    var scrollY = window.scrollY || window.pageYOffset;
    if (scrollY > 0 !== isScrolled) {
      isScrolled = scrollY > 0;
      header.className = isScrolled ? 'scrolled' : '';
    }
  });
}

Now we will only react, if the is actually a change. You also might want to add a little scroll threshold, so the header doesn't change immediately. But I will leave this to you.

And to those of you thinking... "Dude, micro optimisations are evil." You are absolutely right. In this scenario optimisation doesn't make a lot of sense. But for the sake of completeness I wanted to show this, too. This might come in handy, if you do a lot more calculations in this or a similar DOM event. Have fun.