97

I'm trying to make an animated "scroll to top" effect without using jQuery.

In jQuery, I usually use this code:

$('#go-to-top').click(function(){ 
      $('html,body').animate({ scrollTop: 0 }, 400);
      return false; 
});

How do I animate scrollTop without using jQuery?

Jasper
  • 4,312
  • 10
  • 31
  • 38

1 Answers1

182

HTML:

<button onclick="scrollToTop(1000);"></button>

1# JavaScript (linear):

function scrollToTop (duration) {
    // cancel if already on top
    if (document.scrollingElement.scrollTop === 0) return;

    const totalScrollDistance = document.scrollingElement.scrollTop;
    let scrollY = totalScrollDistance, oldTimestamp = null;

    function step (newTimestamp) {
        if (oldTimestamp !== null) {
            // if duration is 0 scrollY will be -Infinity
            scrollY -= totalScrollDistance * (newTimestamp - oldTimestamp) / duration;
            if (scrollY <= 0) return document.scrollingElement.scrollTop = 0;
            document.scrollingElement.scrollTop = scrollY;
        }
        oldTimestamp = newTimestamp;
        window.requestAnimationFrame(step);
    }
    window.requestAnimationFrame(step);
}

2# JavaScript (ease in and out):

function scrollToTop (duration) {
    // cancel if already on top
    if (document.scrollingElement.scrollTop === 0) return;

    const cosParameter = document.scrollingElement.scrollTop / 2;
    let scrollCount = 0, oldTimestamp = null;

    function step (newTimestamp) {
        if (oldTimestamp !== null) {
            // if duration is 0 scrollCount will be Infinity
            scrollCount += Math.PI * (newTimestamp - oldTimestamp) / duration;
            if (scrollCount >= Math.PI) return document.scrollingElement.scrollTop = 0;
            document.scrollingElement.scrollTop = cosParameter + cosParameter * Math.cos(scrollCount);
        }
        oldTimestamp = newTimestamp;
        window.requestAnimationFrame(step);
    }
    window.requestAnimationFrame(step);
}
/* 
  Explanation:
  - pi is the length/end point of the cosinus intervall (see below)
  - newTimestamp indicates the current time when callbacks queued by requestAnimationFrame begin to fire.
    (for more information see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
  - newTimestamp - oldTimestamp equals the delta time

    a * cos (bx + c) + d                        | c translates along the x axis = 0
  = a * cos (bx) + d                            | d translates along the y axis = 1 -> only positive y values
  = a * cos (bx) + 1                            | a stretches along the y axis = cosParameter = window.scrollY / 2
  = cosParameter + cosParameter * (cos bx)  | b stretches along the x axis = scrollCount = Math.PI / (scrollDuration / (newTimestamp - oldTimestamp))
  = cosParameter + cosParameter * (cos scrollCount * x)
*/

Note:

  • Duration in milliseconds (1000ms = 1s)
  • Second script uses the cos function. Example curve:

enter image description here

3# Simple scrolling library on Github

Robbendebiene
  • 2,835
  • 2
  • 18
  • 27
  • 2
    Keep in mind that `const` is not part of EcmaScript 5, but 6 with different semantics. Currently only Firefox and Chrome support it somehow. Read more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const – morkro Jul 07 '14 at 15:33
  • Yes true, good object. So using var might be the better option untill all browsers are supporting const. – Robbendebiene Jul 08 '14 at 16:11
  • 5
    Can I see a JSFIDDLE of this in action? I can't seem to get it to work . . . – Drazzah Dec 03 '14 at 18:00
  • thanks, works great! – Damjan Pavlica Oct 27 '15 at 14:49
  • 9
    Thanks for this! I extended it a bit to allow scrolling to a specific element and a polyfill for browsers without performance.now() ... https://gist.github.com/joshcanhelp/a3a669df80898d4097a1e2c01dea52c1 – joshcanhelp Oct 21 '16 at 21:38
  • This is awesome @Robbendebiene I created a simple scroll-element module based on the linear function above. https://github.com/StevenIseki/scroll-element – svnm Nov 23 '16 at 12:36
  • Big thanks! Works like a charm in my vuejs app – codely May 06 '17 at 15:40
  • I created a [small library](https://alvarotrigo.com/blog/how-create-jquery-scrollTop-animation-without-jquery/) for that. Provides a few more things like callbacks and the ability to stop the animation as well as using different easing effects. And of course, keeping it small! – Alvaro Mar 01 '18 at 13:25
  • This is not working in IE11. I don't like IE, but still there are 2% o 3% of people using it. How can I fix it? – Gerard May 02 '20 at 22:37
  • Ok, I've found a fix for this: use `pageYOffset` instead of `scrollY`, and it will work even in IE. – Gerard May 02 '20 at 22:59
  • This is brilliant, but has one bug. Sometimes the cosine's product does not equal exactly 0, resulting in multiple iterations of the scroll's round trip. Solution, along with a mod to support all elements is here: [link](https://stackoverflow.com/questions/29246640/why-is-this-scroll-to-top-function-glitchy) – Y.K. May 05 '20 at 23:02