Let’s re-implement the progress bar on page scroll effect that you may have seen on many popular blogs including this one.
To implement this, we would be making use of React, styled-components and the Document
onScroll event. So, without further ado, let’s begin.
As stated, we’ll be using React
and styled-components
to implement the progress scroll effect. In order to save time, we’ll make use of Create React App to bootstrap a quick React application for us.
So open your terminal and run the following commands:
$ create-react-app app-name
$ cd app-name
Don’t have `create-react-app` installed? Then you can use this command $ npx create-react-app app-name
Alright so next up we need to also install styled-components
to our newly created project. So while in the project directory, run
$ npm install styled-components -S
Now if you run npm start
in our terminal, you should see something like this:
So, now that our application is setup, let’s start writing some actual code. We’ll begin by building our page layout and writing some CSS to style the layout.
Navigate to your src/App.js
file and then delete everything in the file and add the following lines of code:
import React, { Component } from 'react';
import './App.css';
export default class App extends Component {
render() {
return (
<div className="App">
<header></header>
<main>
<h1>Lorem Ipsum</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p>...more paragraphs of text, enough so that the page gets a scrollbar</p>
</main>
</div>
);
}
}
Basically what we just did is convert the stateless functional component that create-react-app
generated for us to a class-based component. We also added some dummy text content to fill up the page.
Cool now to add some simple styling to the layout. Open the src/App.css
and delete everything in it then add the following CSS lines to it.
header {
background: linear-gradient(to right, #302E24, #28313E);
height: 60px;
width: 100%;
box-shadow: 0 2px 20px 0 rgba(0, 0, 0, .1);
margin-bottom: 70px;
}
.App p, .App h1 {
margin: 30px auto;
width: 800px;
}
Now running npm start
, you should then see something similar to this:
Okay so the next thing on our agenda would be to design the progress bar. To implement this, we will be making use of the styled-components
library we installed.
let’s create a file named Progress.js
in our src folder. Once that is done, add the following lines of code to the file:
import styled from 'styled-components';
const Progress = styled.div`
position: fixed;
background: linear-gradient(
to right,
rgba(250, 224, 66, .8) ${props => props.scroll},
transparent 0);
width: 100%;
height: 4px;
z-index: 3;
`;
export default Progress;
Now, let’s head back to our App.js
file and make use of the newly created Progress
component:
import React, { Component } from 'react';
import './App.css';
import Progress from './Progress';
export default class App extends Component {
render() {
return (
<div className="App">
<Progress scroll="70%" />
{/* ... */}
</div>
);
}
}
Alright!, I guess this is a good time to check how we’re doing so far. So let’s run npm start
once again and check out our application. If all goes well, you should have something like this:
Here comes the fun part, where we implement the scroll logic. Before we start writing the actual code, lets outline the steps that are required to achieve this:
document
objectAlright let’s convert the pseudocode to actual code
Okay so in our App.js
file, lets create a method called listenToScrollEvent
that will listen for scroll events on the DOM. This method would look a little like this:
listenToScrollEvent = () => {
document.addEventListener("scroll", () => {
requestAnimationFrame(() => {
// Calculates the scroll distance
this.calculateScrollDistance();
});
});
};
The method simply listens for any scroll event that takes place on the Document
object and then invokes requestAnimationFrame, which notifies the browser that an animation is about to take place. The browser then calls the calculateScrollDistance
method that was specified in the requestAnimationFrame
.
One final step we need to do is call this method whenever the App
component has mounted. So we take advantage of one of React’s lifecycle methods, componentDidMount
, to call the listenToScrollEvent
method.
componentDidMount() {
this.listenToScrollEvent();
}
Now let’s take care of defining the calculateScrollDistance
method.
To calculate the user’s current scroll distance, we need to do a few things:
To calculate the current user’s scrolled distance, we can make use of the pageYOffset
value available on the windows
object.
So let’s create a calculateScrollDistance
method and add the following line to the method:
calculateScrollDistance = () => {
const scrollTop = window.pageYOffset;
}
In case of older IE browsers, the following should also work document.body.scrollTop
Next we need to get the browser’s height and to do that, we can make use of the innerHeight
value that can be accessed via the window
object.
The browser’s height simply refers to the height of the viewable browser (i.e Chrome or Firefox) area.
So back into our calculateScrollDistance
, let’s add the line that gets the browser window height.
calculateScrollDistance = () => {
const scrollTop = window.pageYOffset;
const windowHeight = window.innerHeight;
}
In case of older IE browsers, the following should also work document.body.clientHeight
Now getting the document’s height is tricky and the reason behind this is because various browsers have different ways in which they interpret or calculate the height of a document.
To bypass this, we need to check different properties that various browsers use to get the document height and make use of Math.max()
to get the highest value.
So let’s implement this quickly by creating a method called getDocHeight
and add the following lines of code:
getDocHeight = () => {
return Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
}
Then we call the method in our calculateScrollDistance
method like so:
calculateScrollDistance = () => {
const scrollTop = window.pageYOffset;
const windowHeight = window.innerHeight;
const docHeight = this.getDocHeight();
}
Now that we have all the values we require, we can calculate the percentage a user has scrolled by dividing the scrollTop
from the total available scroll length of the document (docHeight - winHeight) and multiply the result by 100 to get the result in percentage.
So add the following lines into our code:
calculateScrollDistance = () => {
const scrollTop = window.pageYOffset;
const windowHeight = window.innerHeight;
const docHeight = this.getDocHeight();
const totalDocScrollLength = docHeight - winHeight;
const scrollPostion = Math.floor(scrollTop / totalDocScrollLength * 100)
}
Now that we have scroll position, we need to add a state
variable which we can then update with the current scroll position of the user. To do that, we need to create a state object in your src/App.js
file and add a scrollPosition
and then set the inital state to 0.
// ...
state = {
scrollPosition: 0
}
Back into our calculateScrollDistance
method, we then need to make use of the setState
method given by React which will help update the state of our scrollPosition
:
calculateScrollDistance = () => {
// ...
this.setState({
scrollPosition,
});
}
So the final code will look something like this:-
// ...
state = {
scrollPosition: 0
}
calculateScrollDistance = () => {
const scrollTop = window.pageYOffset;
const winHeight = window.innerHeight;
const docHeight = this.getDocHeight();
const totalDocScrollLength = docHeight - winHeight;
const scrollPostion = Math.floor(scrollTop / totalDocScrollLength * 100);
this.setState({
scrollPostion,
});
}
// ...
Finally the last thing to do is to simply pass our scrollPosition
state to our Progress
bar component.
{/* ... */}
<Progress scroll={ this.state.scrollPostion + '%' } />
So the final complete code would look something like this:-
import React, { Component } from 'react';
import Progress from './Progress';
import './App.css';
export default class App extends Component {
state = {
scrollPostion: 0
}
listenToScrollEvent = () => {
document.addEventListener("scroll", () => {
requestAnimationFrame(() => {
this.calculateScrollDistance();
});
});
}
calculateScrollDistance = () => {
const scrollTop = window.pageYOffset; // how much the user has scrolled by
const winHeight = window.innerHeight;
const docHeight = this.getDocHeight();
const totalDocScrollLength = docHeight - winHeight;
const scrollPostion = Math.floor(scrollTop / totalDocScrollLength * 100)
this.setState({
scrollPostion,
});
}
getDocHeight = () => {
return Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
}
componentDidMount() {
this.listenToScrollEvent();
}
render() {
return (
<div className="App">
<Progress scroll={this.state.scrollPostion + '%'} />
<header></header>
<main>
<h1>Lorem Ipsum</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p>
...
</p>
</main>
</div>
);
}
}
And with all of this in place, you should now have a working progress bar on page scroll!
Whew! 🤗 There you have it folks, we successfully created the progress scroll effect. Hopefully you find it useful!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!