Fall Sale! Code FALL2024 takes 25% OFF our Pro Plugins & Books »
Web Dev + WordPress + Security

Vanilla JavaScript Smooth Scroll

While working on the site’s 24th redesign, my goal was to simplify the UI as much as possible. As much as I enjoy lots of cool effects, I wanted the new design to be as minimalist as possible. So that meant dropping a LOT of little bells and whistles that were included in the previous design. One feature that didn’t make the cut was smooth scrolling to internal anchor targets. Like when you click a link that takes you #somewhere on the same page, but instead of just jumping directly it sort of scrolls you there nice and easy.

Demo

The Code

To implement smooth scrolling for internal anchor links, add the following slice of vanilla JavaScript (no jQuery required!):

// Vanilla JavaScript Scroll to Anchor
// @ https://perishablepress.com/vanilla-javascript-scroll-anchor/

(function() {
	scrollTo();
})();

function scrollTo() {
	const links = document.querySelectorAll('.scroll');
	links.forEach(each => (each.onclick = scrollAnchors));
}

function scrollAnchors(e, respond = null) {
	const distanceToTop = el => Math.floor(el.getBoundingClientRect().top);
	e.preventDefault();
	var targetID = (respond) ? respond.getAttribute('href') : this.getAttribute('href');
	const targetAnchor = document.querySelector(targetID);
	if (!targetAnchor) return;
	const originalTop = distanceToTop(targetAnchor);
	window.scrollBy({ top: originalTop, left: 0, behavior: 'smooth' });
	const checkIfDone = setInterval(function() {
		const atBottom = window.innerHeight + window.pageYOffset >= document.body.offsetHeight - 2;
		if (distanceToTop(targetAnchor) === 0 || atBottom) {
			targetAnchor.tabIndex = '-1';
			targetAnchor.focus();
			window.history.pushState('', '', targetID);
			clearInterval(checkIfDone);
		}
	}, 100);
}

After including that code snippet, any anchor link with a class of scroll will scroll to the target, as shown in the demo. No modifications are required, just drop it in. If you want to change the value of the class from “scroll” to “slide” or whatever, replace scroll in the first line of the scrollTo() function.

How it works

Without going into painful detail about how this code works, the heavy lifting happens in the scrollAnchors() function. That’s where the calculations are made, logic applied, etc. And that function is called by scrollTo(), which simply applies the scroll function to each anchor link that has the scroll class. And then to kick it all off, the scrollTo() function is called via Immediately Invoked Function Expression (IIFE).

Once the smooth-scroll script is included on the web page, any internal link that includes the scroll class will smoothly scroll to the target anchor element (via #fragment-identifier, aka hash tag).

No class required

What if you don’t want to add any scroll classes, and would rather automatically scroll any/all anchor links on the page? Easy. Just replace the scrollTo() function with this:

function scrollTo() {
	var links = document.getElementsByTagName('a');
	for (var i = 0; i < links.length; i++) {
		var link = links[i];
		if ((link.href && link.href.indexOf('#') != -1) && ((link.pathname == location.pathname) || ('/' + link.pathname == location.pathname)) && (link.search == location.search)) {
			link.onclick = scrollAnchors;
		}
	}
}

No modifications are required. How does it work? First grab all links as an array. Then loop through the array and check whether each link includes a hash character #, and also whether the link target is the same as the current location. If both of these conditions prove true, clicking the link will result on a smooth scroll to the target. All happens automatically, giving your pages complete smooth scrolling without having to add any classes.

Smooth scroll for Ajax content

The previous technique works great in all cases except where the HTML is loaded via Ajax. For any content loaded live via Ajax, the link elements are not recognized by the scrollTo() function, so smooth scrolling fails in that case. So, if you want to enable smooth scrolling in Ajax-loaded content, add the following JavaScript:

document.addEventListener('click', function(e) {
	event = e || window.e;
    var target = e.target || e.srcElement;
    var respond = document.getElementById('respond-link');
	if (e.target.classList.contains('scroll-live')) scrollAnchors(e, respond);
});

After adding this code, any links that include the class scroll-live will receive the smooth-scrolling treatment. And if you don’t want to use two different classes, scroll for static content, and scroll-live for Ajax content, just change scroll-live in the Ajax code snippet. Change it to scroll or whatever works best for you.

More smooth scroll techniques

CSS solution

Just a heads up for latest Chrome, Firefox, or Opera, you can enable smooth scroll via the scroll-behavior property (no JS required!), for example:

.module {
  scroll-behavior: [ auto | smooth ];
}

More infos at CSS-Tricks and caniuse. Thanks to DKeu for the tip!

About the Author
Jeff Starr = Designer. Developer. Producer. Writer. Editor. Etc.
.htaccess made easy: Improve site performance and security.

4 responses to “Vanilla JavaScript Smooth Scroll”

  1. Maximillian Heth 2019/03/06 12:49 am

    Hey Jeff,

    Nice script! I think a few changes might be in order though:

    If there are a lot of links on given page, looping through all of them could be computationally expensive, so I reworked your script slightly to use event delegation across the board:

    https://codepen.io/maxxheth/pen/oVYRdm

    For anyone who’s not aware, I think it’s also worth noting that using ES6 features (const, let, fat arrows, etc.) means that you have to transpile them using a transformer such as Babel (and preferably via a bundler or task runner such as Gulp, Webpack, RollUp, or Parcel for example) if you need to support older web browsers (and even certain mobile browsers).

    If that sounds like too much effort for anyone who just wants to take the code and plug it in somewhere, I refactored the code in Jeff’s example above to ES5 JavaScript in my Code Pen example, so it should have fairly widespread support, though your mileage may vary (if you need ironclad browser support, there’s always jQuery! =)). In any case, I hope you find it useful! Good luck!

    • Pure awesome, thank you Maximillian! :)

      • Maximillian Heth 2019/03/06 7:44 pm

        My pleasure! Your books are a godsend (bought them all about two weeks ago), so thank you in return! I’m kicking myself for not getting the .htaccess one years ago…I can recall at least one time when a client’s site got hacked where those blacklisting scripts would’ve saved my bacon. That was a painful lesson! Lol!

        Have a good one and keep up the great work! =)

  2. Jim S Smith 2019/03/28 6:00 am

    Always looking for more handy JavaScripts.

    I have always appreciated what CSS3 can do for animations too. Just imagine putting this baby together with some sweet, smooth-transitions from the CSS3-side!

    I wasn’t sure if you visited DynamicDrive website very often, but I remembered downloading and adapting a really sweet bit of CSS for an animated “toggle menu”. It made a great replacement for the old JavaScripted dynamic menu I was using, and looked a lot cleaner.

    Anyway,

    Consider my exploration of this bit of script on my “To Do” bucket list. :-)

    – Jim

Comments are closed for this post. Something to add? Let me know.
Welcome
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
The Tao of WordPress: Master the art of WordPress.
Thoughts
I disabled AI in Google search results. It was making me lazy.
Went out walking today and soaked up some sunshine. It felt good.
I have an original box/packaging for 2010 iMac if anyone wants it free let me know.
Always ask AI to cite its sources. Also: “The Web” is not a valid answer.
All free plugins updated and ready for WP 6.6 dropping next week. Pro plugin updates in the works also complete :)
99% of video thumbnail/previews are pure cringe. Goofy faces = Clickbait.
RIP ICQ
Newsletter
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.