Skip to content

Instantly share code, notes, and snippets.

@justinmc
Last active May 14, 2019 19:52
Show Gist options
  • Star 53 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save justinmc/d72f38339e0c654437a2 to your computer and use it in GitHub Desktop.
Save justinmc/d72f38339e0c654437a2 to your computer and use it in GitHub Desktop.
Anchor Smooth Scroll - Angular Directive
/**~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 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;
}
}
};
});
@madodela
Copy link

madodela commented Feb 4, 2015

Thanks for this code! I just had to make some minor modifications to your code and it works great.
Good job!

@edcala
Copy link

edcala commented Mar 12, 2015

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?

@aaaristo
Copy link

would be nice to encapsulate functions in arrays for minification:

['$location',function ($location) {...}]

@dancingfrog
Copy link

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/

@jakob-e
Copy link

jakob-e commented Jun 30, 2016

@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 :-)

@adityamittal011
Copy link

Awesome! Thanks a lot 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment