116

Reading the AngularJS docs I haven't figured out if $anchorScroll can have a duration/easing option to smooth scroll to elements.

It only says:

$location.hash('bottom');

// call $anchorScroll()
$anchorScroll();

I do not use jquery and don't want to; is there still a clever yet simple way to make or extend $anchorScroll in order to make scrolling more smooth?

YakovL
  • 7,557
  • 12
  • 62
  • 102
itsme
  • 48,972
  • 96
  • 224
  • 345

8 Answers8

157

Unfortunately this is not possible using $anchorScroll. As you discovered $anchorScroll doesn't have any options and doesn't work with $ngAnimate. In order to animate the scroll you would need to use your own service/factory or just straight javascript.

For the sake of self-learning I put together an example with a smooth scrolling service. There are probably better ways to do this so any feedback is encouraged.

To scroll to an element you attach a ng-click="gotoElement(ID)" to any element. I think an even better route would be to make this a directive.

Here's the working example on jsFiddle.

Update

There are now a number of third-party directives for accomplishing this.

Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184
20

You can also use the angular-scroll, link "https://github.com/durated/angular-scroll/". It is smooth scrolling also few easing functions to get a professional look.

Sagar Parikh
  • 306
  • 3
  • 7
  • 1
    Does this plugin work on other elements apart form $documents? I tried to apply scrollToElement to a div so I can scroll a row inside it into the view, and it did not work.. – Shaunak Jan 13 '15 at 22:31
10

The answer from Brett worked great for me. I did some small changes on his solution in terms of modularization and testability.

Here's is yet another working example on JsFiddle that includes the other version with testing included.

For testing, I'm using Karma and Jasmine. Signature has been slightly modified as follows:

 anchorSmoothScroll.scrollTo(elementId, speed);

Where element is a mandatory attribute to scroll to and speed is optional where the default is 20 (as it was before).

Alan Souza
  • 7,475
  • 10
  • 46
  • 68
2

You can also use ngSmoothScroll, link: https://github.com/d-oliveros/ngSmoothScroll.

Just include the smoothScroll module as a dependency and use it like this:

<a href="#" scroll-to="my-element-3">Click me!</a>

santiaago
  • 608
  • 9
  • 19
2

None of the solutions here actually does what OP originally asked, that is, make $anchorScroll scrolling smoothly. Difference between smooth scrolling directives and $anchroScroll is that it uses/modifies $location.hash(), which may be desirable in some cases.

Here is gist for simple module that replaces $anchorScroll scrolling with smooth scrolling. It uses https://github.com/oblador/angular-scroll library for the scrolling itself (replace it with something else if you want, it should be easy).

https://gist.github.com/mdvorak/fc8b531d3e082f3fdaa9
Note: It actually does not get $anchorScroll to scroll smoothly, but it replaces its handler for scrolling.

Enable it simply by referencing mdvorakSmoothScroll module in your application.

Mikee
  • 21
  • 1
0

Alan, thank you. If anyone interested, I formatted it based on John Pappa standards.

(function() {

'use strict';
var moduleId = 'common';
var serviceId = 'anchorSmoothScroll';

angular
    .module(moduleId)
    .service(serviceId, anchorSmoothScroll);

anchorSmoothScroll.$inject = ['$document', '$window'];

function anchorSmoothScroll($document, $window) {

    var document = $document[0];
    var window = $window;

    var service = {
        scrollDown: scrollDown,
        scrollUp: scrollUp,
        scrollTo: scrollTo,
        scrollToTop: scrollToTop
    };
    return service;

    function getCurrentPagePosition(currentWindow, doc) {
        // Firefox, Chrome, Opera, Safari
        if (currentWindow.pageYOffset) return currentWindow.pageYOffset;
        // Internet Explorer 6 - standards mode
        if (doc.documentElement && doc.documentElement.scrollTop)
            return doc.documentElement.scrollTop;
        // Internet Explorer 6, 7 and 8
        if (doc.body.scrollTop) return doc.body.scrollTop;
        return 0;
    }

    function getElementY(doc, element) {
        var y = element.offsetTop;
        var node = element;
        while (node.offsetParent && node.offsetParent !== doc.body) {
            node = node.offsetParent;
            y += node.offsetTop;
        }
        return y;
    }

    function scrollDown(startY, stopY, speed, distance) {

        var timer = 0;

        var step = Math.round(distance / 25);
        var leapY = startY + step;

        for (var i = startY; i < stopY; i += step) {
            setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
            leapY += step;
            if (leapY > stopY) leapY = stopY;
            timer++;
        }
    };

    function scrollUp(startY, stopY, speed, distance) {

        var timer = 0;

        var step = Math.round(distance / 25);
        var leapY = startY - step;

        for (var i = startY; i > stopY; i -= step) {
            setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
            leapY -= step;
            if (leapY < stopY) leapY = stopY;
            timer++;
        }
    };

    function scrollToTop(stopY) {
        scrollTo(0, stopY);
    };

    function scrollTo(elementId, speed) {

        var element = document.getElementById(elementId);

        if (element) {
            var startY = getCurrentPagePosition(window, document);
            var stopY = getElementY(document, element);

            var distance = stopY > startY ? stopY - startY : startY - stopY;

            if (distance < 100) {
                this.scrollToTop(stopY);

            } else {

                var defaultSpeed = Math.round(distance / 100);
                speed = speed || (defaultSpeed > 20 ? 20 : defaultSpeed);

                if (stopY > startY) {
                    this.scrollDown(startY, stopY, speed, distance);
                } else {
                    this.scrollUp(startY, stopY, speed, distance);
                }
            }

        }

    };

};

})();
Rentering.com
  • 399
  • 3
  • 9
0

I am not aware of how to animate $anchorScroll . Here's how I do it in my projects:

/* Scroll to top on each ui-router state change */
$rootScope.$on('$stateChangeStart', function() {
 scrollToTop();
});

And the JS function:

function scrollToTop() {
    if (typeof jQuery == 'undefined') {
        return window.scrollTo(0,0);
    } else {
        var body = $('html, body');
        body.animate({scrollTop:0}, '600', 'swing');
    }
    log("scrollToTop");
    return true;
}
Deepak Thomas
  • 3,355
  • 4
  • 37
  • 39
0

We can use JQuery and Javascript with Directive to perform the scrolling to a specific div on anchor tag click.

Please check the working example on the below link -

https://stackoverflow.com/a/67513880/6656918

Ravindra Vairagi
  • 1,055
  • 15
  • 22