Is it possible to use smooth scroll to anchor links but without jQuery
? I am creating a new site and I don't want to use jQuery
.

- 2,898
- 3
- 40
- 58

- 1,239
- 2
- 13
- 17
19 Answers
Extending this answer: https://stackoverflow.com/a/8918062/3851798
After defining your function of scrollTo, you can pass the element you want to scrollTo in the function.
function scrollTo(element, to, duration) {
if (duration <= 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
If you have a div with an id="footer"
<div id="footer" class="categories">…</div>
In the script that you run to scroll you can run this,
elmnt = document.getElementById("footer");
scrollTo(document.body, elmnt.offsetTop, 600);
And there you have it. Smooth scrolling without jQuery. You can actually play around with that code on your browser's console and fine tune it to your liking.

- 1
- 1

- 1,421
- 11
- 20
-
12I had to pass `document.documentElement` into the function instead of `document.body` to make it work – oelna Nov 08 '16 at 17:07
-
document.body for edge, document.documentElement for chrome. – funky-nd Nov 18 '20 at 09:57
-
edge browser supports `document.documentElement` since version 12. MDN ref: https://developer.mozilla.org/en-US/docs/Web/API/Document/documentElement#browser_compatibility. caniuse ref: https://caniuse.com/?search=documentElement – Muhammed Nov 26 '22 at 12:00
Actually, there is more lightweight and simple way to do that: https://codepen.io/ugg0t/pen/mqBBBY
function scrollTo(element) {
window.scroll({
behavior: 'smooth',
left: 0,
top: element.offsetTop
});
}
document.getElementById("button").addEventListener('click', () => {
scrollTo(document.getElementById("8"));
});
div {
width: 100%;
height: 200px;
background-color: black;
}
div:nth-child(odd) {
background-color: white;
}
button {
position: absolute;
left: 10px;
top: 10px;
}
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
<div id="4"></div>
<div id="5"></div>
<div id="6"></div>
<div id="7"></div>
<div id="8"></div>
<div id="9"></div>
<div id="10"></div>
<button id="button">Button</button>

- 1,739
- 16
- 17
-
7Only issue is that instead of ``element.offsetTop`` you should use ``element.getBoundingClientRect().top + window.scrollY`` – Braggae Nov 28 '17 at 15:17
-
@zdolny It now works on native Edge, but for IE you will need a polyfill - https://github.com/iamdustan/smoothscroll – ekfuhrmann Dec 06 '18 at 18:26
-
@ekfuhrmann [I don't care about IE](https://element5digital.com/why-wont-support-internet-explorer-and-neither-should-you/) but does it work in Safari? Or should I not care about Safari too? – ADTC Feb 24 '21 at 04:34
-
This is the same as simply adding `scroll-behavior: smooth`, which works well in most current browsers, but Safari has not adopted. – Orun Jul 15 '21 at 16:03
-
Using the function from here: JavaScript animation and modifying it to modify a property (not only a style's property), you can try something like this:
DEMO: http://jsfiddle.net/7TAa2/1/
Just saying...
function animate(elem, style, unit, from, to, time, prop) {
if (!elem) {
return;
}
var start = new Date().getTime(),
timer = setInterval(function() {
var step = Math.min(1, (new Date().getTime() - start) / time);
if (prop) {
elem[style] = (from + step * (to - from)) + unit;
} else {
elem.style[style] = (from + step * (to - from)) + unit;
}
if (step === 1) {
clearInterval(timer);
}
}, 25);
if (prop) {
elem[style] = from + unit;
} else {
elem.style[style] = from + unit;
}
}
window.onload = function() {
var target = document.getElementById("div5");
animate(document.scrollingElement || document.documentElement, "scrollTop", "", 0, target.offsetTop, 2000, true);
};
div {
height: 50px;
}
<div id="div1">asdf1</div>
<div id="div2">asdf2</div>
<div id="div3">asdf3</div>
<div id="div4">asdf4</div>
<div id="div5">asdf5</div>
<div id="div6">asdf6</div>
<div id="div7">asdf7</div>
<div id="div8">asdf8</div>
<div id="div9">asdf9</div>
<div id="div10">asdf10</div>
<div id="div10">asdf11</div>
<div id="div10">asdf12</div>
<div id="div10">asdf13</div>
<div id="div10">asdf14</div>
<div id="div10">asdf15</div>
<div id="div10">asdf16</div>
<div id="div10">asdf17</div>
<div id="div10">asdf18</div>
<div id="div10">asdf19</div>
<div id="div10">asdf20</div>
-
2To people trying that out : that's great script but don't expect any easing or nice tween. This is kind of a raw steppy animation. – Sbu Sep 06 '13 at 08:20
-
1Similar attempts: http://stackoverflow.com/questions/10063380/javascript-smooth-scroll-without-the-use-of-jquery & http://stackoverflow.com/questions/8917921/cross-browser-javascript-not-jquery-scroll-to-top-animation – Louis Maddox Apr 30 '14 at 18:31
-
@mtness It works fine in Firefox (the code, maybe not the demo). You need to update what element you're animating, because Firefox apparently doesn't like changing the `scrollTop` of `document.body` (took a quick debug and Google search to figure this out). Try this: https://jsfiddle.net/zpu16nen/ (I'll update this post) – Ian Dec 01 '16 at 16:15
This is a pretty old question, but it's important to say that nowadays smooth scrolling is supported in CSS, so there's no need for any scripts:
html {
scroll-behavior: smooth;
}
As noted by @Andiih, as of late 2022 there is full browser support for this.

- 1,494
- 2
- 14
- 26
-
1As of mid 2022 support for scroll-behaviour is now pretty solid ( https://caniuse.com/css-scroll-behavior ) – Andiih Jun 23 '22 at 15:01
-
-
Use this:
let element = document.getElementById("box");
element.scrollIntoView();
element.scrollIntoView(false);
element.scrollIntoView({block: "end"});
element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"});

- 165
- 1
- 4
-
3
-
1Explanation: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#Browser_compatibility – Pedro Ferreira Jan 31 '18 at 15:56
-
1Very interesting solution (+1) but not being supported in IE stinks at the moment! – Zach Saucier Feb 21 '18 at 12:24
-
More or less the same as `scroll-behavior: smooth`, which isn't supported in Safari (or IE, but more importantly modern Safari). – Orun Jul 15 '21 at 16:05
Vanilla js variant using requestAnimationFrame
with easings and all browsers supported:
const requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
function scrollTo(to) {
const start = window.scrollY || window.pageYOffset
const time = Date.now()
const duration = Math.abs(start - to) / 3;
(function step() {
var dx = Math.min(1, (Date.now() - time) / duration)
var pos = start + (to - start) * easeOutQuart(dx)
window.scrollTo(0, pos)
if (dx < 1) {
requestAnimationFrame(step)
}
})()
}
Any easing supported!

- 101
- 3
- 5
-
-
I find this the best answer. Short, performant, customisable and works wonders. `requestAnimationFrame()` suits better for scrolling than `setTimeout()`. – AxeEffect Feb 22 '23 at 16:00
CSS3 transitions with a :target
selector can give a nice result without any JS hacking. I was just contemplating whether to imlement this but without Jquery it does get a bit messy. See this question for details.

- 1
- 1

- 5,226
- 5
- 36
- 66
-
2Why not include the most important bit? `html { scroll-behavior: smooth; }` - so simple and works a treat, if you're okay to eschew IE support – Matt Fletcher Mar 13 '19 at 10:15
-
Smooth Scroll behavior with polyfill...
Example:
document.querySelectorAll('a[href^="#"]').addEventListener("click", function(event) {
event.preventDefault();
document.querySelector(this.getAttribute("href")).scrollIntoView({ behavior: "smooth" });
});
Repository: https://github.com/iamdustan/smoothscroll

- 847
- 9
- 10
-
1You can't listen click event with querySelectorAll, it returns an array. – ronaldtgi Apr 26 '20 at 06:25
Try this code here:
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});

- 1,543
- 2
- 14
- 22

- 41
- 1
March 2022
I know this is an old question but wanted to put forward an answer that has simpler ways of doing it in modern days. As of today, almost all the major browsers are compatible with scroll-behavior
including Safari with its latest release. Still, you might want to employ fallback methods or just use the javascript approach described in method 2 for compatibility in older browsers.
Method 1: HTML and CSS
You can just do this with
<a href="#target" id="scroll-trigger">Click</a>
.
.
.
<h2 id="target">Target</h2>
and CSS
html {
scroll-behavior: smooth
}
Method 2: JavaScript
Or if you have a unique case that needs javascript, go on elaborate with this method.
const scrollTrigger = document.getElementById('scroll-trigger');
const target = document.getElementById('target');
scrollTrigger.addEventListener('click', function (e) {
window.scroll({
top: target.offsetTop,
left:0,
behavior: 'smooth' });
}, false)

- 162
- 10
-
You can use `Element.scrollIntoView` with just `behavior: "smooth"` instead of `window.scroll` (which requires `top` and `left` as well). – Darryl Noakes Sep 12 '22 at 16:03
-
This is great! Note that the CSS solution won't work in IE. https://caniuse.com/css-scroll-behavior – Steve Meisner Nov 09 '22 at 00:12
My favorite scroll-to library currently is Zenscroll because of the wide range of features and small size (currently only 3.17kb).
In the future it may make more sense to use the native scrollIntoView
functionality, but since it'd have to be polyfilled in most production sites today due to the lack of IE support, I recommend using Zenscroll instead in all cases.

- 24,871
- 12
- 85
- 147
-
amem! This is only library which is currently working as I expected. Thanks! – Luis Febro Aug 10 '20 at 01:38
-
@zach-sauchier Would you still recommend to use this library today or did you move on to a native solution? – Vincent Feb 03 '22 at 10:50
-
There's still no native solution. There are a lot of alternatives these days, like GSAP's ScrollToPlugin – Zach Saucier Feb 03 '22 at 14:44
It's upgraded version from @Ian
// Animated scroll with pure JS
// duration constant in ms
const animationDuration = 600;
// scrollable layout
const layout = document.querySelector('main');
const fps = 12; // in ms per scroll step, less value - smoother animation
function scrollAnimate(elem, style, unit, from, to, time, prop) {
if (!elem) {
return;
}
var start = new Date().getTime(),
timer = setInterval(function () {
var step = Math.min(1, (new Date().getTime() - start) / time);
var value = (from + step * (to - from)) + unit;
if (prop) {
elem[style] = value;
} else {
elem.style[style] = value;
}
if (step === 1) {
clearInterval(timer);
}
}, fps);
if (prop) {
elem[style] = from + unit;
} else {
elem.style[style] = from + unit;
}
}
function scrollTo(hash) {
const target = document.getElementById(hash);
const from = window.location.hash.substring(1) || 'start';
const offsetFrom = document.getElementById(from).offsetTop;
const offsetTo = target.offsetTop;
scrollAnimate(layout,
"scrollTop", "", offsetFrom, offsetTo, animationDuration, true);
setTimeout(function () {
window.location.hash = hash;
}, animationDuration+25)
};
// add scroll when click on menu items
var menu_items = document.querySelectorAll('a.mdl-navigation__link');
menu_items.forEach(function (elem) {
elem.addEventListener("click",
function (e) {
e.preventDefault();
scrollTo(elem.getAttribute('href').substring(1));
});
});
// scroll when open link with anchor
window.onload = function () {
if (window.location.hash) {
var target = document.getElementById(window.location.hash.substring(1));
scrollAnimate(layout, "scrollTop", "", 0, target.offsetTop, animationDuration, true);
}
}

- 11
- 2
For anyone in 2019, first, you add an event listener
document.getElementById('id').addEventListener('click', () => scrollTo())
then you target the element and go smoothly to it
function scrollTo() {
let target = document.getElementById('target');
target.scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
})
}

- 2,177
- 4
- 18
- 35

- 51
- 2
-
5NOTE: This example has very limited support as is. Arrow functions won't work in any version of IE, Opera Mini, Blackberry Browser, IE Mobile and QQ Browser without using a transpiler such as Babel caniuse.com/#feat=arrow-functions and the behaviour 'smooth' also has pretty significant lack of support as of 2019 https://caniuse.com/#search=scrollIntoView – Martin James Sep 07 '19 at 13:47
Here is a simple solution in pure JavaScript. It takes advantage of CSS property scroll-behavior: smooth
function scroll_to(id) {
document.documentElement.style.scrollBehavior = 'smooth'
element = document.createElement('a');
element.setAttribute('href', id)
element.click();
}
Usage:
Say we have 10 divs:
<div id='df7ds89' class='my_div'>ONE</div>
<div id='sdofo8f' class='my_div'>TWO</div>
<div id='34kj434' class='my_div'>THREE</div>
<div id='gbgfh98' class='my_div'>FOUR</div>
<div id='df89sdd' class='my_div'>FIVE</div>
<div id='34l3j3r' class='my_div'>SIX</div>
<div id='56j5453' class='my_div'>SEVEN</div>
<div id='75j6h4r' class='my_div'>EIGHT</div>
<div id='657kh54' class='my_div'>NINE</div>
<div id='43kjhjh' class='my_div'>TEN</div>
We can scroll to the ID of choice:
scroll_to('#657kh54')
You simply call this function on your click event (e.g. click button then scroll to div #9).
Result:
Of course it looks much smoother in real life.
Unfortunately, IE and Safari don't support scrollBehavior = 'smooth' as of 2019

- 12,628
- 16
- 93
- 132
For a more comprehensive list of methods for smooth scrolling, see my answer here.
To scroll to a certain position in an exact amount of time, window.requestAnimationFrame
can be put to use, calculating the appropriate current position each time. setTimeout
can be used to a similar effect when requestAnimationFrame
is not supported.
/*
@param pos: the y-position to scroll to (in pixels)
@param time: the exact amount of time the scrolling will take (in milliseconds)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
Demo:
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
document.getElementById("toElement").addEventListener('click', function(e) {
var elem = document.querySelector("div");
scrollToSmoothly(elem.offsetTop);
});
document.getElementById("toTop").addEventListener('click', function(e){
scrollToSmoothly(0, 700);
});
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="toTop">Scroll back to top</button>
</div>
For more complex cases, the SmoothScroll.js library can be used, which handles smooth scrolling both vertically and horizontally, scrolling inside other container elements, different easing behaviors, scrolling relatively from the current position, and more.
document.getElementById("toElement").addEventListener('click', function(e) {
smoothScroll({toElement: document.querySelector('div'), duration: 500});
});
document.getElementById("toTop").addEventListener('click', function(e){
smoothScroll({yPos: 0, duration: 700});
});
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll@1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<button id="toElement">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element
<button id="toTop">Scroll back to top</button>
</div>
Alternatively, you can pass an options object to window.scroll
which scrolls to a specific x and y position and window.scrollBy
which scrolls a certain amount from the current position:
// Scroll to specific values
// scrollTo is the same
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// Scroll certain amounts from current position
window.scrollBy({
top: 100, // could be negative value
left: 0,
behavior: 'smooth'
});
Demo:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
var elem = document.querySelector("div");
window.scroll({
top: elem.offsetTop,
left: 0,
behavior: 'smooth'
});
}
</script>
Modern browsers support the scroll-behavior
CSS property, which can be used to make scrolling in the document smooth (without the need for JavaScript). Anchor tags can be used for this by giving the anchor tag a href
of #
plus the id
of the element to scroll to). You can also set the scroll-behavior
property for a specific container like a div
to make its contents scroll smoothly.
html, body{
scroll-behavior: smooth;
}
<a href="#elem">Scroll To Element</a>
<div id="elem" style="margin: 500px 0px;">Div</div>

- 76,500
- 11
- 62
- 80
Without jQuery
const links = document.querySelectorAll('header nav ul a')
for (const link of links) {
link.onclick = function clickHandler(e) {
e.preventDefault()
const href = this.getAttribute('href')
document.querySelector(href).scrollIntoView({ behavior: 'smooth' })
}
}
body {
background-color: black;
height:7000px
}
header {
margin-top: 1.3rem;
margin-bottom: 25rem;
display: flex;
justify-content: center;
align-items: center;
}
nav ul {
display: flex;
}
nav ul li {
all: unset;
margin: 2rem;
cursor: pointer;
}
nav ul li a {
all: unset;
font: bold 1.8rem robto;
color: white;
letter-spacing: 1px;
cursor: pointer;
padding-top: 3rem;
padding-bottom: 2rem;
}
#team,
#contact,
#about {
background-color: #e2df0d;
width: 100%;
height: 35rem;
display: flex;
justify-content: center;
align-items: center;
color: black;
font: bold 4rem roboto;
letter-spacing: 6.2px;
margin-top: 70rem;
}
<header>
<!-- NavBar -->
<nav>
<ul>
<li><a href="#team">Team</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>
</nav>
</header>
<!-- ----------- Team ----------------------- -->
<div id="team">
<h2>Team</h2>
</div>
<!-- ----------- Contact ----------------------- -->
<div id="contact">
<h2>Contact</h2>
</div>
<!-- ----------- About ----------------------- -->
<div id="about">
<h2>About</h2>
</div>
Or with just CSS, but it's not supported in all browsers yet
html {scroll-behavior: smooth}

- 1,419
- 13
- 15
If you want to set all of your deep links #
to scroll smoothly you can do this:
const allLinks = document.querySelectorAll('a[href^="#"]')
allLinks.forEach(link => {
const
targetSelector = link.getAttribute('href'),
target = document.querySelector(targetSelector)
if (target) {
link.addEventListener('click', function(e) {
e.preventDefault()
const top = target.offsetTop // consider decreasing your main nav's height from this number
window.scroll({
behavior: 'smooth',
left: 0,
top: top
});
})
}
})
An example code to consider also your main nav's height (this code goes where top
const is declared):
const
mainHeader = document.querySelector('header#masthead'), //change to your correct main nav selector
mainHeaderHeight = mainHeader.offsetHeight,
// now calculate top like this:
top = target.offsetTop - mainHeaderHeight

- 1,730
- 16
- 20
Here is the most elegant and concise solution.
Links:
<a href="#elementIDtoScrollTo"></a>
CSS:
html {
scroll-behavior: smooth;
}
Remember to add a unique id="elementIDtoScrollTo"
to each HTML element.

- 1,332
- 16
- 15