The HTML5 progress element

Posted October 14, 2016

Taken by Matthew Kane

Something I’ve learned that wasn’t included in the Learn curriculum was the progress element. Now what that does is show the progress of a task. In the case of this page. It shows how far down the page a user has scrolled. This can be beneficial to a long webpage such as a blog post. Another reason one could use the progress element is to show a loading bar for something.

The HTML is pretty simple in itself, just set a max value and you're done

1<body>
2 <progress value="0" max="100"></progress>
3 ...the rest of your content
4</body>

The CSS is straight forward as well but you have to remember to also clear out some vendor prefixed psuedo elements on the progress bar

1progress::-webkit-progress-bar {
2 background-color: transparent;
3}
4
5progress::-webkit-progress-value {
6 background-image: linear-gradient(135deg, #52e5e7 0%, #130cb7 100%);
7}
8
9progress::-moz-progress-bar {
10 background-image: linear-gradient(135deg, #52e5e7 0%, #130cb7 100%);
11}
12
13progress {
14 position: fixed;
15 left: 0;
16 top: 0;
17 z-index: 2;
18 width: 100%;
19 height: 3px;
20 appearance: none;
21 background: none;
22 border: none;
23}

And now the fun part, setting the value based on your scroll distance

1function youReadThisMuch() {
2 const scroll = window.pageYOffset; // window.scrollY isnt supported in IE
3 const bodyHeight = document.body.offsetHeight;
4 const windowHeight = window.innerHeight;
5 const scrollPercent = (scroll / (bodyHeight - windowHeight)) * 100;
6 document.querySelector('progress').value = scrollPercent;
7}
8
9window.addEventListener('scroll', youReadThisMuch);

Setting this up in React is also super simple

1class Progress extends React.Component {
2 state = { progress: 0 };
3 }
4
5 // add the listener when the component mounts
6 componentDidMount() {
7 window.addEventListener('scroll', this.handleScroll, { passive: true });
8 }
9
10 // remove the listener when the component unmounts
11 componentWillUnmount() {
12 window.removeEventListener('scroll', this.handleScroll);
13 }
14
15 handleScroll = () => {
16 const scroll = window.pageYOffset; // window.scrollY is less supported
17 const bodyHeight = document.body.offsetHeight;
18 const windowHeight = window.innerHeight;
19 const scrollPercent = (scroll / (bodyHeight - windowHeight)) * 100;
20 const maxMinscroll = Math.min(100, Math.max(0, scrollPercent));
21 this.setState({ progress: maxMinscroll });
22 }
23
24 render() {
25 return (
26 <progress value={this.state.progress} max="100">
27 // for styling im using styled-jsx but you should be able to do the same with another css-in-js method
28 <style jsx>{
29 progress::-webkit-progress-bar {
30 background-color: transparent;
31 }
32
33 progress::-webkit-progress-value {
34 background-image: linear-gradient(135deg, #52E5E7 0%, #130CB7 100%);
35 }
36
37 progress::-moz-progress-bar {
38 background-image: linear-gradient(135deg, #52E5E7 0%, #130CB7 100%);
39 }
40
41 progress {
42 position: fixed;
43 top: 0;
44 left: 0;
45 width: 100%;
46 z-index: 2;
47 height: 3px;
48 appearance: none;
49 background: none;
50 border: none;
51 }
52 }</style>
53 </progress>
54 );
55 }
56}
57
58export default Progress;

React Hooks!!!!

1// useScrollProgress.js
2import { useState, useEffect } from 'react';
3const useScrollProgress = () => {
4 const [scrollProgress, setScrollProgress] = useState(0); // initialize the value
5 const handleScroll = () => {
6 const scroll = window.pageYOffset;
7 const bodyHeight = document.body.offsetHeight;
8 const windowHeight = window.innerHeight;
9 const scrollPercent = (scroll / (bodyHeight - windowHeight)) * 100;
10 const maxMinscroll = Math.min(100, Math.max(0, scrollPercent));
11 setScrollProgress(maxMinscroll);
12 };
13
14 useEffect(() => {
15 window.addEventListener('scroll', handleScroll, { passive: true });
16 handleScroll(); // call it right away so the scroll position is set on page refresh
17 return () => {
18 window.removeEventListener('scroll', handleScroll); // make sure to remove the listener on umount
19 };
20 }, []); // passing an empty array as the second arg to useEffect will make sure this code is only run on mount
21 return scrollProgress;
22};
23
24export default useScrollProgress;
1// Layout.js
2import React from 'react';
3import styled from 'styled-componenets';
4
5const ScrollProgress = styled.progress`
6 position: fixed;
7 top: 0;
8 left: 0;
9 width: 100%;
10 z-index: 2;
11 height: 3px;
12 appearance: none;
13 border: none;
14 background: none;
15 &::-webkit-progress-bar {
16 background-color: transparent;
17 }
18 &::-webkit-progress-value {
19 background-image: linear-gradient(135deg, #52e5e7 0%, #130cb7 100%);
20 }
21 &::-moz-progress-bar {
22 background-image: linear-gradient(135deg, #52e5e7 0%, #130cb7 100%);
23 }
24`;
25
26const Layout = ({ children }) => {
27 const scrollProgress = useScrollProgress();
28 return (
29 <div>
30 <ScrollProgress value={scrollProgress} max={100} />
31 </div>
32 );
33};