Vanilla JavaScript to show scroll progress bar that fills only while scrolling the <main> element?

25 Views Asked by At

I'm trying to create a scroll progress bar that fills from 0-100% when scrolling the element only, but i can't get it to work. I've got it working to show the whole page scrolling progress when scrolling the element, but I need it to only show 0-100% on the element, and only while that element is being scrolled. I also need to create it using vanilla javascript.

Here's what i have that isn't working:

HTML

<body>
    <header>
        <section>
        </section>
    </header>
    <nav>
        <div class="nav"></div>
        <div class="scroll">
                <div class="scroll-bar" id="scroll-progress"></div>
        </div>
    </nav>
    <main>
        <section>
            <p>some content</p>
        </section>
        <section>
            <p>some content</p>
        </section>
    </main>
    <section>
        <p>some content</p>
    </section>
    <section>
        <p>some content</p>
    </section>
    
</body>

CSS

.nav {
    background-color: black;
    height:50px;
}
section {
    height: 400px;
}

.scroll {
    width: 100%;
    height: 4px;
    background-color: lightgray;
}

#scroll-progress {
    width: 0%;
    height: 4px;
    background-color: green;
}

JS

const main = document.querySelector('main');
const progress = () => {
    const scroll = main.scrollTop;
    const height = main.scrollHeight - main.clientHeight;
    const scrollProgress = (scroll / height) * 100;
    document.getElementById('scroll-progress').style.width = scrollProgress + "%";
}

window.addEventListener('scroll', progress);

This, however, works as expected, but shows the progress through the whole page, while scrolling the element, I'm just not sure how to get from this to what i need.

const main = document.querySelector('main');
const progress = () => {
    let scroll = document.documentElement.scrollTop;
    let height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
    let scrollProgress = (scroll / height) * 100;
    document.getElementById("scroll-progress").style.width = scrollProgress + "%";
}

window.addEventListener('scroll', progress);

Any help greatly appreciated

1

There are 1 best solutions below

2
Jacob Belanger On BEST ANSWER

two things are missing.

  1. you need to attach the scroll handler to the main element, not the window.
  2. the main element won't scroll as-is, you need to give it either a height, or a max-height and an overflow-y: auto
.nav {
    background-color: black;
    height:50px;
}

main {
  height: 450px;
  overflow-y: auto;
}
section {
    height: 400px;
}

.scroll {
    width: 100%;
    height: 4px;
    background-color: lightgray;
}

#scroll-progress {
    width: 0%;
    height: 4px;
    background-color: green;
}

js

const main = document.querySelector('main');
const scrollProgress = document.getElementById('scroll-progress')

const progress = () => {
    const scroll = main.scrollTop;
    const height = main.scrollHeight - main.clientHeight;
    const percent = (scroll / height) * 100;
    scrollProgress.style.width = percent + "%";
}

main.addEventListener('scroll', progress);

Notice I moved the scroll-progress selector outside the process callback, so that the query only runs once. Here's a working demo: https://codepen.io/jacob_124/pen/QWzQGBP