Create parallax scrolling effect with vanilla Javascript
Parallax scrolling is a really cool visual effect that is widely used in animation movies, and it can really make an outstanding, immersive experience for the users of your website.
Demo:
Some prepared images are needed for this tutorial you can find those in the GitHub repository.
Video tutorial
If you would watch a detailed step-by-step video instead you can check out the video I made covering this project on my Youtube Channel:
HTML markup
In this tutorial I'll use a Google font ("Roboto") so if you want to use it don't forget to include it in the head section of the HTML document. I'll aslo include the stylesheet that we will create later.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
<title>Document</title>
</head>
In the body of the document we will have two main sections. The first one will be the actual parallax scrolling effect and the second one will be mock page content.
Let's first create the HTML needed for the parallax scrolling effect. In this section we will add all the images that we prepared. Make sure that you give them a unique and meaningful id, because we will use these later. I'll also add a title with an id too.
<section>
<img src="./images/road.png" id="road"alt="">
<img src="./images/mountains.png" id="mountain" alt="">
<img src="./images/moon.png" id="moon" alt="">
<img src="./images/sky.jpg" id="sky" alt="">
<h1 id="midnight">Midnight</h1>
</section>
Now it's time to add the mock page section. This one will be pretty simple it only contains a heading and a paragraph. In the paragraph I only have some placeholder text (lorem ipsum).
<section class="welcome">
<h1>Midnight</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam rutrum ante ante. Donec sagittis ultricies venenatis. Proin non neque ex. Suspendisse tristique lorem ac laoreet laoreet. Maecenas viverra, erat at laoreet convallis, felis leo gravida est, nec ullamcorper nibh metus ac magna. Etiam pretium eu purus et congue. Nullam placerat nisi a pellentesque aliquam. Quisque consequat placerat arcu id dapibus. Curabitur odio ligula, accumsan nec velit ac, accumsan tempor justo.
Nulla sit amet nisl in purus viverra maximus quis et nunc. Fusce rhoncus justo a posuere aliquam. Nam fringilla mauris vitae enim mattis, non ullamcorper massa facilisis. Cras eu erat nisl. Aliquam tempus nulla ex. Aliquam tristique a nisi ac mollis. Mauris condimentum lobortis nisi eu sagittis. Aenean pharetra fringilla varius. Phasellus id nulla eget massa bibendum pellentesque et quis ligula. Suspendisse potenti. Ut rutrum, nisi at maximus suscipit, ipsum ligula egestas tellus, quis lobortis ligula mauris a erat. Duis placerat dui at neque commodo laoreet. Aenean molestie quam id nunc aliquam aliquam. Donec sed hendrerit ipsum. Phasellus vitae metus in quam iaculis tincidunt.
Nam ac ligula ac nulla rutrum convallis. Duis ac magna ex. Vestibulum efficitur justo sit amet ex finibus tempor. Aliquam nibh risus, ultrices id turpis eu, dapibus tempus eros. Nam consectetur lobortis felis. Morbi ac congue augue. Aliquam gravida magna ut nunc pretium, non accumsan justo aliquet. Morbi mauris leo, consequat non odio ut, dignissim dictum nisi. Maecenas eget neque ipsum. Phasellus condimentum risus eu cursus vestibulum.
Integer efficitur fringilla nunc id volutpat. Morbi vel mauris vitae lectus scelerisque blandit. Fusce ut libero justo. Vivamus vitae finibus nulla. Nulla at enim vulputate diam dignissim ullamcorper sollicitudin ut nunc. Vestibulum ultrices hendrerit urna, vel tincidunt leo faucibus sit amet. Morbi vel leo ac diam euismod commodo. Nulla a interdum lectus, eu laoreet tellus. In tempus, nisl id posuere mattis, erat odio vestibulum elit, in consectetur magna massa at libero. Integer dapibus vulputate convallis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer in risus maximus tortor commodo aliquet id ac quam. Cras ac venenatis ipsum. Fusce sit amet hendrerit lorem.
Quisque leo dui, cursus at nisi scelerisque, ultrices iaculis augue. Morbi dapibus tellus sem, sed maximus eros scelerisque sit amet. Sed eget leo viverra, dictum neque at, posuere urna. Aliquam laoreet quam ac est malesuada, eu blandit dolor sollicitudin. Aliquam erat volutpat. Duis mi erat, semper ac finibus sit amet, consequat a augue. Curabitur diam diam, malesuada quis aliquam ut, laoreet nec augue. Donec non ipsum a turpis sollicitudin rhoncus in id arcu.</p>
</section>
<script src="index.js"></script>
Don't forget to add the index.js file at the last line of the body. And this is all the HTML markup that we need for this project.
CSS implementation
First I'll remove any browser default paddings and margins and make the website take the full width and height of the viewport. I'll also set the font to be the included Roboto Google Font.
* {
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
max-width: 100vw;
font-family: 'Roboto', sans-serif;
}
Next I'll set the positioning of the sections to be relative, so we can position our images absolutely relative to the section. I'll center the content using flexbox, remove the scrollbar with the overflow: hidden;
and make it 100% wide and high.
Regarding the images, we'll set absolute positioning and place them on top of each other and make them full width.
body section {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
section img {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
pointer-events: none;
}
For better visual outline I'll add a bluish overlay to the page to hide the color differences between the images. Make sure it has a high z-index value as it has to be top of everything else, and has 100% width and height.
section:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #051b61;
z-index: 5;
mix-blend-mode: color;
}
Next we'll make sure that the visual hierarchy is in place. We can do that by aligning the z-index values. The higher value means that the image has to be on the top of lower z-indexed images.
#road {
z-index: 3;
}
#mountain {
z-index: 1;
top: 150px;
}
#sky {
z-index: 0;
}
#moon {
z-index: 1;
}
Next we will add some basic styling to the texts and their containers. As it is not closely connected to the main goal of the project I won't explain it in details, but here is the code:
#midnight {
top: 120px;
position: relative;
color: #ffffff;
font-size: 12rem;
z-index: 2;
text-shadow: 0 1px 0 #CCCCCC, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 10px 10px rgba(0,0,0,.2), 0 20px 20px rgba(0,0,0,.15);
}
.welcome {
color: #fff;
text-align: center;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.welcome h1 {
font-size: 10rem;
margin-top: 10%;
padding-left: 70px;
padding-right: 70px;
}
.welcome p {
margin-top: 50px;
font-weight: normal;
line-height: 1.6;
padding-left: 70px;
padding-right: 70px;
}
Adding JavaScript
The first thing we have to do in javascript is to save references into variables for all the elements that you want to add parallax effect to. For me it will be the road
, mountain
, sky
, moon
, midnight
. Just make sure that you used these ids in the HTML markup.
let road = document.getElementById('road');
let mountain = document.getElementById('mountain');
let sky = document.getElementById('sky');
let moon = document.getElementById('moon');
let midnight = document.getElementById('midnight');
Now we have reference for all the needed DOM elements. It's time to add an event listener to the scroll event
of the page. We can do that with the addEventListener
method of the window
object. Be sure that you provide 'scroll'
as the first parameter.
Also we will save the scrollY
value of the window object in the callback function. This will tell us how many pixels the site is currently scrolled down from its original position. More information about window.scrollY.
Note:
We are using object destructuring to retrieve the data from the window object. If you are unfamiliar with modern javascript this destructuring assignment is the same as we would write let scrollY = window.scrollY
.
More info about destructuring.
window.addEventListener('scroll', () => {
let { scrollY } = window;
});
Now to make the parallax effect work we will play around with the top
and left
values of the selected elements. If you modify the top (or bottom) value it will affect the vertical scrolling of the element, if you modify the left (or right) value it'll affect the horizontal scrolling of the element.
We have two options: we can play with the initial offset and adjust the scrolling speed of the element.
Let's take a look at what we did with the road
element. As you can see the we multiply the scrollY value with 0.5. This will make the road scroll 2 times slower than it would normally scroll.
We can add a fix offset to alter the starting position of an element. We just have to get the sum of the offset and the multiplied value of the scrollY. Just we did with the mountain
or the midnight
. Feel free to play around with these numbers and see what works best for you.
window.addEventListener('scroll', () => {
let { scrollY } = window;
road.style.top = 0.5 * scrollY + 'px';
mountain.style.top = (150 +0.05 * scrollY) + 'px';
moon.style.left = 1.1 * scrollY + 'px';
sky.style.top = -1.2 * scrollY + 'px';
midnight.style.top = (120 + -1.5 * scrollY) + 'px';
});
And now the parallax scrolling effect is working flawlessly. Thanks for reading!
Where can you learn more from me?
I create education content covering web-development on several platforms, feel free to 👀 check them out.
I also create a newsletter where I share the week's or 2 week's educational content that I created. No bull💩 just educational content.
🔗 Links:
- 🍺 Support free education and buy me a beer
- 💬 Join our community on Discord
- 📧 Newsletter Subscribe here
- 🎥 YouTube Javascript Academy
- 🐦 Twitter: @dev_adamnagy
- 📷 Instagram @javascriptacademy