DEV Community

Jesse Wei
Jesse Wei

Posted on

Implement a Dynamically Displayed Header With Window & Element Scroll Positions

One of the most frequently seen patterns in web design is to hide the header on page load until the user scrolls and when the page hits a specific point.

You may choose to use jQuery or vanilla Javascript to implement this but whichever tool you use, you need to be able to obtain the top/bottom positon of the target element relative to the scroll position of the page.

In this tutorial, we'll get our hands dirty and build a simple page with a header and some content. The header is not shown at page load but when the user scrolls the page to a certain point, it appears. When he/she scrolls past a certain point, the header becomes invisible again.

Table of Contents

An Overview of Our Example

This is what we'll build:

The example we'll build

The example we'll build

The header is only visible when the page is scrolled within a range.

The behavior of the header

The behavior of the header

HTML & CSS

Let's prepare our html code and give it some CSS first.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    section {
      margin-bottom: 100px;
    }
    div {
      background-color: black;
      color: white;
      display: flex;
      justify-content: center;
      padding: 300px 0;
    }
    ul {
      list-style: none;
      display: flex;
      justify-content: flex-end;
      margin: 0;
      padding: 0;
    }
    li a {
      display: block;
      padding: 20px;
    }
    header {
      width: 100%;
      visibility: hidden;
      opacity: 0;
      position: fixed;
      top: 0;
      left: 0;
      background-color: white;
      transition: all 0.5s ease-in-out;
    }
    header.show {
      visibility: visible;
      opacity: 1;
    }
  </style>
</head>
<body>
  <header>
    <nav>
      <ul>
        <li>
        <a href="#section1">Section1</a>
      </li>
      <li>
        <a href="#section2">Section2</a>
      </li>
      <li>
        <a href="#section3">Section3</a>
      </li>
      </ul>
    </nav>
  </header>
  <main>
    <section id="section1">
      <div>Section 1</div>
    </section>
    <section id="section2">
      <div>Section 2</div>
    </section>
    <section id="section3">
      <div>Section 3</div>
    </section>
    <section id="section4">
      <div>Section 4</div>
    </section>
    <section id="section5">
      <div>Section 5</div>
    </section>
  </main>
  <script>
    // todo: implement header behavior.
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

At this point, nothing will happen when you scroll the page because we haven't implemented our Javascript code yet. We'll do this in the next steps.

Query and Store Elements

Let's start by querying and storing the relevant elements in variables as we'll need them later.

...
<script>
  const section2 = document.querySelector('#section2')
  const section3 = document.querySelector('#section3')
  const header = document.querySelector('header')
</script>
Enter fullscreen mode Exit fullscreen mode

Next comes one of the key steps in our implementation. We want to add the show class name to our header when the top of section2 hits the top of the window as we scroll and remove the class name when the bottom of section3 reaches the top of the window. To achieve this, we need 3 values:

  1. The y position of the top of section2
  2. The y position of the bottom of section3
  3. How much the document is scrolled

To obtain value 1 and 2, I'm going to call the getBoundingClientRect() method on section2 and section3 elements and to get value 3, I'll use scrollY of the window object.

Element.getBoundingClientRect()

This method returns an object containing the following properties:

  • left: pixels from the left of the viewport to the left of the element
  • top: pixels from the top of the viewport to the top of the element
  • right: pixels from the left of the viewport to the right of the element
  • bottom: pixels from the top of the viewport to the bottom of the element
  • x: equal to the value of left
  • y: equal to the value of top
  • width: width of the element including padding and border
  • height: height of the element including padding and border

You can see that this method is very powerful because we can get the position and size of an element in one shot. Check out MDN to learn more about it.

We'll need the top value of section2 and bottom value of section3. Add the following code:

...
<script>
  ...
  // add these
  const {top: top_} = section2.getBoundingClientRect()
  const {bottom: bottom_} = section3.getBoundingClientRect()
</script>
Enter fullscreen mode Exit fullscreen mode

window.scrollY

This property of the window object gives us the number of pixels the document is scrolled vertically.

To achieve the expected behavior of our header, we'll check window.scrollY against the top_ and bottom_ to decide whether to add or remove the show class name in the next step.

Event Listener

Now it's time to wire up our scroll event listener.

...
<script>
  ...
  // add these
  window.addEventListener('scroll', function() {
    if (window.scrollY >= top_ && window.scrollY <= bottom_) {
      if (!header.classList.contains('show')) {
        header.classList.add('show')
      }
    } else {
      if (header.classList.contains('show')) {
        header.classList.remove('show')
      }
    }
  })
</script>
Enter fullscreen mode Exit fullscreen mode

And the full Javascript code looks like this.

<script>
  const section2 = document.querySelector('#section2')
  const section3 = document.querySelector('#section3')
  const header = document.querySelector('header')

  const {top: top_} = section2.getBoundingClientRect()
  const {bottom: bottom_} = section3.getBoundingClientRect()

  window.addEventListener('scroll', function() {
    if (window.scrollY >= top_ && window.scrollY <= bottom_) {
      if (!header.classList.contains('show')) {
        header.classList.add('show')
      }
    } else {
      if (header.classList.contains('show')) {
        header.classList.remove('show')
      }
    }
  })
</script>
Enter fullscreen mode Exit fullscreen mode

That's it. Open the page in your browser and make sure it works as expected. If anything goes wrong, double check your code.

Conclusion

In this tutorial, we built a simple page with a dynamic header that appears and disappears based on the scroll position of the document.

At the core of the implementation are the Element.getBoundingClientRect() method and the window.scrollY property. You can also make use of them to implement things like back-to-top button which only appears when user scrolls past a certain point.

Top comments (0)