Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ScrollTo animation using pure javascript and no jquery
document.getElementsByTagName('button')[0].onclick = function () {
scrollTo(document.body, 0, 1250);
}
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
@magm84

This comment has been minimized.

Copy link

magm84 commented Apr 5, 2017

thank is very useful!

@DanielGBullido

This comment has been minimized.

Copy link

DanielGBullido commented Aug 21, 2017

Thanks!

@LordZombi

This comment has been minimized.

Copy link

LordZombi commented Sep 6, 2017

not working on firefox and IE ... it's because of document.body, you have to use document.documentElement for firefox and IE

@piotr-placzek

This comment has been minimized.

Copy link

piotr-placzek commented Nov 24, 2017

I struggled with this problem some time ago.
I did not want to load the jQuery library for this reason.
Your easeInOutQuad function solved my problem.
My class you can find here.
Thanks for you.

@tarun-dugar

This comment has been minimized.

Copy link

tarun-dugar commented Dec 28, 2017

Made a small lib for smooth scroll animations. Check it out: https://github.com/tarun-dugar/easy-scroll

@i3web

This comment has been minimized.

Copy link

i3web commented Jan 10, 2018

awsome!it works

@notrealdev

This comment has been minimized.

Copy link

notrealdev commented Feb 8, 2018

document.documentElemen it working!!
document.body not works for me

@cnanders

This comment has been minimized.

Copy link

cnanders commented Feb 20, 2018

Thank you! Agree, this is very useful.

@exactlyaaron

This comment has been minimized.

Copy link

exactlyaaron commented Mar 16, 2018

Awesome and simple solution! Super useful 👍

@naeemrind

This comment has been minimized.

Copy link

naeemrind commented Mar 17, 2018

Thanks :)

@JullietV

This comment has been minimized.

Copy link

JullietV commented Mar 20, 2018

Thank you very much!

@staradayev

This comment has been minimized.

Copy link

staradayev commented Mar 22, 2018

Thx you this approach is awesome!

But small update:

document.body.scrollTop is deprecated and not works now.

please change to document.documentElement.scrollTop

@kimwes

This comment has been minimized.

Copy link

kimwes commented Apr 7, 2018

const
scrollTo = function(to, duration) {
    const
    element = document.scrollingElement || document.documentElement,
    start = element.scrollTop,
    change = to - start,
    startDate = +new Date(),
    // t = current time
    // b = start value
    // c = change in value
    // d = duration
    easeInOutQuad = function(t, b, c, d) {
        t /= d/2;
        if (t < 1) return c/2*t*t + b;
        t--;
        return -c/2 * (t*(t-2) - 1) + b;
    },
    animateScroll = function() {
        const currentDate = +new Date();
        const currentTime = currentDate - startDate;
        element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration));
        if(currentTime < duration) {
            requestAnimationFrame(animateScroll);
        }
        else {
            element.scrollTop = to;
        }
    };
    animateScroll();
};

Here's the code a bit modernized. Now it's a lot smoother and works in Safari as well. If you don't work with babel then replace const with var.

@wellitongervickas

This comment has been minimized.

Copy link

wellitongervickas commented Apr 12, 2018

I have changed you code for actual patterns
Thanks, works for me!

function scrollTo(element, to = 0, duration= 1000) {

    const start = element.scrollTop;
    const change = to - start;
    const increment = 20;
    let currentTime = 0;

    const animateScroll = (() => {

      currentTime += increment;

      const val = Math.easeInOutQuad(currentTime, start, change, duration);

      element.scrollTop = val;

      if (currentTime < duration) {
        setTimeout(animateScroll, increment);
      }
    });

    animateScroll();
  };

  Math.easeInOutQuad = function (t, b, c, d) {

    t /= d/2;
    if (t < 1) return c/2*t*t + b;
    t--;
    return -c/2 * (t*(t-2) - 1) + b;
  };

Ex: scrollTo(document.documentElement);
@albannurkollari

This comment has been minimized.

Copy link

albannurkollari commented May 14, 2018

Thank you very, very much. Much helpful. One tip though, I'd suggest updating to ES6 standards, you know like replcaing var with let or const and arrow functions etc.

@manufufu

This comment has been minimized.

Copy link

manufufu commented May 16, 2018

Can anyone provide an example on how to use this code? :(

@JHoelzel

This comment has been minimized.

Copy link

JHoelzel commented May 21, 2018

Thanks guys!

Bonus points:
Scroll an element into view but center it on screeen too:

const nextItem = document.getElementById(nextName);
if (nextItem !== null) {
    const middle = (nextItem.getBoundingClientRect().top + window.pageYOffset) - (window.innerHeight / 2);
    scrollTo(document.body, middle, 1250);
}

@JHoelzel

This comment has been minimized.

Copy link

JHoelzel commented May 21, 2018

Bonus Bonus Points:
a) rename the function so it doesnt actually override "scrollto" for the window
b) add a flag that prevents "upwards" scrolling, yes that may only be my usecase but YMMV

   //Scrollto copied from github.com/andjosh https://gist.github.com/andjosh/6764939
      function cstmScrollTo(element, to = 0, duration = 1000, onlyDown) {

          const start = element.scrollTop;
          if(onlyDown && (start > to)){
             return;
          }
          const change = to - start;
          const increment = 20;
          let currentTime = 0;

          const animateScroll = (() => {

              currentTime += increment;

              const val = Math.easeInOutQuad(currentTime, start, change, duration);

              element.scrollTop = val;

              if (currentTime < duration) {
                  setTimeout(animateScroll, increment);
              }
          });

          animateScroll();
      };

      Math.easeInOutQuad = function (t, b, c, d) {

          t /= d / 2;
          if (t < 1) return c / 2 * t * t + b;
          t--;
          return -c / 2 * (t * (t - 2) - 1) + b;
      };
..........
usage:
const nextItem = document.getElementById(nextName);
if (nextItem !== null) {
  const middle = (nextItem.getBoundingClientRect().top + window.pageYOffset) - (window.innerHeight / 2);
  cstmScrollTo(document.body, middle, 1250);
}



@kingname

This comment has been minimized.

Copy link

kingname commented Jun 6, 2018

maybe scrollBy is also useful.

@kriodis

This comment has been minimized.

Copy link

kriodis commented Jul 3, 2018

for typescript in angular 6
//animation for scroll
//duration set in 1000 ms
public scrollTo() {
if (window.scrollY !== 0) {
console.log("entro al rolling");
const
start = window.scrollY,
change = 0 - start,
startDate = +new Date(),
easeInOutQuad = function (t, b, c, d) {
t /= d / 2;
if (t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
},
animateScroll = function () {
const currentDate = +new Date();
const currentTime = currentDate - startDate;
const val = parseInt(easeInOutQuad(currentTime, start, change, 1000));
window.scroll(0, val);
if (currentTime < 1000) {
requestAnimationFrame(animateScroll);
}
else {
window.scroll(0, 0);
}
};
animateScroll();
}
}

@AdaltonLeite

This comment has been minimized.

Copy link

AdaltonLeite commented Jul 12, 2018

Thank you very much! 😄

@Cdvalencia

This comment has been minimized.

Copy link

Cdvalencia commented Aug 22, 2018

thanks, this example used it, with react.js, so:

constructor(props){
   super(props);
   this.scrollTo = this.scrollTo.bind(this);
 }; 

 scrollDown(num) {    
   console.log(num)
   this.scrollTo(document.body, num, 1250);
 }

 scrollTo(element, to, duration) {
     var start = document.scrollingElement.scrollTop,
         change = to - start,
         currentTime = 0,
         increment = 20;

     var animateScroll = function(){
         var easeInOutQuad = function (t, b, c, d) {
           t /= d/2;
         	if (t < 1) return c/2*t*t + b;
         	t--;
         	return -c/2 * (t*(t-2) - 1) + b;
         };
         currentTime += increment;
         var val = easeInOutQuad(currentTime, start, change, duration);
         document.scrollingElement.scrollTop = val;
         if(currentTime < duration) {
             setTimeout(animateScroll, increment);
         }
     };
     animateScroll();
 }

@hugotox

This comment has been minimized.

Copy link

hugotox commented Sep 5, 2018

Just curious, why mutate the global Math object?

@tomaswolfgang

This comment has been minimized.

Copy link

tomaswolfgang commented Sep 6, 2018

If you don't want to deal with any sort of intense implementation and you don't care about the easing function, duration, animation speed or any particulars:
scrollTo(ypos){ window.scrollTo({ top: ypos, behavior: "smooth" }) }
This should do the trick

@vinczebalazs

This comment has been minimized.

Copy link

vinczebalazs commented Sep 11, 2018

Thanks so much, great solution! :)

@c-emil

This comment has been minimized.

Copy link

c-emil commented Sep 18, 2018

@tomaswolfgang window.scrollTo is very nice solution as long as you need to scroll window. It's unfortunately not usable for any element on the page.

@DenysMorozov

This comment has been minimized.

Copy link

DenysMorozov commented Oct 3, 2018

@tomaswolfgang window.scrollTo is very nice solution as long as you need to scroll window. It's unfortunately not usable for any element on the page.

const scrolEl = document.querySelector('.modal-window-container');
const x = elX - (scrolEl.clientWidth / 2);
const y = elY - (scrolEl.clientHeight / 2);
scrolEl.scrollTo(x, y);

Everything works fine for us.

@whossein

This comment has been minimized.

Copy link

whossein commented Nov 22, 2018

const
scrollTo = function(to, duration) {
    const
    element = document.scrollingElement || document.documentElement,
    start = element.scrollTop,
    change = to - start,
    startDate = +new Date(),
    // t = current time
    // b = start value
    // c = change in value
    // d = duration
    easeInOutQuad = function(t, b, c, d) {
        t /= d/2;
        if (t < 1) return c/2*t*t + b;
        t--;
        return -c/2 * (t*(t-2) - 1) + b;
    },
    animateScroll = function() {
        const currentDate = +new Date();
        const currentTime = currentDate - startDate;
        element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration));
        if(currentTime < duration) {
            requestAnimationFrame(animateScroll);
        }
        else {
            element.scrollTop = to;
        }
    };
    animateScroll();
};

Here's the code a bit modernized. Now it's a lot smoother and works in Safari as well. If you don't work with babel then replace const with var.

thanks very much!

@ClausClaus

This comment has been minimized.

Copy link

ClausClaus commented Nov 22, 2018

Thanks!!

@felipenmoura

This comment has been minimized.

Copy link

felipenmoura commented Dec 9, 2018

Very nice.

I forked it and made a few changes:
https://gist.github.com/felipenmoura/650e7e1292c1e7638bcf6c9f9aeb9dd5

  • It returns a promise that resolves when the animation is done
  • It accepts an element as coordinate and scrolls to it (also works with a selector like #some-section-id)
  • It will not overwrite any existing public structure (like the scrollTo native function or Math's prototype)
  • Has a default duration
  • It also uses the document.scrollingElement and you don't need to send document, document.body, window or document.documentElement according to your page structure

I hope it helps and thanks for both inspiring it making it public ;)

@sundayhd

This comment has been minimized.

Copy link

sundayhd commented Apr 26, 2019


function scrollTo(element, to = 0, duration= 1000, scrollToDone = null) {
    const start = element.scrollTop;
    const change = to - start;
    const increment = 20;
    let currentTime = 0;

    const animateScroll = (() => {

      currentTime += increment;

      const val = Math.easeInOutQuad(currentTime, start, change, duration);

      element.scrollTop = val;

      if (currentTime < duration) {
        setTimeout(animateScroll, increment);
	} else {
		if (scrollToDone) scrollToDone();
	}
    });

    animateScroll();
  };

  Math.easeInOutQuad = function (t, b, c, d) {

    t /= d/2;
    if (t < 1) return c/2*t*t + b;
    t--;
    return -c/2 * (t*(t-2) - 1) + b;
  };

Thanks guys!! I added "scrollToDone" to execute callback function when scrolling done.

usage example:
scrollTo(document.body, 500, 1000, () => { console.log("Done with scrolling !!!!") }

@ruucm

This comment has been minimized.

Copy link

ruucm commented May 6, 2019

Thanks a lot!

@marsd

This comment has been minimized.

Copy link

marsd commented May 9, 2019

Has anyone added more easing functions?

@orrybaram

This comment has been minimized.

Copy link

orrybaram commented Jun 18, 2019

Here's a typescript version if anyone is interested:

type EaseInOutQuadOptions = {
  currentTime: number;
  start: number;
  change: number;
  duration: number;
};

const easeInOutQuad = ({
  currentTime,
  start,
  change,
  duration,
}: EaseInOutQuadOptions) => {
  let newCurrentTime = currentTime;
  newCurrentTime /= duration / 2;

  if (newCurrentTime < 1) {
    return (change / 2) * newCurrentTime * newCurrentTime + start;
  }

  newCurrentTime -= 1;
  return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
};

type SmoothScrollOptions = {
  duration: number;
  element: HTMLElement;
  to: number;
};
export default function smoothScroll({
  duration,
  element,
  to,
}: SmoothScrollOptions) {
  const start = element.scrollTop;
  const change = to - start;
  const startDate = new Date().getTime();

  const animateScroll = () => {
    const currentDate = new Date().getTime();
    const currentTime = currentDate - startDate;
    element.scrollTop = easeInOutQuad({
      currentTime,
      start,
      change,
      duration,
    });

    if (currentTime < duration) {
      requestAnimationFrame(animateScroll);
    } else {
      element.scrollTop = to;
    }
  };
  animateScroll();
}
@rlawnsgh78

This comment has been minimized.

Copy link

rlawnsgh78 commented Aug 21, 2019

	function scrollTo(element, durationTop, durationLeft) {
	    var startTop = element.scrollTop,
	    	startLeft = element.scrollLeft,
	        changeTop = durationTop - startTop,
	        changeLeft = durationLeft - startLeft,//window.scrollX - startLeft,
	        currentTimeTop = 0,
	        currentTimeLeft = 0,
	        increment = 20;
	       
	    var animateScrollTop = function(){        
	        currentTimeTop += increment;
	        var val = Math.easeInOutQuad(currentTimeTop, startTop, changeTop, durationTop);
	        element.scrollTop = val;
	        if(currentTimeTop < durationTop) {
	            setTimeout(animateScrollTop, increment);
	        }
		};

		var animateScrollLeft = function(){        
	        currentTimeLeft += increment;
	        var val = Math.easeInOutQuad(currentTimeLeft, startLeft, changeLeft, durationLeft);
	        element.scrollLeft = val;
	        if(currentTimeLeft < durationLeft) {
	            setTimeout(animateScrollLeft, increment);
	        }
		};

		animateScrollTop();
		animateScrollLeft();
	}

//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2tt + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};

@zhiyilee

This comment has been minimized.

Copy link

zhiyilee commented Nov 20, 2019

@andjosh Thank you!! Very useful!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.