I'm not sure what is the correct title for this question, so please correct me if I am wrong.
Let's say that on page refresh (load) I need to perform an animated scrolling to anchor according to the hash of current location (I know about ngAnchorScroll, but I need exectly an animated scolling, which ngAnchorScorll can't to do).
To do that I implemented a service, which will be called on controller initialization:
var Ctrl = function ($scope, initAnchorScrolling) {
initAnchorScrolling($scope);
};
Ctrl.$inject = ['$scope', "initAnchorScrolling"];
applicationServices.factory("initAnchorScrolling",
['scroll', '$location', '$timeout', function(scrollUtils, $location, $timeout) {
return function(scope) {
/** Some stuff here */
function scroll() {
var hash = $location.hash(), elm;
if (!hash) {
scrollUtils.scrollTo(0, 0);
} else if ((elm = getAnchorElement(hash))) {
scrollUtils.scrollToElement(elm);
} else {
scrollUtils.scrollTo(0, 0);
}
}
scope.$watch(function scrollWatch() { return $location.hash(); },
function scrollWatchAction() {
$timeout(function () {
scroll();
}, 0, true);
});
};
}]);
You may see that this implementation of anchors is very similar to implementation of ngAnchorScroll. The main difference is that I'm using jQuery.animate() to move between anchors.
As I understood from here to defer function execution till the moment, when browser finished render, we just need to call $timeout service with a delay parameter, which equals to 0. But this is not working for me. Sporadically page scrolls to the different positions. I can explain it this way - scroll function invoked before digest cycle finished synchronization of views and models, so at the moment of invokation HTML blocks have invalid height and position.
So my question: is it possible to defer function invokation till the moment when browser fully finished render and a digest cycle finished synchronization of views and models (applied bindings)?
The original question is about scrolling to a particular hash while this solution is about retaining the scroll position no matter what. Still it might give you a solution
i created a directive that works on the window scroll ( it could updated to work on any element though )
html usage
<div ng-keep-scroll="service.scrollY">
<!-- list of scrolling things here -->
</div>
where "service.scrollY" MUST be a variable within a service. Services retain their state and values, controllers are recreated every time they load and clear their values so you cant use them to store persistent data. the controller has a scope variable pointing to the service.
directive js
app.directive('ngKeepScroll', function ($timeout) {
return function (scope, element, attrs) {
//load scroll position after everything has rendered
$timeout(function () {
var scrollY = parseInt(scope.$eval(attrs.ngKeepScroll));
$(window).scrollTop(scrollY ? scrollY : 0);
}, 0);
//save scroll position on change
scope.$on("$routeChangeStart", function () {
scope.$eval(attrs.ngKeepScroll + " = " + $(window).scrollTop());
});
}
});