0

I'm attempting to use the solution here to scroll a container to a certain position. Here's my version:

var wrapper = angular.element(document.getElementById('myWrapper'))[0];
var container = wrapper.querySelector('#myContainer');
var anchor = wrapper.querySelector('#myAnchorWithinTheContainer');
scrollContainerToAnchor(container, anchor);

...

function scrollContainerToAnchor(container, anchor) {
    var element = angular.element(anchor);
    angular.element(container).animate({scrollTop: element.offset().top}, "slow");
}

However, angular.element returns an array, so I don't see how that answer can work... but even if I correct it to the following:

function scrollContainerToAnchor(container, anchor) {
    var element = angular.element(anchor);
    angular.element(container)[0].animate({scrollTop: element[0].offset().top}, "slow");
}

the browser will still complain that "element[0].offset is not a function". So, I attempted to use getBoundingClientRect() instead:

function scrollContainerToAnchor(container, anchor) {
    var element = angular.element(anchor);
    angular.element(container)[0].animate({scrollTop: element[0].getBoundingClientRect().top}, "slow");
}

but then the browser gives me "Failed to execute 'animate' on 'Element': The provided double value is non-finite" (in my case, the "non-finite" value that it's complaining about is 3282.9375(?)).


Anyway, the fact that the above-linked answer has (as of today) 10 upvotes and no complaints in the comments suggests that I am missing something, not that the answer is incorrect... So, what am I missing?


If there's a better way to scroll a div without using jQuery and without scrolling the whole page in addition to the div (I've already looked at $anchorScroll, but it scrolls both the window and the div), I'm up for other suggestions/techniques.

Community
  • 1
  • 1
Troy
  • 21,172
  • 20
  • 74
  • 103
  • have you included `ngAnimate` module and its respective js file? – Pankaj Parkar Jun 23 '16 at 21:02
  • Yes, ngAnimate is included. – Troy Jun 23 '16 at 21:03
  • jQuery isn't included, and I'm hoping to avoid including it. Is there jQuery code above? It looks all plain angular to me... – Troy Jun 23 '16 at 21:08
  • **what am I missing?** - jQuery! :) - But in all seriousness, if you're wanting a way to do this without jQuery, spending 95% of your question talking about an implementation that depends on jQuery is probably not the best way to go about it. – Snixtor Jun 23 '16 at 21:26
  • Does it work if you don't get element [0]? I might be getting confused with jQuery, but it's possible that that would work since AngularJS's version is based off of jQuery -[docs](https://docs.angularjs.org/api/ng/function/angular.element). – meepzh Jun 23 '16 at 21:26

1 Answers1

0

The answer referenced above requires jQuery. The offset function is part of jQuery. Angular makes use of jQuery Lite aka jqLite, which has no offset function.


To scroll the div without jQuery, I did the following:

HTML:

...
<div id="myScrollableDiv">
    ...
    <div id="elementIWantToScrollTo">...</div>
</div>
<div in-view="scrollableDivIsVisible()"></div>
...

Controller:

angular.module('myModule')
.controller('MyCtrl', ['$scope', function($scope) {
    ...

    var _hasBeenScrolled = false;
    $scope.scrollableDivIsVisible() {
        if (!_hasBeenScrolled) {
            document.getElementById('elementIWantToScrollTo').scrollIntoView();
            _hasBeenScrolled = true;
        }
    }
}]);

Note that scrollIntoView() causes both the window and the div to scroll (similar to $anchorScroll, but easier to use and doesn't add a hash tag to the URL). Since one of the requirements is to NOT scroll the window, I avoid that by using the in-view directive to detect when the bottom of my scrollable div is visible. Thus I only trigger scrollIntoView() when scrolling the window won't matter/is already complete. Then I use _hasBeenScrolled to ensure scrollIntoView() doesn't interfere with any scrolling that the user did (i.e. it's only auto scrolled once).


If you need to scroll a div based on some other trigger, rather than when the user first sees it, as was my need, then you can use the in-view directive to set a variable and then $scope.$watch() that variable before running your other trigger.


Maybe there's a better "Angular way" to do it, but the above gets the job done, and doesn't need jQuery.

Community
  • 1
  • 1
Troy
  • 21,172
  • 20
  • 74
  • 103