I’m Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with , and . To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

web developer guide

my books

Book cover of Pro CSS3 AnimationPro CSS3 Animation, Apress, 2013

my other blogs

Massive Head Canon: Intelligent discussion of movies, books, games, and technology.

my projects

A Sass color keyword system for designers. Replaces CSS defaults with improved hues and more memorable, relevant color names.

CSSslidy: an auto-generated #RWD image slider. 3.8K of JS, no JQuery. Drop in images, add a line of CSS. Done.

tipster.ioAutomatically provides local tipping customs and percentages for services anywhere.

Click me to journey through the article.

Smooth Page Scroll in 5 Lines of JavaScript

javascript / scroll effects

Estimated reading time: 3 minutes

HTML has always had the ability to jump to locations on a page, provided the target element has an id attribute, via a technique known as anchor links.

However, this movement is instantaneous. For the sake of appearance, a site's design sometimes calls for a smooth or slowed scroll to a point on a page.

Historically, this was achieved with JQuery, but it's overkill to load a framework just for one technique. Modern JavaScript provides a more efficient, native alternative, in the form of the window.scrollTo method.

A standard anchor link is used as the basis of the technique: that way, if the JavaScript doesn’t work for any reason, the page will still go to the targeted location.

<a href="#destination">Click me: I’m <em>smoooooth</em>.</a>
<p id="destination">This is the target, further down the page.

Then, the body needs to have a scroll-behavior of smooth applied in CSS:

body {
	scroll-behavior: smooth;

Finally, the , added to the bottom so it doesn’t block execution on the rest of the page. I’ve expanded the code slightly from the promised five lines for clarity:

var anchorLink = document.querySelector("a[href^='#']");
var target = document.getElementById("destination");
anchorLink.addEventListener("click", function(el) {
if (window.scrollTo) {
window.scrollTo(0, target.offsetTop);

The querySelector uses a CSS attribute selector to find the first link that targets a URL beginning with #; clicking on this link initiates a test that the browser supports the scrollTo method. If it does, el.preventDefault stops the browser from immediately jumping to the anchor, and uses the scrollTo method to get there instead, smoothed by the earlier CSS declaration.

In comparison to a framework, this syntax is far simpler; the one downside is that it doesn’t allow the designer to change the timing function or scroll movement, to avoid abuses.

Abstracting the Script

The script as shown works well, but the approach demands that you know the name of the target in advance, and it can only be applied to one link. What if you want to have a series of links to different targets on the same page, with smooth scrolling between all of them? In that case, we need a little abstraction.

Because we’re referencing a nodeList, I’m going to start with a small helper function that will allow me to address the links in a forEach loop:

var forEach = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) {, i, array[i]);

Next, I’ll gather all the anchor links together, find what they address, and apply scrollTo when they are clicked on:

var anchorLinks = document.querySelectorAll("a[href^='#']");
if (window.scrollTo) {
forEach(anchorLinks, function(index, element) {
var target = document.getElementById(element.getAttribute("href").substring(1));
element.addEventListener("click", function(el) {
window.scrollTo(0, target.offsetTop);

Note that this still requires scroll-behavior to be placed on the body tag.

Browser support

The one issue with this approach at this point in time is browser compatibility: window.scrollTo works in the latest versions of Chrome (with Enable experimental Web Platform features turned on inside chrome://flags) and Firefox, but not (yet) in Safari, Opera, or Internet Explorer. But because we’ve built the script on the principles of progressive enhancement, those browsers will still jump to the correct location on click using this script: they just won’t exhibit the smooth scroll behaviour.


This technique is often combined with elements that are are “sticky”, i.e. elements that scroll with the page until they reach the top of the viewport, where they “stick” in place. This is also commonly achieved with a framework, but modern CSS can accomplish it easily all by itself. I’ll cover that in the next article. See another example of this script on CodePen

comments powered by Disqus

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.