DEV Community

lencx
lencx

Posted on

How to implement the scroll progress bar

Demo - Code Snippets

Creates a progress bar indicating the scroll percentage of the page.

  • Use position: fixed and a large z-index value to place the element at the top of the page and above any content.
  • Use EventTarget.addEventListener() along with Element.scrollTop to determine the scroll percentage of the document and apply it to the width of the element.

💠 Core

<div id="scroll_progress_bar"></div> 
Enter fullscreen mode Exit fullscreen mode
#scroll_progress_bar { position: fixed; top: 0; width: 0%; height: 4px; background: #7983ff; z-index: 10000; } 
Enter fullscreen mode Exit fullscreen mode
const scrollProgress = document.getElementById('scroll_progress_bar'); const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; window.addEventListener('scroll', () => { const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; scrollProgress.style.width = `${(scrollTop / height) * 100}%`; }); 
Enter fullscreen mode Exit fullscreen mode

✍️ Implement - Vue3

<!-- ScrollProgress.vue --> <script setup lang="ts"> import { ref, onMounted, onUnmounted } from 'vue' const props = defineProps({ root: { type: String, default: '#app', required: false, }, height: { type: String, default: '4px', required: false, }, theme: { type: String, default: '#3eaf7c', required: false, validator: (v: string) => { document.head.style.color = v const q = document.head.style.color document.head.style.color = '' return !!q }, }, placement: { type: String, default: 'top', required: false, validator: (v: string) => { if (!['top', 'bottom'].includes(v)) { console.error(`[ScrollProgress(placement)] The value must match one of these strings: 'top' | 'bottom'`) return false } return true }, }, zIndex: { type: [Number, String], default: 10000, required: false, validator: (v: string) => /^-?[\d]+$/.test(v), }, }) const el = ref(null) const appHeight = ref(0) onMounted(() => { const targetNode = document.querySelector(props.root) if (!targetNode) return console.error(`[ScrollProgress(root)] '${props.root}' is invalid`) const config = { attributes: true, childList: false, subtree: true } const observer = new MutationObserver((mutationsList: MutationRecord[]) => { // Use traditional 'for loops' for IE 11 for(let mutation of mutationsList) { if (mutation.type === 'attributes') { appHeight.value = document.documentElement.scrollHeight } } }) observer.observe(targetNode, config) }) const listener = () => { const scrollProgress = el.value const height = appHeight.value - document.documentElement.clientHeight const scrollTop = document.body.scrollTop || document.documentElement.scrollTop scrollProgress.style.width = `${(scrollTop / height) * 100}%` } onMounted(() => window.addEventListener('scroll', listener)) onUnmounted(() => window.removeEventListener('scroll', listener)) const style: any = { background: props.theme, zIndex: props.zIndex, height: props.height, } if (props.placement === 'top') style.top = 0 if (props.placement === 'bottom') style.bottom = 0 defineExpose({ style }) </script> <template> <div id="scroll_progress" ref="el" :style="style" /> </template> <style scoped> #scroll_progress { position: fixed; width: 0%; transition: width 300ms ease-out; } </style> 
Enter fullscreen mode Exit fullscreen mode

🎥 Demo

scroll_progress_bar


🔗 Reference

Top comments (0)