Anchor Smooth Scroll - Angular Directive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
* Anchor Smooth Scroll - Smooth scroll to the given anchor on click | |
* adapted from this stackoverflow answer: http://stackoverflow.com/a/21918502/257494 | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
angular.module('yourapp').directive('anchorSmoothScroll', function($location) { | |
'use strict'; | |
return { | |
restrict: 'A', | |
replace: false, | |
scope: { | |
'anchorSmoothScroll': '@' | |
}, | |
link: function($scope, $element, $attrs) { | |
initialize(); | |
/* initialize - | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
function initialize() { | |
createEventListeners(); | |
} | |
/* createEventListeners - | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
function createEventListeners() { | |
// listen for a click | |
$element.on('click', function() { | |
// set the hash like a normal anchor scroll | |
$location.hash($scope.anchorSmoothScroll); | |
// smooth scroll to the passed in element | |
scrollTo($scope.anchorSmoothScroll); | |
}); | |
} | |
/* scrollTo - | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
function scrollTo(eID) { | |
// This scrolling function | |
// is from http://www.itnewb.com/tutorial/Creating-the-Smooth-Scroll-Effect-with-JavaScript | |
var i; | |
var startY = currentYPosition(); | |
var stopY = elmYPosition(eID); | |
var distance = stopY > startY ? stopY - startY : startY - stopY; | |
if (distance < 100) { | |
scrollTo(0, stopY); return; | |
} | |
var speed = Math.round(distance / 100); | |
if (speed >= 20) speed = 20; | |
var step = Math.round(distance / 25); | |
var leapY = stopY > startY ? startY + step : startY - step; | |
var timer = 0; | |
if (stopY > startY) { | |
for (i = startY; i < stopY; i += step) { | |
setTimeout('window.scrollTo(0, '+leapY+')', timer * speed); | |
leapY += step; if (leapY > stopY) leapY = stopY; timer++; | |
} return; | |
} | |
for (i = startY; i > stopY; i -= step) { | |
setTimeout('window.scrollTo(0, '+leapY+')', timer * speed); | |
leapY -= step; if (leapY < stopY) leapY = stopY; timer++; | |
} | |
} | |
/* currentYPosition - | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
function currentYPosition() { | |
// Firefox, Chrome, Opera, Safari | |
if (window.pageYOffset) { | |
return window.pageYOffset; | |
} | |
// Internet Explorer 6 - standards mode | |
if (document.documentElement && document.documentElement.scrollTop) { | |
return document.documentElement.scrollTop; | |
} | |
// Internet Explorer 6, 7 and 8 | |
if (document.body.scrollTop) { | |
return document.body.scrollTop; | |
} | |
return 0; | |
} | |
/* scrollTo - | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | |
function elmYPosition(eID) { | |
var elm = document.getElementById(eID); | |
var y = elm.offsetTop; | |
var node = elm; | |
while (node.offsetParent && node.offsetParent != document.body) { | |
node = node.offsetParent; | |
y += node.offsetTop; | |
} return y; | |
} | |
} | |
}; | |
}); |
There is a bug which occurs when you click on an anchor where the scrollY location is < 100px away from the current scrollY position. This can be traced back to line 50:
scrollTo(0, stopY); return;
I think you meant:
window.scrollTo(0, stopY); return;
Am I right?
would be nice to encapsulate functions in arrays for minification:
['$location',function ($location) {...}]
I like the idea, which is how I found this gist, but I think it's more useful as a service:
https://jsfiddle.net/brettdewoody/y65G5/
@dancingfrog
If you change the service to simply returning a function
angular
.module('app.service.anchor',[])
.service('$anchorScrollAnimate', function(){
return function(eID) { ...}
});
All you have to do is change the injection name on your old projects
angular
.module('app',['app.service.anchor'])
.controller('AppController',[
'$anchorScrollAnimate', // <= change it here
function($anchorScroll){ // <= no need to change inside controller/directive
// scroll to top
$anchorScroll('top');
}
]);
Thanks for sharing :-)
Awesome! Thanks a lot
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this code! I just had to make some minor modifications to your code and it works great.
Good job!