Advertisement
Advertisement


Is it possible to animate scrollTop with jQuery?


Question

I want to smoothly scroll down. I do not want to have to write a function for that - especially if jQuery already has one.

2010/10/27
1
226
10/27/2010 3:00:48 PM

Accepted Answer

You can just use .animate() the scrollTop property, like this:

$("html, body").animate({ scrollTop: "300px" });
2010/10/27
464
10/27/2010 6:27:44 PM

Nick's answer works great. Be careful when specifying a complete() function inside the animate() call because it will get executed twice since you have two selectors declared (html and body).

$("html, body").animate(
    { scrollTop: "300px" },
    {
        complete : function(){
            alert('this alert will popup twice');
        }
    }
);

Here's how you can avoid the double callback.

var completeCalled = false;
$("html, body").animate(
    { scrollTop: "300px" },
    {
        complete : function(){
            if(!completeCalled){
                completeCalled = true;
                alert('this alert will popup once');
            }
        }
    }
);
2012/02/20

Nick's answer works great and the default settings are nice, but you can more fully control the scrolling by completing all of the optional settings.

here is what it looks like in the API:

.animate( properties [, duration] [, easing] [, complete] )

so you could do something like this:

.animate( 
    {scrollTop:'300px'},
    300,
    swing,
    function(){ 
       alert(animation complete! - your custom code here!); 
       } 
    )

here is the jQuery .animate function api page: http://api.jquery.com/animate/

2011/10/15

Like Kita mentioned there is a problem with multiple callbacks firing when you animate on both 'html' and 'body'. Instead of animating both and blocking subsequent callbacks I prefer to use some basic feature detection and only animate the scrollTop property of a single object.

The accepted answer on this other thread gives some insight as to which object's scrollTop property we should try to animate: pageYOffset Scrolling and Animation in IE8

// UPDATE: don't use this... see below
// only use 'body' for IE8 and below
var scrollTopElement = (window.pageYOffset != null) ? 'html' : 'body';

// only animate on one element so our callback only fires once!
$(scrollTopElement).animate({ 
        scrollTop: '400px' // vertical position on the page
    },
    500, // the duration of the animation 
    function() {       
        // callback goes here...
    })
});

UPDATE - - -

The above attempt at feature detection fails. Seems like there's not a one-line way of doing it as webkit type browsers pageYOffset property always returns zero when there's a doctype. Instead, I found a way to use a promise to do a single callback for every time the animation executes.

$('html, body')
    .animate({ scrollTop: 100 })
    .promise()
    .then(function(){
        // callback code here
    })
});
2017/05/23

I have what I believe is a better solution than the $('html, body') hack.

It's not a one-liner, but the issue I had with $('html, body') is that if you log $(window).scrollTop() during the animation, you'll see that the value jumps all over the place, sometimes by hundreds of pixels (though I don't see anything like that happening visually). I needed the value to be predictable, so that I could cancel the animation if the user grabbed the scroll bar or twirled the mousewheel during the auto-scroll.

Here is a function will animate scrolling smoothly:

function animateScrollTop(target, duration) {
    duration = duration || 16;
    var scrollTopProxy = { value: $(window).scrollTop() };
    if (scrollTopProxy.value != target) {
        $(scrollTopProxy).animate(
            { value: target }, 
            { duration: duration, step: function (stepValue) {
                var rounded = Math.round(stepValue);
                $(window).scrollTop(rounded);
            }
        });
    }
}

Below is a more complex version that will cancel the animation on user interaction, as well as refiring until the target value is reached, which is useful when trying to set the scrollTop instantaneously (e.g. simply calling $(window).scrollTop(1000) — in my experience, this fails to work about 50% of the time.)

function animateScrollTop(target, duration) {
    duration = duration || 16;

    var $window = $(window);
    var scrollTopProxy = { value: $window.scrollTop() };
    var expectedScrollTop = scrollTopProxy.value;

    if (scrollTopProxy.value != target) {
        $(scrollTopProxy).animate(
            { value: target },
            {
                duration: duration,

                step: function (stepValue) {
                    var roundedValue = Math.round(stepValue);
                    if ($window.scrollTop() !== expectedScrollTop) {
                        // The user has tried to scroll the page
                        $(scrollTopProxy).stop();
                    }
                    $window.scrollTop(roundedValue);
                    expectedScrollTop = roundedValue;
                },

                complete: function () {
                    if ($window.scrollTop() != target) {
                        setTimeout(function () {
                            animateScrollTop(target);
                        }, 16);
                    }
                }
            }
        );
    }
}
2013/12/20

I was having issues where the animation was always starting from the top of the page after a page refresh in the other examples.

I fixed this by not animating the css directly but rather calling window.scrollTo(); on each step:

$({myScrollTop:window.pageYOffset}).animate({myScrollTop:300}, {
  duration: 600,
  easing: 'swing',
  step: function(val) {
    window.scrollTo(0, val);
  }
});

This also gets around the html vs body issue as it's using cross-browser JavaScript.

Have a look at http://james.padolsey.com/javascript/fun-with-jquerys-animate/ for more information on what you can do with jQuery's animate function.

2014/05/27

Source: https://stackoverflow.com/questions/4034659
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]