AngularJS - $anchorScroll smooth/duration

JavascriptAngularjsScrollAnchor Scroll

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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.

Solution 3 - Javascript

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).

Solution 4 - Javascript

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>

Solution 5 - Javascript

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<br> 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.

Solution 6 - Javascript

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);
                }
            }

        }

    };

};

})();

Solution 7 - Javascript

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;
}

Solution 8 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionitsmeView Question on Stackoverflow
Solution 1 - JavascriptBrett DeWoodyView Answer on Stackoverflow
Solution 2 - JavascriptSagar ParikhView Answer on Stackoverflow
Solution 3 - JavascriptAlan SouzaView Answer on Stackoverflow
Solution 4 - JavascriptsantiaagoView Answer on Stackoverflow
Solution 5 - JavascriptMikeeView Answer on Stackoverflow
Solution 6 - JavascriptRentering.comView Answer on Stackoverflow
Solution 7 - JavascriptDeepak ThomasView Answer on Stackoverflow
Solution 8 - JavascriptRavindra VairagiView Answer on Stackoverflow