Search code examples
javascriptprogress-barscrollbar

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


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


Solution

  • 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