How to implement the scroll progress bar
Tags: css, animation, vue3, scrollprogressbar
Published: 2021-09-22 07:11:49

---
title: How to implement the scroll progress bar
published: true
description: 
tags: CSS, Animation, Vue3, ScrollProgressBar
//cover_image: https://direct_url_to_image.jpg
---

[Demo](https://code.lencx.tech/) - 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

```html
<div id="scroll_progress_bar"></div>
```

```css
#scroll_progress_bar {
  position: fixed;
  top: 0;
  width: 0%;
  height: 4px;
  background: #7983ff;
  z-index: 10000;
}
```

```js
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}%`;
});
```

## ✍️ Implement - Vue3

```vue
<!-- 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>
```

## 🎥 Demo

![scroll_progress_bar](https://user-images.githubusercontent.com/16164244/133965898-4994f377-68de-4c6c-8d19-d00a61625c06.gif)

---

## 🔗 Reference

- [[MDN] Document: scroll event](https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event) - The scroll event fires when the document view has been scrolled.
- [[MDN] MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) - The `MutationObserver` interface provides the ability to watch for changes being made to the `DOM tree`.
- [Vue3 API](https://v3.vuejs.org/)
  - [props](https://v3.vuejs.org/api/options-data.html#props)
  - [\<script setup\>](https://v3.vuejs.org/api/sfc-script-setup.html#basic-syntax)
  - [Lifecycle Hooks](https://v3.vuejs.org/api/composition-api.html#lifecycle-hooks)
  - [defineProps and defineEmits](https://v3.vuejs.org/api/sfc-script-setup.html#defineprops-and-defineemits)
  - [defineExpose](https://v3.vuejs.org/api/sfc-script-setup.html#defineexpose)