Detect current section on scroll

Scroll menu horizontal

Today I created a menu that highlighted an item depending on the scroll position.

This was the starting point:

HTML
<div class = 'Menu'>    
    <div class = 'Item'>Section A</div>
    <div class = 'Item'>Section B</div>
    <div class = 'Item'>Section C</div>
</div>
<div class = 'Content'>
    <div id = 'A'>Bla bla bla</div>
    <div id = 'B'>Ble ble ble</div>
    <div id = 'C'>Bli bli bli</div>
</div>

I wanted the menu to be sticky:

CSS
.Menu{
    display: flex;
    position: sticky;
    top: 0;
}
.Menu .Item.Highlight{
    border-bottom: 3px solid black;
}

The goal was that, if you scrolled into section A, B or C, the item on the menu highlights accordingly.

How to detect that you arrive into a specific section?

On every scroll, you need to calculate the content block which has a shortest and positive distance to the menu:

javascript
const scrollHandler = () => {

    let menu = document.querySelector('.Menu')

    let A = document.getElementById('A')
    let B = document.getElementById('B')
    let C = document.getElementById('C')

    let pos_menu = window.pageYOffset + menu.offsetHeight

    let pos_A = A.offsetTop + A.offsetHeight
    let pos_B = B.offsetTop + B.offsetHeight
    let pos_C = C.offsetTop + C.offsetHeight
    
    let distance_A = pos_A - pos_menu
    let distance_B = pos_B - pos_menu
    let distance_C = pos_C - pos_menu

    let min = Math.min(...[distance_A, distance_B, distance_C].filter(num => num > 0))

    document.querySelectorAll('.Menu .Item')[0].classList.remove('Highlight')
    document.querySelectorAll('.Menu .Item')[1].classList.remove('Highlight')
    document.querySelectorAll('.Menu .Item')[2].classList.remove('Highlight')

    if(min === distance_A) document.querySelectorAll('.Menu .Item')[0].classList.add('Highlight')
    if(min === distance_B) document.querySelectorAll('.Menu .Item')[1].classList.add('Highlight')
    if(min === distance_C) document.querySelectorAll('.Menu .Item')[2].classList.add('Highlight')

}

Now attach the function to a scroll listener:

javascript
window.addEventListener('scroll', scrollHandler)

Bonus

To add the smooth animation, use element.scroll():

javascript
// You'll need to replace element with document.querySelectorAll('.Menu .Item')[n]
element.scroll({left: element.offsetLeft, behavior: 'smooth'})

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.