AngularJS - $anchorScroll smooth/duration
Asked Answered
S

8

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?

Stalemate answered 13/2, 2014 at 9:22 Comment(0)
N
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.

Nichellenichol answered 20/2, 2014 at 20:12 Comment(8)
Very nice. Here it is as a directive: gist.github.com/justinmc/d72f38339e0c654437a2Berlyn
@JustinMcCandless how do you call your directive? I've tried: <a ng-click="anchor-smooth-school('about');">About 1</a> <a ng-click="anchorSmoothScroll('about');">About 2</a>Bloodred
@Bloodred just do <a anchor-smooth-scroll>About 1</a> <a anchor-smooth-scroll>About 2</a>Berlyn
Nice, i like this answer. But this still adds another reason to hate AngularJS, I mean, look at the size of this compared to JQuery scrollToRetardment
Is there a reason jQuery shouldn't be used in this situation, or at any point while using AngularJS?Leroy
You can also check out github.com/oblador/angular-scroll. Extremely simple and only 9kb. Also supports promises for scroll end callbacks and lots moreDismiss
To use the directive, create an element with an ID (e.g. <div id="my-div">my div</div>) and then create a link like this: <a anchor-smooth-scroll="my-div">visit my div</a>.Pesky
After checking out angular-scroll, it seems like a better solution.Pesky
K
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.

Kelci answered 3/6, 2014 at 12:14 Comment(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..Halpin
V
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).

Vitamin answered 15/5, 2014 at 19:37 Comment(0)
A
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>

Aerate answered 10/3, 2015 at 0:47 Comment(0)
R
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.

Remsen answered 10/3, 2015 at 9:10 Comment(0)
N
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);
                }
            }

        }

    };

};

})();
Nolie answered 6/5, 2016 at 15:51 Comment(0)
C
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;
}
Callosity answered 24/10, 2017 at 11:33 Comment(0)
B
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://mcmap.net/q/189481/-scrollto-function-in-angularjs

Bumgardner answered 13/5, 2021 at 4:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.