ScrollTo function in AngularJS
I'm trying to get a quick nav to work correctly. It's floating on the side. When they click on a link, it takes them to that ID on the page. I'm following this guide from Treehouse. This is what I have for the scrolling:
$("#quickNav a").click(function(){
var quickNavId = $(this).attr("href");
$("html, body").animate({scrollTop: $(location).offset().top}, "slow");
return false;
});
I initially placed it before the </body>
. But I seem to be running into a race condition where that was firing before the quickNav compiled (it has a ng-hide
placed on it, not sure if that's causing it - but it is within the DOM).
If I run that block of code in the console, then the scrolling works as expected.
I figured it'd be more effective to move this into the controller - or more likely within a directive. But I'm not having luck accomplishing that. How can I get this block of code to work with AngularJS?
Here is a simple directive that will scroll to an element on click:
myApp.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm) {
$elm.on('click', function() {
$("body").animate({scrollTop: $elm.offset().top}, "slow");
});
}
}
});
Demo: http://plnkr.co/edit/yz1EHB8ad3C59N6PzdCD?p=preview
For help creating directives, check out the videos at http://egghead.io, starting at #10 "first directive".
edit: To make it scroll to a specific element specified by a href, just check attrs.href
.
myApp.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, $elm, attrs) {
var idToScroll = attrs.href;
$elm.on('click', function() {
var $target;
if (idToScroll) {
$target = $(idToScroll);
} else {
$target = $elm;
}
$("body").animate({scrollTop: $target.offset().top}, "slow");
});
}
}
});
Then you could use it like this: <div scroll-on-click></div>
to scroll to the element clicked. Or <a scroll-on-click href="#element-id"></div>
to scroll to element with the id.
This is a better directive in case you would like to use it:
you can scroll to any element in the page:
.directive('scrollToItem', function() {
return {
restrict: 'A',
scope: {
scrollTo: "@"
},
link: function(scope, $elm,attr) {
$elm.on('click', function() {
$('html,body').animate({scrollTop: $(scope.scrollTo).offset().top }, "slow");
});
}
}})
Usage (for example click on div 'back-to-top' will scroll to id scroll-top):
<a id="top-scroll" name="top"></a>
<div class="back-to-top" scroll-to-item scroll-to="#top-scroll">
It's also supported by chrome,firefox,safari and IE cause of the html,body element .
In order to animate to a specific element inside a scroll container (fixed DIV)
/*
@param Container(DIV) that needs to be scrolled, ID or Div of the anchor element that should be scrolled to
Scrolls to a specific element in the div container
*/
this.scrollTo = function(container, anchor) {
var element = angular.element(anchor);
angular.element(container).animate({scrollTop: element.offset().top}, "slow");
}
An angular solution using $anchorScroll
taken from a now archived blog post by Ben Lesh, which is also reproduced in some detail at this SO answer he contributed (including a rewrite of how to do this within a routing):
app.controller('MainCtrl', function($scope, $location, $anchorScroll) {
var i = 1;
$scope.items = [{ id: 1, name: 'Item 1' }];
$scope.addItem = function (){
i++;
//add the item.
$scope.items.push({ id: i, name: 'Item ' + i});
//now scroll to it.
$location.hash('item' + i);
$anchorScroll();
};
});
And here is the plunker, from the blog that provided this solution: http://plnkr.co/edit/xi2r8wP6ZhQpmJrBj1jM?p=preview
Important to note that the template at that plunker includes this, which sets up the id
that you're using $anchorScroll
to scroll to:
<li ng-repeat="item in items"
id="item{{item.id}}"
>{{item.name}</li>
And if you care for a pure javascript solution, here is one:
Invoke runScroll in your code with parent container id and target scroll id:
function runScroll(parentDivId,targetID) {
var longdiv;
longdiv = document.querySelector("#" + parentDivId);
var div3pos = document.getElementById(targetID).offsetTop;
scrollTo(longdiv, div3pos, 600);
}
function scrollTo(element, to, duration) {
if (duration < 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function () {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop == to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
Reference: Cross browser JavaScript (not jQuery...) scroll to top animation