Last week we put out a challenge to build out a scroll-spy navbar. Scroll-spy navbars are great and highlights the navbar menu when a specific element or portion of the page comes into view. Are you yet to take the challenge? Do so and showcase your submission in the comment section of the post or on Twitter using the hashtag #scotchchallenge to receive reviews. This challenge can be completed using any language, tool, technique or technology.

In this post we shall be solving this challenge by implementing the scroll-spy on the provided base code using Vanilla JavaScript and in-view.js. This will be achieved in about 11 lines of code.

In-view.js is a JavaScript utility which notifies us when a DOM element enters or exits a viewport.

The Base

Base code was provided for this challenge which consisted of HTML and CSS code to deliver the base structure and style of the page.

The HTML

In the HTML, a nav element is used to create the navbar and a section element houses the body of the page. The body contains headers of different sizes created with h1 and h2 elements. Bulma classes were used to style the various headers and the navbar.

<nav class="navbar is-info is-fixed-top">
<div class="container">
  <div class="navbar-menu">  
  <div class="navbar-start">
    <a class="navbar-item" href="#one" id="link-one">One</a>
    <a class="navbar-item" href="#two"  id="link-two">Two</a>
    <a class="navbar-item" href="#three" id="link-three">Three</a>
    <a class="navbar-item" href="#four" id="link-four">Four</a>
    <a class="navbar-item" href="#five" id="link-five">Five</a>
    <a class="navbar-item" href="#six" id="link-six">Six</a>
  </div>
  </div>
</div>
</nav>

<section class="section">
  <div class="container">
    <h1 class="title">The List</h1>

    <div class="block content">
      <h2 class="title" id="one">One</h2>
      <p>Lorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset</p>  
      <h3>Sub Chakra</h3>
      <p>glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouwsLorem ipsum sit dolor aset glouws</p>
    [...]
    </div>
</section>    

The CSS

While Bulma classes have been used to style the page, the padding-top property of the body element is styled to accomodate the fixed navbar.

body {
  padding-top: 3.25rem;
}

No JavaScript was included in the base code provided.

The Technique

In-view.js provides a seamless way to handle elements when they enter and exit the viewport, most likely during scroll. Once we scroll to a section, the class list of corresponding navbar menu is updated. Bulma provides an is-active class which highlights a menu item when active. This class will be toggled on and off whenever a menu item enters or leaves the viewport respectively.

Installing in-view

While in-view can be installed using the node.js package manager npm, we shall be importing the script from a CDN. In the codepen, add a new external script from here.

Implementing Spy-Scroll

Using the on method in in-view, we state actions which will be carried out when an element enters or exits the viewport. The on method takes two parameters with the first being either enter or exit and the second is a callback function with an argument which is the element entering or leaving the viewport. Also, it is necessary to minimize the viewport area being watched so, we don't have two elements in the viewport at a time. This is achieved by specifying an offset of the viewport using the offset() method.

Note that in this demo, only the headers are tracked during scroll and subsequent use cases may require tracking the whole content of a section during scroll.

While registering these methods on a single DOM element, we would like to spy on all the headers of the menu items at once. This will be achieved using the forEach() method to traverse the elements and apply an in-view function to each element. This is done with:

// In-view function
const handleView = item => {
  const linkEl = document.querySelector(`#link-${item}`);

  let offsetHeight = 0.6*(window.innerHeight)
  inView.offset({
    bottom:offsetHeight
  });

  inView(`#${item}`)
    .on("enter", () => linkEl.classList.add('is-active'))
    .on("exit", el  => linkEl.classList.remove('is-active'))
};

// Apply method on each DOM element 
["one", "two", "three", "four", "five", "six"].forEach(handleView);

Once we created a function which takes an argument of the unique identifier in each DOM element, we apply the in-view method and used the forEach() method to spread the function across the elements. Zero rocket science! Also, it can be seen that the viewport is offset by 60% from the bottom, this means that the header has to enter the top 40% of the page to be considered in view.

Here is the final product:

Conclusion

In this post, we successfully completed the challenge by implementing the scroll-spy on the navbar using in-view.js and an is-active Bulma class to highlight the navbar. This is just a simple use case of in-view.js, feel free to try it out in other cool UI tricks and techniques while building awesome stuff. Feel free to leave your comments and feedback in the comment section and let's watch out for the next challenge. Happy coding!!