Build a parallax scrolling website interface with jQuery and CSS
Parallax scrolling website interfaces have been popping up all over the place recently. I didn’t want to miss out on the fun, so I have put together a parallax scrolling demo built using jQuery and CSS.
Parallax what?
Even if you’re not familiar with the term “parallax scrolling” you will certainly be familiar with the technique. Parallax scrolling is a 2d animation process that creates an illusion of depth by animating foreground layers faster than background layers. When you observe the landscape from a moving car, objects closer to the car appear to pass you faster than scenery further away. Parallax scrolling uses the same principle to trick the viewer into thinking they are observing a 3d scene.
Demo and Download
My demo web page shows one approach to building a vertical parallax scrolling interface:
You can scroll in the usual fashion, use the navigation menu at the right-hand side of the page, or the next/prev buttons that appear underneath each article. As you scroll, the page’s four content layers are animated independently of one another to create an illusion of depth.
The scrolling looks smoothest in Safari (at least that’s the case on my PC), but my demo should work in any modern browser.
Disclaimer 1: Because this is just an experiment I’ve not spent any time optimising the demo to work on mobile devices. I wanted to keep the demo lean ‘n’ mean, and not clutter it by sniffing mobile browsers and forking my code. On a production site you’d want to ensure that the site degrades gracefully on mobile devices, where scroll events and fixed positioning might work in unexpected ways.
Disclaimer 2: The navigation menu in my demo is inspired by the menu on the Nike Better World website. If you plan on implementing a similar menu on a production site, please be aware of its origin.
How it works
The articles and background layers are given a fixed positioned with CSS, and assigned a z-index so that the foreground layers appear above the background layers. The four layers are: small clouds, large clouds, balloon/landscape images, articles.
/* foreground (ballons/landscape) */ #parallax-bg3 { z-index: 3; position: fixed; left: 50%; /* align left edge with center of viewport */ top: 0; width: 940px; margin-left: -470px; /* move left by half element's width */ }
Within each layer individual content elements are absolutely positioned. This was the most fiddly part of the process, since the elements need to positioned in such a way that they align in a pleasing manner when the user scrolls to any of the four articles. In this case it was really just a process of trial and error.
#bg3-1 { position: absolute; top: -111px; left: 355px; } #bg3-2 { position: absolute; top: 812px; left: 321px; } /* etc... */
A few lines of jQuery control the parallax effect, triggered by a scroll event. I was surprised how easy this was to achieve, it is literally just a handful of lines of code.
$(window).bind('scroll',function(e){ parallaxScroll(); }); function parallaxScroll(){ var scrolled = $(window).scrollTop(); $('#parallax-bg1').css('top',(0-(scrolled*.25))+'px'); $('#parallax-bg2').css('top',(0-(scrolled*.5))+'px'); $('#parallax-bg3').css('top',(0-(scrolled*.75))+'px'); }
As you can see the CSS top property is used to move each layer as the user scrolls. The foreground layer is always aligned to the top of the document, while the movement of other layers is adjusted according to their depth. The lower a layer sits in the stack, the less distance it is moved.
The rest of the jQuery is concerned with controlling the navigation menus. When the user clicks a navigation button the page scrolls to the top of the associated article. In the event that the user has JavaScript disabled, regular HTML anchor links still allow the page to be navigated, but without any fancy pants animations.
Next steps
I’m sure there are plenty of other approaches to parallax scrolling, and hopefully my experiment provides a starting point for your own explorations of the technique.
Update
I have updated the demo so that each parallax layer is given a fixed, rather than absolute, position. This approach gives a smoother scrolling effect.
Tweet

This really is a fantastic article, and a pretty strong demo—came in handy when trying to explain to clients! Thanks for this.
Awesome and smooth Parallax!
Do you have any plans to make this work horizontally? Would be great.
@Nader I hadn’t considered it, but perhaps I shall…
Hi!
Just for fun version without jQuery – http://h123.ru/ES5-DOM-SHIM/parallax-scrolling-demo/ using just DOM4 API and ES6.
IE7+ and other good browsers.
Really great demo. I’m relatively new to web design and I’m playing with it now. Do you happen to know how to make an object stop and stand still at certain points…and then carry on scrolling again at other points?
@termi – nice!
Just for fun… horizontally. It could be better. I know… I hardcoded some numbers over there…
http://prottotipo.com/app/parallax-scrolling/
@Sebastian You da man!
Thanks! Great Demo, but there seems to be something broken with refreshing the Page in Firefox (mine is 11.0). Images Disappear at certain Point and come back when i start to scroll. Also sometimes at refresh it moves to the beginning, and then -refreshing again- it moves back to the point it came from.
I´m no pro, so i couldn´t figure out why. Do you?
Thx anyway
@Christopher I can’t reproduce the bug you describe (FF 11 Mac). When I refresh the page it displays exactly as it was prior to the refresh, and I haven’t noticed any images disappearing. Will test on a PC later.
That’s so awesome article.
Very very nice !!
Cool guide and demo, will try to work it out, thanks a lot!
Brilliant!
Thanks a lot!
Wow! Nice tut…Cheers!
How do I add additional items to the page and Nav?
Hey Jonathan, great plugin I really appreciate you sharing this. There is one problem I’m facing though; the first ‘redrawDotNav();’ in the parallax.js keeps returning an error and stops all scripts working.
I’ve recently updated my jquery to version 1.7.3 to make use of some other scripts but this single operation seems to fail where the others work fine. I get the parallax effect and the menu scroll works, I just can’t get the menu class to change to ‘active’ because of this.
Any ideas? Thanks.
@Prizme I tested with the latest version of jQuery (1.7.2) and my demo work fine – no console errors or anything, so I’m afraid I can’t suggest why you might be getting errors.
Hi. This tutorial is great. I love it. I’ve gone through this tutorial and understand everything bar one part.
I’ve been able to add an extra 3 sections so there are now 7 sections, each with content. I’ve managed to add the extra dots for the navigation and I can get each section to scroll smoothly. The part I am struggling with is getting the side dot navigation to be set to an active state on the new sections ive created.I get to section 4 and after that the links work and the scrolling effect works but the little dot isn’t set as active. What do I need to change to make this work?
I think its this section but I cant get it to work. Any help would be greatly appreciated:
/* Set navigation dots to an active state as the user scrolls */function redrawDotNav(){
var section1Top = 0;
// The top of each section is offset by half the distance to the previous section.
var section2Top = $('#frameless-parachute').offset().top - (($('#english-channel').offset().top - $('#frameless-parachute').offset().top) / 2);
var section3Top = $('#english-channel').offset().top - (($('#about').offset().top - $('#english-channel').offset().top) / 2);
var section4Top = $('#about').offset().top - (($(document).height() - $('#about').offset().top) / 2);
$('nav#primary a').removeClass('active');
if($(document).scrollTop() >= section1Top && $(document).scrollTop() = section2Top && $(document).scrollTop() = section3Top && $(document).scrollTop() = section4Top){
$('nav#primary a.about').addClass('active');
Thanks,
Richard
@Richard You’re looking at the right portion of code, but the edit you’ve made to it doesn’t make much sense
There are two steps to achieve what you want:
1. Add new variables representing the top offset of sections 5 through 7. For example:
var section5Top = $('#your-section-id').offset().top - (($(document).height() - $('#your-section-id').offset().top) / 2);2. Edit the series of if/else statements that follow your new variable declarations to detect when the user has scrolled to one of the new sections. For example, the following conditional logic detects when the user is viewing section 4:
} else if ($(document).scrollTop() >= section4Top && $(document).scrollTop() < section5Top){$('nav#primary a.my-new-class').addClass('active');
All you're doing here is checking against the top offset values you detected in step 1. Note that the snippet above is just one portion of the required code. Check against the source code of my demo and it should be fairly clear that there is a separate if/else for each of the four sections of the interface - you'll need to add three more.
How do we set the frameless-parachute, manned-flight text etc.. not to move? does anyone know?